gemcode 0.3.104__tar.gz → 0.3.106__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.
- {gemcode-0.3.104/src/gemcode.egg-info → gemcode-0.3.106}/PKG-INFO +33 -2
- {gemcode-0.3.104 → gemcode-0.3.106}/README.md +32 -1
- {gemcode-0.3.104 → gemcode-0.3.106}/pyproject.toml +1 -1
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/agent.py +5 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/invoke.py +174 -3
- gemcode-0.3.106/src/gemcode/kaira_client.py +51 -0
- gemcode-0.3.106/src/gemcode/kaira_daemon.py +653 -0
- gemcode-0.3.106/src/gemcode/kaira_ipc.py +289 -0
- gemcode-0.3.106/src/gemcode/kaira_job_store.py +157 -0
- gemcode-0.3.106/src/gemcode/org.py +296 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/repl_commands.py +3 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/repl_slash.py +294 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/__init__.py +8 -1
- gemcode-0.3.106/src/gemcode/tools/org_tools.py +158 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/subtask.py +45 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tui/scrollback.py +294 -0
- {gemcode-0.3.104 → gemcode-0.3.106/src/gemcode.egg-info}/PKG-INFO +33 -2
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode.egg-info/SOURCES.txt +5 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_multimodal_input.py +3 -1
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_tools.py +5 -1
- gemcode-0.3.104/src/gemcode/kaira_daemon.py +0 -221
- {gemcode-0.3.104 → gemcode-0.3.106}/LICENSE +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/MANIFEST.in +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/setup.cfg +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/__init__.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/__main__.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/audit.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/autocompact.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/autotune.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/callbacks.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/capability_routing.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/checkpoints.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/cli.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/compaction.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/computer_use/__init__.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/computer_use/browser_computer.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/config.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/context_budget.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/context_warning.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/credentials.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/curated_memory.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/dynamic_policy.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/evals/harness.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/hitl_session.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/hooks.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/ide_protocol.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/ide_stdio.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/intent_classifier.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/interactions.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/learning.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/limits.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/live_audio_engine.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/logging_config.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/mcp_loader.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/memory/__init__.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/memory/embedding_memory_service.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/memory/file_memory_service.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/modality_tools.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/model_errors.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/model_routing.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/multimodal_input.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/openapi_loader.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/output_styles.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/paths.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/permissions.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/plugins/__init__.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/policy_profile.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/pricing.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/prompt_suggestions.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/query/__init__.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/query/config.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/query/deps.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/query/engine.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/query/stop_hooks.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/query/token_budget.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/query/transitions.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/query_sanitizer.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/refine.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/review_agent.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/rules.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/session_runtime.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/session_store.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/session_summariser.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/skills.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/slash_commands.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/thinking.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tool_prompt_manifest.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tool_registry.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tool_result_store.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/bash.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/browser.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/compress_memory.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/curated_memory.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/edit.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/filesystem.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/notebook.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/notes.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/repo_map.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/search.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/shell.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/shell_gate.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/skills.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/tasks.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/think.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/todo.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/veomem_tools.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/web.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools/web_search.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tools_inspector.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/trust.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tui/input_handler.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tui/spinner.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tui/welcome_banner.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/tui/welcome_rich.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/veomem_bridge.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/version.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/vertex.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/wal.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/web/__init__.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/web/sse_adapter.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/web/terminal_repl.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/web/web_sse_compat.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode/workspace_hints.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode.egg-info/dependency_links.txt +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode.egg-info/entry_points.txt +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode.egg-info/requires.txt +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/src/gemcode.egg-info/top_level.txt +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_add_dir.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_agent_instruction.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_autocompact.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_capability_routing.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_checkpoint_diff_command.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_cli_init.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_compress_memory_tool.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_computer_use_permissions.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_context_budget.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_context_warning.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_credentials.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_eval_harness_layout.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_ide_stdio_attachments.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_interactive_permission_ask.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_kaira_scheduler.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_modality_tools.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_model_error_retry.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_model_errors.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_model_routing.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_output_styles_and_rules.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_paths.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_permissions.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_prompt_suggestions.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_repl_commands.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_repl_slash.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_skills.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_slash_commands.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_slash_completion_registry.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_thinking_config.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_token_budget.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_tool_context_circulation.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_tools_inspector.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/tests/test_web_sse_adapter.py +0 -0
- {gemcode-0.3.104 → gemcode-0.3.106}/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.
|
|
3
|
+
Version: 0.3.106
|
|
4
4
|
Summary: Local-first coding agent on Google Gemini + ADK
|
|
5
5
|
Author: GemCode Contributors
|
|
6
6
|
License: Apache License
|
|
@@ -213,7 +213,7 @@ GemCode is designed for repository-native work rather than copy-paste chat workf
|
|
|
213
213
|
| TUI | Scrollback terminal UI over the REPL runtime |
|
|
214
214
|
| IDE stdio | Editor integration over JSONL stdin/stdout |
|
|
215
215
|
| Kaira | Priority-queue scheduler for background jobs |
|
|
216
|
-
| Live audio |
|
|
216
|
+
| Live audio (future scope) | Planned: microphone-driven Gemini Live sessions (currently experimental/unreliable) |
|
|
217
217
|
|
|
218
218
|
## Recommended reading order
|
|
219
219
|
|
|
@@ -349,6 +349,33 @@ gemcode -C . --attach ./report.pdf "Summarize this"
|
|
|
349
349
|
gemcode kaira -C .
|
|
350
350
|
```
|
|
351
351
|
|
|
352
|
+
### Orchestration (Kaira + org delegation)
|
|
353
|
+
|
|
354
|
+
Docs:
|
|
355
|
+
- `../docs/orchestration.md`
|
|
356
|
+
|
|
357
|
+
In one terminal:
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
gemcode kaira -C .
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
In another terminal:
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
gemcode -C .
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Then in the REPL/TUI:
|
|
370
|
+
|
|
371
|
+
```text
|
|
372
|
+
/org tree
|
|
373
|
+
/org hire verifier "QA / test planner" subagent gemcode "Find risks, propose tests, review plans."
|
|
374
|
+
/org assign verifier "Review the plan and propose tests"
|
|
375
|
+
/kaira jobs
|
|
376
|
+
/kaira follow <job_id_prefix>
|
|
377
|
+
```
|
|
378
|
+
|
|
352
379
|
### Start the IDE bridge
|
|
353
380
|
```bash
|
|
354
381
|
gemcode ide --stdio
|
|
@@ -359,6 +386,10 @@ gemcode ide --stdio
|
|
|
359
386
|
gemcode live-audio -C .
|
|
360
387
|
```
|
|
361
388
|
|
|
389
|
+
Status note:
|
|
390
|
+
- `live-audio` is currently **experimental** and may fail due to upstream Gemini Live availability/reliability (for example transient `1011` internal errors).
|
|
391
|
+
- Treat this as **future scope** for production workflows.
|
|
392
|
+
|
|
362
393
|
## REPL command highlights
|
|
363
394
|
|
|
364
395
|
| Command | Purpose |
|
|
@@ -21,7 +21,7 @@ GemCode is designed for repository-native work rather than copy-paste chat workf
|
|
|
21
21
|
| TUI | Scrollback terminal UI over the REPL runtime |
|
|
22
22
|
| IDE stdio | Editor integration over JSONL stdin/stdout |
|
|
23
23
|
| Kaira | Priority-queue scheduler for background jobs |
|
|
24
|
-
| Live audio |
|
|
24
|
+
| Live audio (future scope) | Planned: microphone-driven Gemini Live sessions (currently experimental/unreliable) |
|
|
25
25
|
|
|
26
26
|
## Recommended reading order
|
|
27
27
|
|
|
@@ -157,6 +157,33 @@ gemcode -C . --attach ./report.pdf "Summarize this"
|
|
|
157
157
|
gemcode kaira -C .
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
+
### Orchestration (Kaira + org delegation)
|
|
161
|
+
|
|
162
|
+
Docs:
|
|
163
|
+
- `../docs/orchestration.md`
|
|
164
|
+
|
|
165
|
+
In one terminal:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
gemcode kaira -C .
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
In another terminal:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
gemcode -C .
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Then in the REPL/TUI:
|
|
178
|
+
|
|
179
|
+
```text
|
|
180
|
+
/org tree
|
|
181
|
+
/org hire verifier "QA / test planner" subagent gemcode "Find risks, propose tests, review plans."
|
|
182
|
+
/org assign verifier "Review the plan and propose tests"
|
|
183
|
+
/kaira jobs
|
|
184
|
+
/kaira follow <job_id_prefix>
|
|
185
|
+
```
|
|
186
|
+
|
|
160
187
|
### Start the IDE bridge
|
|
161
188
|
```bash
|
|
162
189
|
gemcode ide --stdio
|
|
@@ -167,6 +194,10 @@ gemcode ide --stdio
|
|
|
167
194
|
gemcode live-audio -C .
|
|
168
195
|
```
|
|
169
196
|
|
|
197
|
+
Status note:
|
|
198
|
+
- `live-audio` is currently **experimental** and may fail due to upstream Gemini Live availability/reliability (for example transient `1011` internal errors).
|
|
199
|
+
- Treat this as **future scope** for production workflows.
|
|
200
|
+
|
|
170
201
|
## REPL command highlights
|
|
171
202
|
|
|
172
203
|
| Command | Purpose |
|
|
@@ -733,6 +733,11 @@ If you need more tool usage examples, set `GEMCODE_VERBOSE_INSTRUCTIONS=1`.
|
|
|
733
733
|
- Always give the sub-agent enough context to operate independently.
|
|
734
734
|
- End your task prompt with "Summarise your findings clearly." so the result is useful.
|
|
735
735
|
|
|
736
|
+
- **`spawn_subtasks`** — spawn multiple isolated sub-agents in parallel (preferred fan-out helper).
|
|
737
|
+
- Use this instead of manually issuing many `run_subtask` calls.
|
|
738
|
+
- Provide 3–6 focused tasks; keep each subtask self-contained.
|
|
739
|
+
- Then synthesise the combined findings into a single plan/answer.
|
|
740
|
+
|
|
736
741
|
## Multi-step task execution
|
|
737
742
|
One user message = many model↔tool rounds (up to 256 LLM calls by default). This is intentional — you are expected to do complete tasks autonomously.
|
|
738
743
|
|
|
@@ -25,6 +25,82 @@ _TRANSIENT_RETRY_DELAYS = [2.0, 5.0, 12.0]
|
|
|
25
25
|
|
|
26
26
|
_HITL_PROMPT_LOCK = Lock()
|
|
27
27
|
|
|
28
|
+
async def _maybe_enqueue_kaira_autopilot(*, cfg: "GemCodeConfig", session_id: str) -> None:
|
|
29
|
+
"""If Kaira IPC is available, enqueue background quality checks after edits."""
|
|
30
|
+
try:
|
|
31
|
+
enabled = os.environ.get("GEMCODE_KAIRA_AUTOPILOT", "1").strip().lower() in (
|
|
32
|
+
"1",
|
|
33
|
+
"true",
|
|
34
|
+
"yes",
|
|
35
|
+
"on",
|
|
36
|
+
)
|
|
37
|
+
if not enabled:
|
|
38
|
+
return
|
|
39
|
+
# Only run when files were touched (edit tools track this).
|
|
40
|
+
touched = getattr(cfg, "_touched_paths", None)
|
|
41
|
+
if not touched:
|
|
42
|
+
return
|
|
43
|
+
# Avoid enqueuing repeatedly within the same session unless touched paths change.
|
|
44
|
+
last_fp = str(getattr(cfg, "_kaira_autopilot_fp", "") or "")
|
|
45
|
+
fp = ",".join(sorted(str(x) for x in touched))[:4000]
|
|
46
|
+
if fp and fp == last_fp:
|
|
47
|
+
return
|
|
48
|
+
object.__setattr__(cfg, "_kaira_autopilot_fp", fp)
|
|
49
|
+
|
|
50
|
+
sock = os.environ.get("GEMCODE_KAIRA_SOCKET") or str(cfg.project_root / ".gemcode" / "ipc.sock")
|
|
51
|
+
if not Path(sock).exists():
|
|
52
|
+
return
|
|
53
|
+
# Heuristic: suggest likely checks, but let the agent choose based on repo.
|
|
54
|
+
prompt = (
|
|
55
|
+
"You are Kaira (background worker). Run the most relevant automated quality checks for this repo "
|
|
56
|
+
"based on its files, and report succinctly.\n\n"
|
|
57
|
+
"Rules:\n"
|
|
58
|
+
"- Prefer fast checks first (lint/typecheck/unit tests).\n"
|
|
59
|
+
"- If Python: try `pytest -q` (or detect other common runners).\n"
|
|
60
|
+
"- If Node: try `npm test` / `npm run lint` when package.json exists.\n"
|
|
61
|
+
"- If there are failures, include the smallest actionable summary and exact command to reproduce.\n"
|
|
62
|
+
"- If everything passes, say PASS and list what you ran.\n"
|
|
63
|
+
"- Return a final STRICT JSON report with keys: status, summary, evidence, recommended_next_actions.\n\n"
|
|
64
|
+
f"Touched files (recent): {', '.join(sorted(list(touched))[:30])}"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Org-aware routing: delegate to the kaira org member if present so the
|
|
68
|
+
# manager/worker hierarchy stays consistent.
|
|
69
|
+
try:
|
|
70
|
+
auto_org = os.environ.get("GEMCODE_AUTO_SLASH_ORG", "1").strip().lower() in (
|
|
71
|
+
"1",
|
|
72
|
+
"true",
|
|
73
|
+
"yes",
|
|
74
|
+
"on",
|
|
75
|
+
)
|
|
76
|
+
if auto_org:
|
|
77
|
+
from gemcode.org import find_member
|
|
78
|
+
m = find_member(cfg.project_root, "kaira")
|
|
79
|
+
if m is not None and m.kind == "kaira_worker":
|
|
80
|
+
from gemcode.tools.org_tools import make_org_tools
|
|
81
|
+
tools = make_org_tools(cfg)
|
|
82
|
+
org_delegate = None
|
|
83
|
+
for t in tools:
|
|
84
|
+
if getattr(t, "__name__", "") == "org_delegate":
|
|
85
|
+
org_delegate = t
|
|
86
|
+
break
|
|
87
|
+
if org_delegate is not None:
|
|
88
|
+
await org_delegate("kaira", prompt, "") # type: ignore[misc]
|
|
89
|
+
return
|
|
90
|
+
except Exception:
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
from gemcode.kaira_client import KairaIpcClient
|
|
94
|
+
|
|
95
|
+
client = await KairaIpcClient.connect(socket_path=sock)
|
|
96
|
+
try:
|
|
97
|
+
# Low priority by default; user can override with env in future.
|
|
98
|
+
await client.request(action="enqueue", prompt=prompt, priority=-1, session_id=session_id)
|
|
99
|
+
finally:
|
|
100
|
+
await client.close()
|
|
101
|
+
except Exception:
|
|
102
|
+
return
|
|
103
|
+
|
|
28
104
|
|
|
29
105
|
def _events_to_text(events: list[Any]) -> str:
|
|
30
106
|
"""Best-effort extraction of assistant text from ADK events."""
|
|
@@ -102,6 +178,56 @@ async def run_turn(
|
|
|
102
178
|
object.__setattr__(cfg, "_risk_score", risk)
|
|
103
179
|
except Exception:
|
|
104
180
|
pass
|
|
181
|
+
|
|
182
|
+
# Deterministic manager dispatcher: pre-delegate certain work to org members
|
|
183
|
+
# and inject their results into the prompt before the main agent runs.
|
|
184
|
+
try:
|
|
185
|
+
auto_mgr = os.environ.get("GEMCODE_MANAGER_DISPATCH", "1").strip().lower() in (
|
|
186
|
+
"1","true","yes","on"
|
|
187
|
+
)
|
|
188
|
+
if auto_mgr:
|
|
189
|
+
import re
|
|
190
|
+
p0 = (prompt or "")[:12_000]
|
|
191
|
+
# Only do this for broad/complex prompts.
|
|
192
|
+
score = float(getattr(cfg, "_parallelism_score", 0.0) or 0.0)
|
|
193
|
+
if score >= float(os.environ.get("GEMCODE_MANAGER_DISPATCH_THRESHOLD", "0.7")):
|
|
194
|
+
from gemcode.tools.org_tools import make_org_tools
|
|
195
|
+
tools = make_org_tools(cfg)
|
|
196
|
+
org_delegate = None
|
|
197
|
+
for t in tools:
|
|
198
|
+
if getattr(t, "__name__", "") == "org_delegate":
|
|
199
|
+
org_delegate = t
|
|
200
|
+
break
|
|
201
|
+
# Ask verifier for a quick risk check / plan critique (fast, in-process).
|
|
202
|
+
if org_delegate is not None and re.search(r"\\b(fix|refactor|rewrite|change|implement)\\b", p0, re.I):
|
|
203
|
+
v = await org_delegate("verifier", "Review the requested task and list key risks + verification steps.", p0) # type: ignore[misc]
|
|
204
|
+
if isinstance(v, dict) and v.get("ok") and v.get("result"):
|
|
205
|
+
object.__setattr__(cfg, "_manager_verifier_context", str(v.get("result"))[:4000])
|
|
206
|
+
except Exception:
|
|
207
|
+
pass
|
|
208
|
+
# Parallelism score: heuristic signal for automatic subtask fan-out.
|
|
209
|
+
try:
|
|
210
|
+
import re
|
|
211
|
+
p2 = (prompt or "")[:20_000]
|
|
212
|
+
par = 0.0
|
|
213
|
+
if len(p2) > 800:
|
|
214
|
+
par += 0.15
|
|
215
|
+
if len(p2) > 2400:
|
|
216
|
+
par += 0.2
|
|
217
|
+
if re.search(r"\b(analy[sz]e|audit|map|inventory|scan|survey)\b", p2, re.I):
|
|
218
|
+
par += 0.2
|
|
219
|
+
if re.search(r"\b(refactor|migrate|rewrite|redesign|architecture)\b", p2, re.I):
|
|
220
|
+
par += 0.2
|
|
221
|
+
if re.search(r"\b(across|entire|whole|end-to-end|e2e|multiple modules|many files)\b", p2, re.I):
|
|
222
|
+
par += 0.2
|
|
223
|
+
if p2.count("/") >= 8 or p2.count(".py") + p2.count(".ts") + p2.count(".tsx") >= 5:
|
|
224
|
+
par += 0.15
|
|
225
|
+
if attachment_paths:
|
|
226
|
+
par += 0.08
|
|
227
|
+
par = max(0.0, min(1.0, float(par)))
|
|
228
|
+
object.__setattr__(cfg, "_parallelism_score", par)
|
|
229
|
+
except Exception:
|
|
230
|
+
pass
|
|
105
231
|
run_config = (
|
|
106
232
|
RunConfig(max_llm_calls=max_llm_calls) if max_llm_calls is not None else None
|
|
107
233
|
)
|
|
@@ -222,9 +348,48 @@ async def run_turn(
|
|
|
222
348
|
for w in attach_warn:
|
|
223
349
|
print(f"[gemcode] {w}", file=sys.stderr)
|
|
224
350
|
else:
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
)
|
|
351
|
+
effective_prompt = prompt
|
|
352
|
+
# Auto fan-out: when prompt looks broad, guide model to spawn parallel
|
|
353
|
+
# isolated subtasks first (bounded) and then synthesise.
|
|
354
|
+
try:
|
|
355
|
+
auto_on = os.environ.get("GEMCODE_AUTO_FANOUT", "1").strip().lower() in (
|
|
356
|
+
"1",
|
|
357
|
+
"true",
|
|
358
|
+
"yes",
|
|
359
|
+
"on",
|
|
360
|
+
)
|
|
361
|
+
threshold = float(os.environ.get("GEMCODE_AUTO_FANOUT_THRESHOLD", "0.6"))
|
|
362
|
+
if auto_on and cfg is not None:
|
|
363
|
+
score = float(getattr(cfg, "_parallelism_score", 0.0) or 0.0)
|
|
364
|
+
already = bool(getattr(cfg, "_auto_fanout_applied", False))
|
|
365
|
+
if (not already) and score >= threshold:
|
|
366
|
+
object.__setattr__(cfg, "_auto_fanout_applied", True)
|
|
367
|
+
effective_prompt = (
|
|
368
|
+
"Before you answer, do a quick parallel exploration pass:\n"
|
|
369
|
+
"- Decompose this into 3–6 independent investigation subtasks.\n"
|
|
370
|
+
"- Call `spawn_subtasks(tasks=[...], max_concurrency=4)` to run them in parallel.\n"
|
|
371
|
+
"- If an org member is appropriate, prefer delegating with `org_delegate` / `org_spawn`.\n"
|
|
372
|
+
"- Synthesize the results into a single plan/answer, then proceed.\n\n"
|
|
373
|
+
+ (prompt or "")
|
|
374
|
+
)
|
|
375
|
+
except Exception:
|
|
376
|
+
pass
|
|
377
|
+
|
|
378
|
+
# Inject dispatcher context (e.g., verifier critique) if available.
|
|
379
|
+
try:
|
|
380
|
+
if cfg is not None:
|
|
381
|
+
ctx = str(getattr(cfg, "_manager_verifier_context", "") or "").strip()
|
|
382
|
+
if ctx:
|
|
383
|
+
effective_prompt = (
|
|
384
|
+
"Manager pre-delegation result (verifier):\n"
|
|
385
|
+
+ ctx
|
|
386
|
+
+ "\n\nUser request:\n"
|
|
387
|
+
+ (effective_prompt or "")
|
|
388
|
+
)
|
|
389
|
+
except Exception:
|
|
390
|
+
pass
|
|
391
|
+
|
|
392
|
+
current_message = types.Content(role="user", parts=[types.Part(text=effective_prompt)])
|
|
228
393
|
|
|
229
394
|
async def _await_runner_events(
|
|
230
395
|
*, next_message: types.Content, do_reset: bool
|
|
@@ -347,6 +512,12 @@ async def run_turn(
|
|
|
347
512
|
)
|
|
348
513
|
continue
|
|
349
514
|
|
|
515
|
+
# Background autopilot: if we touched files, enqueue Kaira checks (best-effort).
|
|
516
|
+
if cfg is not None:
|
|
517
|
+
try:
|
|
518
|
+
await _maybe_enqueue_kaira_autopilot(cfg=cfg, session_id=session_id)
|
|
519
|
+
except Exception:
|
|
520
|
+
pass
|
|
350
521
|
return collected
|
|
351
522
|
finally:
|
|
352
523
|
if cfg is not None and orig_ctx_chars is not None:
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import uuid
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any, AsyncIterator
|
|
7
|
+
|
|
8
|
+
from gemcode.ide_protocol import IdeEmitter, make_response, parse_json_line
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class KairaIpcClient:
|
|
13
|
+
reader: asyncio.StreamReader
|
|
14
|
+
writer: asyncio.StreamWriter
|
|
15
|
+
emitter: IdeEmitter
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
async def connect(cls, *, socket_path: str) -> "KairaIpcClient":
|
|
19
|
+
reader, writer = await asyncio.open_unix_connection(socket_path)
|
|
20
|
+
return cls(reader=reader, writer=writer, emitter=IdeEmitter(stream=writer))
|
|
21
|
+
|
|
22
|
+
async def close(self) -> None:
|
|
23
|
+
try:
|
|
24
|
+
self.writer.close()
|
|
25
|
+
await self.writer.wait_closed()
|
|
26
|
+
except Exception:
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
async def request(self, *, action: str, **payload: Any) -> dict[str, Any]:
|
|
30
|
+
req_id = f"req_{uuid.uuid4().hex[:12]}"
|
|
31
|
+
msg = {"type": "request", "id": req_id, "action": action}
|
|
32
|
+
msg.update(payload)
|
|
33
|
+
self.emitter.send(msg)
|
|
34
|
+
# Wait for matching response.
|
|
35
|
+
while True:
|
|
36
|
+
line = await self.reader.readline()
|
|
37
|
+
if not line:
|
|
38
|
+
return make_response(id=req_id, ok=False, error="ipc_eof")
|
|
39
|
+
obj = parse_json_line(line.decode("utf-8", errors="replace").strip())
|
|
40
|
+
if obj.get("type") == "response" and str(obj.get("id") or "") == req_id:
|
|
41
|
+
return obj
|
|
42
|
+
# Other messages (events) are ignored here; caller should use iter_messages.
|
|
43
|
+
|
|
44
|
+
async def iter_messages(self) -> AsyncIterator[dict[str, Any]]:
|
|
45
|
+
while True:
|
|
46
|
+
line = await self.reader.readline()
|
|
47
|
+
if not line:
|
|
48
|
+
return
|
|
49
|
+
obj = parse_json_line(line.decode("utf-8", errors="replace").strip())
|
|
50
|
+
yield obj
|
|
51
|
+
|