gemcode 0.3.105__tar.gz → 0.3.107__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.105/src/gemcode.egg-info → gemcode-0.3.107}/PKG-INFO +40 -1
- {gemcode-0.3.105 → gemcode-0.3.107}/README.md +39 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/pyproject.toml +1 -1
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/agent.py +9 -2
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/cli.py +29 -4
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/config.py +25 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/invoke.py +184 -4
- gemcode-0.3.107/src/gemcode/kaira_client.py +51 -0
- gemcode-0.3.107/src/gemcode/kaira_daemon.py +657 -0
- gemcode-0.3.107/src/gemcode/kaira_ipc.py +289 -0
- gemcode-0.3.107/src/gemcode/kaira_job_store.py +157 -0
- gemcode-0.3.107/src/gemcode/org.py +296 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/repl_commands.py +5 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/repl_slash.py +311 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tool_prompt_manifest.py +9 -1
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/__init__.py +8 -1
- gemcode-0.3.107/src/gemcode/tools/org_tools.py +158 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/subtask.py +48 -2
- gemcode-0.3.107/src/gemcode/tools/user_choice.py +58 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tui/scrollback.py +327 -12
- {gemcode-0.3.105 → gemcode-0.3.107/src/gemcode.egg-info}/PKG-INFO +40 -1
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode.egg-info/SOURCES.txt +6 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_multimodal_input.py +3 -1
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_tools.py +5 -1
- gemcode-0.3.105/src/gemcode/kaira_daemon.py +0 -221
- {gemcode-0.3.105 → gemcode-0.3.107}/LICENSE +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/MANIFEST.in +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/setup.cfg +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/__init__.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/__main__.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/audit.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/autocompact.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/autotune.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/callbacks.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/capability_routing.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/checkpoints.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/compaction.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/computer_use/__init__.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/computer_use/browser_computer.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/context_budget.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/context_warning.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/credentials.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/curated_memory.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/dynamic_policy.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/evals/harness.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/hitl_session.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/hooks.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/ide_protocol.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/ide_stdio.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/intent_classifier.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/interactions.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/learning.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/limits.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/live_audio_engine.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/logging_config.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/mcp_loader.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/memory/__init__.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/memory/embedding_memory_service.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/memory/file_memory_service.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/modality_tools.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/model_errors.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/model_routing.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/multimodal_input.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/openapi_loader.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/output_styles.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/paths.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/permissions.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/plugins/__init__.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/policy_profile.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/pricing.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/prompt_suggestions.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/query/__init__.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/query/config.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/query/deps.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/query/engine.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/query/stop_hooks.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/query/token_budget.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/query/transitions.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/query_sanitizer.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/refine.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/review_agent.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/rules.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/session_runtime.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/session_store.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/session_summariser.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/skills.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/slash_commands.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/thinking.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tool_registry.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tool_result_store.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/bash.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/browser.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/compress_memory.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/curated_memory.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/edit.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/filesystem.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/notebook.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/notes.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/repo_map.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/search.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/shell.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/shell_gate.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/skills.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/tasks.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/think.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/todo.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/veomem_tools.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/web.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools/web_search.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tools_inspector.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/trust.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tui/input_handler.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tui/spinner.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tui/welcome_banner.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/tui/welcome_rich.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/veomem_bridge.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/version.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/vertex.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/wal.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/web/__init__.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/web/sse_adapter.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/web/terminal_repl.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/web/web_sse_compat.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode/workspace_hints.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode.egg-info/dependency_links.txt +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode.egg-info/entry_points.txt +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode.egg-info/requires.txt +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/src/gemcode.egg-info/top_level.txt +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_add_dir.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_agent_instruction.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_autocompact.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_capability_routing.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_checkpoint_diff_command.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_cli_init.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_compress_memory_tool.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_computer_use_permissions.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_context_budget.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_context_warning.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_credentials.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_eval_harness_layout.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_ide_stdio_attachments.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_interactive_permission_ask.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_kaira_scheduler.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_modality_tools.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_model_error_retry.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_model_errors.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_model_routing.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_output_styles_and_rules.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_paths.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_permissions.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_prompt_suggestions.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_repl_commands.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_repl_slash.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_skills.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_slash_commands.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_slash_completion_registry.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_thinking_config.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_token_budget.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_tool_context_circulation.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_tools_inspector.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/tests/test_web_sse_adapter.py +0 -0
- {gemcode-0.3.105 → gemcode-0.3.107}/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.107
|
|
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
|
|
@@ -349,6 +360,33 @@ gemcode -C . --attach ./report.pdf "Summarize this"
|
|
|
349
360
|
gemcode kaira -C .
|
|
350
361
|
```
|
|
351
362
|
|
|
363
|
+
### Orchestration (Kaira + org delegation)
|
|
364
|
+
|
|
365
|
+
Docs:
|
|
366
|
+
- `../docs/orchestration.md`
|
|
367
|
+
|
|
368
|
+
In one terminal:
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
gemcode kaira -C .
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
In another terminal:
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
gemcode -C .
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Then in the REPL/TUI:
|
|
381
|
+
|
|
382
|
+
```text
|
|
383
|
+
/org tree
|
|
384
|
+
/org hire verifier "QA / test planner" subagent gemcode "Find risks, propose tests, review plans."
|
|
385
|
+
/org assign verifier "Review the plan and propose tests"
|
|
386
|
+
/kaira jobs
|
|
387
|
+
/kaira follow <job_id_prefix>
|
|
388
|
+
```
|
|
389
|
+
|
|
352
390
|
### Start the IDE bridge
|
|
353
391
|
```bash
|
|
354
392
|
gemcode ide --stdio
|
|
@@ -383,6 +421,7 @@ Status note:
|
|
|
383
421
|
| `/review` | Run a review workflow |
|
|
384
422
|
| `/eval` | Run evaluation gates |
|
|
385
423
|
| `/kaira` | Show scheduler usage help |
|
|
424
|
+
| `/super` | Super mode: auto-approve tools, no GemCode HITL · `/super off` |
|
|
386
425
|
|
|
387
426
|
Detailed behavior:
|
|
388
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
|
|
@@ -157,6 +168,33 @@ gemcode -C . --attach ./report.pdf "Summarize this"
|
|
|
157
168
|
gemcode kaira -C .
|
|
158
169
|
```
|
|
159
170
|
|
|
171
|
+
### Orchestration (Kaira + org delegation)
|
|
172
|
+
|
|
173
|
+
Docs:
|
|
174
|
+
- `../docs/orchestration.md`
|
|
175
|
+
|
|
176
|
+
In one terminal:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
gemcode kaira -C .
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
In another terminal:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
gemcode -C .
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Then in the REPL/TUI:
|
|
189
|
+
|
|
190
|
+
```text
|
|
191
|
+
/org tree
|
|
192
|
+
/org hire verifier "QA / test planner" subagent gemcode "Find risks, propose tests, review plans."
|
|
193
|
+
/org assign verifier "Review the plan and propose tests"
|
|
194
|
+
/kaira jobs
|
|
195
|
+
/kaira follow <job_id_prefix>
|
|
196
|
+
```
|
|
197
|
+
|
|
160
198
|
### Start the IDE bridge
|
|
161
199
|
```bash
|
|
162
200
|
gemcode ide --stdio
|
|
@@ -191,6 +229,7 @@ Status note:
|
|
|
191
229
|
| `/review` | Run a review workflow |
|
|
192
230
|
| `/eval` | Run evaluation gates |
|
|
193
231
|
| `/kaira` | Show scheduler usage help |
|
|
232
|
+
| `/super` | Super mode: auto-approve tools, no GemCode HITL · `/super off` |
|
|
194
233
|
|
|
195
234
|
Detailed behavior:
|
|
196
235
|
- [`../docs/cli-and-repl.md`](../docs/cli-and-repl.md)
|
|
@@ -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
|
|
|
@@ -1008,9 +1013,11 @@ def build_root_agent(
|
|
|
1008
1013
|
tools = [preload_memory, *tools]
|
|
1009
1014
|
|
|
1010
1015
|
# ADK built-in interactive + artifact tools — always available when ADK supports them.
|
|
1016
|
+
# In super mode, ``get_user_choice`` auto-picks the first option (no UI).
|
|
1011
1017
|
try:
|
|
1012
|
-
from
|
|
1013
|
-
|
|
1018
|
+
from gemcode.tools.user_choice import append_user_choice_load_artifacts_exit_loop
|
|
1019
|
+
|
|
1020
|
+
append_user_choice_load_artifacts_exit_loop(cfg, tools)
|
|
1014
1021
|
except Exception:
|
|
1015
1022
|
pass
|
|
1016
1023
|
|
|
@@ -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
|
|
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.
|
|
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:
|
|
@@ -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
|
|
@@ -295,11 +460,20 @@ async def run_turn(
|
|
|
295
460
|
and hasattr(sys.stdin, "isatty")
|
|
296
461
|
and sys.stdin.isatty()
|
|
297
462
|
)
|
|
463
|
+
auto_ok = bool(
|
|
464
|
+
cfg is not None
|
|
465
|
+
and (
|
|
466
|
+
bool(getattr(cfg, "yes_to_all", False))
|
|
467
|
+
or bool(getattr(cfg, "super_mode", False))
|
|
468
|
+
)
|
|
469
|
+
)
|
|
298
470
|
|
|
299
471
|
parts: list[types.Part] = []
|
|
300
472
|
for fc in confirmation_fcs:
|
|
301
473
|
tool_name, hint = _extract_hint_and_tool(fc)
|
|
302
|
-
if
|
|
474
|
+
if auto_ok:
|
|
475
|
+
ok = True
|
|
476
|
+
elif interactive_enabled:
|
|
303
477
|
suffix = f"\n Hint: {hint}" if hint else ""
|
|
304
478
|
ok = _prompt_yes_no(
|
|
305
479
|
f"\n[gemcode HITL] Approve tool call '{tool_name}'? [y/N]{suffix}\n> "
|
|
@@ -347,6 +521,12 @@ async def run_turn(
|
|
|
347
521
|
)
|
|
348
522
|
continue
|
|
349
523
|
|
|
524
|
+
# Background autopilot: if we touched files, enqueue Kaira checks (best-effort).
|
|
525
|
+
if cfg is not None:
|
|
526
|
+
try:
|
|
527
|
+
await _maybe_enqueue_kaira_autopilot(cfg=cfg, session_id=session_id)
|
|
528
|
+
except Exception:
|
|
529
|
+
pass
|
|
350
530
|
return collected
|
|
351
531
|
finally:
|
|
352
532
|
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
|
+
|