gemcode 0.4.5__tar.gz → 0.4.7__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.4.5/src/gemcode.egg-info → gemcode-0.4.7}/PKG-INFO +1 -1
- {gemcode-0.4.5 → gemcode-0.4.7}/pyproject.toml +1 -1
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/agent_mesh.py +60 -16
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/invoke.py +1 -4
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/org.py +19 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/repl_slash.py +21 -1
- {gemcode-0.4.5 → gemcode-0.4.7/src/gemcode.egg-info}/PKG-INFO +1 -1
- {gemcode-0.4.5 → gemcode-0.4.7}/LICENSE +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/MANIFEST.in +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/README.md +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/setup.cfg +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/__init__.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/__main__.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/a2a_bridge.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/agent.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/agent_habits.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/agent_intelligence.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/agent_triggers.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/audit.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/autocompact.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/automations.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/autotune.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/callbacks.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/capability_routing.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/checkpoints.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/cli.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/codebase_awareness.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/compaction.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/computer_use/__init__.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/computer_use/browser_computer.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/config.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/context_budget.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/context_warning.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/credentials.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/curated_memory.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/delegation_learning.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/dynamic_policy.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/evals/harness.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/event_bus.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/fleet_reports.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/hitl_session.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/hooks.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/ide_protocol.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/ide_stdio.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/intent_classifier.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/interactions.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/kaira_client.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/kaira_daemon.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/kaira_ipc.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/kaira_job_store.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/learning.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/limits.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/live_audio_engine.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/logging_config.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/mcp_loader.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/memory/__init__.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/memory/embedding_memory_service.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/memory/file_memory_service.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/modality_tools.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/model_errors.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/model_routing.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/multimodal_input.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/openapi_loader.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/output_styles.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/paths.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/permissions.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/plugins/__init__.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/policy_profile.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/pricing.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/prompt_suggestions.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/query/__init__.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/query/config.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/query/deps.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/query/engine.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/query/stop_hooks.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/query/token_budget.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/query/transitions.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/query_sanitizer.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/refine.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/repl_commands.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/review_agent.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/rules.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/self_healing.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/session_runtime.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/session_store.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/session_summariser.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/skills.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/slash_commands.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/thinking.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tool_prompt_manifest.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tool_registry.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tool_result_store.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tool_synthesis.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/__init__.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/automations_tools.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/bash.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/browser.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/compress_memory.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/curated_memory.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/edit.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/filesystem.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/notebook.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/notes.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/org_tools.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/repo_map.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/search.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/shell.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/shell_gate.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/skills.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/subtask.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/tasks.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/think.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/todo.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/user_choice.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/veomem_tools.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/web.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools/web_search.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tools_inspector.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/trust.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tui/input_handler.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tui/scrollback.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tui/spinner.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tui/welcome_banner.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/tui/welcome_rich.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/veomem_bridge.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/version.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/vertex.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/wal.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/web/__init__.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/web/sse_adapter.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/web/terminal_repl.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/web/web_sse_compat.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode/workspace_hints.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode.egg-info/SOURCES.txt +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode.egg-info/dependency_links.txt +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode.egg-info/entry_points.txt +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode.egg-info/requires.txt +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/src/gemcode.egg-info/top_level.txt +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_add_dir.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_agent_habits.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_agent_instruction.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_agent_mesh.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_autocompact.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_automations.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_capability_routing.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_checkpoint_diff_command.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_cli_init.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_compress_memory_tool.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_computer_use_permissions.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_context_budget.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_context_warning.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_credentials.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_eval_harness_layout.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_event_bus.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_fleet_reports.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_ide_stdio_attachments.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_interactive_permission_ask.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_kaira_ipc_paths.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_kaira_scheduler.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_modality_tools.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_model_error_retry.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_model_errors.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_model_routing.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_multimodal_input.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_output_styles_and_rules.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_paths.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_permissions.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_prompt_suggestions.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_repl_commands.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_repl_slash.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_session_runtime_cache.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_skills.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_slash_commands.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_slash_completion_registry.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_thinking_config.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_token_budget.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_tool_context_circulation.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_tools.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_tools_inspector.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_web_sse_adapter.py +0 -0
- {gemcode-0.4.5 → gemcode-0.4.7}/tests/test_workspace_hints.py +0 -0
|
@@ -66,7 +66,11 @@ class AgentMesh:
|
|
|
66
66
|
self._running: dict[str, asyncio.Task] = {}
|
|
67
67
|
self._completed: list[AgentJob] = []
|
|
68
68
|
self._scheduler_task: asyncio.Task | None = None
|
|
69
|
-
self._stop
|
|
69
|
+
self._stop: asyncio.Event | None = None # Created in background loop
|
|
70
|
+
self._bg_thread: "threading.Thread | None" = None
|
|
71
|
+
self._bg_loop: asyncio.AbstractEventLoop | None = None
|
|
72
|
+
# _stop is created lazily in the background loop
|
|
73
|
+
self._stop_flag = False
|
|
70
74
|
|
|
71
75
|
# Subscribe to org.assign messages on the bus
|
|
72
76
|
self._bus.subscribe(
|
|
@@ -112,25 +116,69 @@ class AgentMesh:
|
|
|
112
116
|
return self._bus
|
|
113
117
|
|
|
114
118
|
def start(self) -> None:
|
|
115
|
-
"""Start the background
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
"""Start the mesh in a dedicated background thread with its own event loop.
|
|
120
|
+
|
|
121
|
+
This is critical: the TUI's event loop is blocked on user input most of the
|
|
122
|
+
time. If we run mesh jobs on the same loop, they can only execute during the
|
|
123
|
+
brief moments prompt_toolkit yields control — not enough for a full agent turn.
|
|
124
|
+
|
|
125
|
+
By running in a separate thread, mesh jobs execute independently. Results are
|
|
126
|
+
written to fleet_reports.jsonl and picked up on the next user turn, or printed
|
|
127
|
+
to the terminal via the notification callback.
|
|
128
|
+
"""
|
|
129
|
+
if self._bg_thread is not None and self._bg_thread.is_alive():
|
|
130
|
+
return # Already running
|
|
131
|
+
|
|
132
|
+
import threading
|
|
133
|
+
|
|
134
|
+
def _run_bg_loop():
|
|
135
|
+
"""Background thread entry: create a new event loop and run the scheduler."""
|
|
136
|
+
self._bg_loop = asyncio.new_event_loop()
|
|
137
|
+
asyncio.set_event_loop(self._bg_loop)
|
|
138
|
+
try:
|
|
139
|
+
self._bg_loop.run_until_complete(self._bg_main())
|
|
140
|
+
except Exception:
|
|
141
|
+
pass
|
|
142
|
+
finally:
|
|
143
|
+
try:
|
|
144
|
+
self._bg_loop.close()
|
|
145
|
+
except Exception:
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
self._bg_thread = threading.Thread(target=_run_bg_loop, daemon=True, name="gemcode-mesh")
|
|
149
|
+
self._bg_thread.start()
|
|
150
|
+
|
|
151
|
+
async def _bg_main(self) -> None:
|
|
152
|
+
"""Main coroutine for the background event loop."""
|
|
153
|
+
self._stop = asyncio.Event() # Create in the correct loop
|
|
154
|
+
|
|
155
|
+
# Start all sub-systems in this loop
|
|
156
|
+
self._scheduler_task = asyncio.create_task(self._scheduler_loop())
|
|
157
|
+
|
|
119
158
|
if self._trigger_engine is not None:
|
|
120
159
|
self._trigger_engine.start()
|
|
121
|
-
# Start habit scheduler (cron/interval recurring tasks)
|
|
122
160
|
if self._habit_scheduler is not None:
|
|
123
161
|
self._habit_scheduler.start()
|
|
124
162
|
|
|
163
|
+
# Wait until stopped
|
|
164
|
+
await self._stop.wait()
|
|
165
|
+
|
|
125
166
|
def stop(self) -> None:
|
|
126
167
|
"""Stop the scheduler and trigger engine."""
|
|
127
|
-
self._stop
|
|
168
|
+
if self._stop is not None:
|
|
169
|
+
self._stop.set()
|
|
128
170
|
if self._scheduler_task and not self._scheduler_task.done():
|
|
129
171
|
self._scheduler_task.cancel()
|
|
130
172
|
if self._trigger_engine is not None:
|
|
131
173
|
self._trigger_engine.stop()
|
|
132
174
|
if self._habit_scheduler is not None:
|
|
133
175
|
self._habit_scheduler.stop()
|
|
176
|
+
# Signal the background loop to exit
|
|
177
|
+
if self._bg_loop is not None:
|
|
178
|
+
try:
|
|
179
|
+
self._bg_loop.call_soon_threadsafe(self._bg_loop.stop)
|
|
180
|
+
except Exception:
|
|
181
|
+
pass
|
|
134
182
|
|
|
135
183
|
def enqueue(
|
|
136
184
|
self,
|
|
@@ -141,7 +189,7 @@ class AgentMesh:
|
|
|
141
189
|
member_name: str = "",
|
|
142
190
|
meta: dict[str, Any] | None = None,
|
|
143
191
|
) -> str:
|
|
144
|
-
"""Enqueue a job and return its job_id."""
|
|
192
|
+
"""Enqueue a job and return its job_id. Thread-safe."""
|
|
145
193
|
job_id = f"mesh_{uuid.uuid4().hex[:10]}"
|
|
146
194
|
self._seq += 1
|
|
147
195
|
job = AgentJob(
|
|
@@ -163,13 +211,9 @@ class AgentMesh:
|
|
|
163
211
|
payload={"job_id": job_id, "member": member_name, "priority": priority},
|
|
164
212
|
))
|
|
165
213
|
|
|
166
|
-
# Auto-start
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if self._scheduler_task is None or self._scheduler_task.done():
|
|
170
|
-
self._scheduler_task = loop.create_task(self._scheduler_loop())
|
|
171
|
-
except RuntimeError:
|
|
172
|
-
pass
|
|
214
|
+
# Auto-start the background thread if not running
|
|
215
|
+
if self._bg_thread is None or not self._bg_thread.is_alive():
|
|
216
|
+
self.start()
|
|
173
217
|
|
|
174
218
|
return job_id
|
|
175
219
|
|
|
@@ -267,7 +311,7 @@ class AgentMesh:
|
|
|
267
311
|
|
|
268
312
|
async def _scheduler_loop(self) -> None:
|
|
269
313
|
"""Continuously dequeue and run jobs."""
|
|
270
|
-
while not self._stop.is_set():
|
|
314
|
+
while self._stop is None or not self._stop.is_set():
|
|
271
315
|
try:
|
|
272
316
|
await self._sem.acquire()
|
|
273
317
|
try:
|
|
@@ -111,10 +111,7 @@ async def run_turn(
|
|
|
111
111
|
try:
|
|
112
112
|
from gemcode.agent_mesh import ensure_mesh
|
|
113
113
|
mesh = ensure_mesh(cfg)
|
|
114
|
-
|
|
115
|
-
mesh.start()
|
|
116
|
-
except Exception:
|
|
117
|
-
pass
|
|
114
|
+
mesh.start() # Starts background thread if not already running
|
|
118
115
|
except Exception:
|
|
119
116
|
pass
|
|
120
117
|
|
|
@@ -336,6 +336,25 @@ def hire_member(
|
|
|
336
336
|
root = resolve_fleet_root(project_root)
|
|
337
337
|
org = load_org(root)
|
|
338
338
|
members = list(org.get("members") or [])
|
|
339
|
+
|
|
340
|
+
# Prevent duplicate names — ADK sub_agents require unique names
|
|
341
|
+
clean_name = name.strip().lower()
|
|
342
|
+
for existing in members:
|
|
343
|
+
if str(existing.get("name", "")).strip().lower() == clean_name:
|
|
344
|
+
# Return the existing member instead of creating a duplicate
|
|
345
|
+
return OrgMember(
|
|
346
|
+
id=str(existing.get("id") or ""),
|
|
347
|
+
name=str(existing.get("name") or ""),
|
|
348
|
+
title=str(existing.get("title") or title),
|
|
349
|
+
kind=str(existing.get("kind") or kind),
|
|
350
|
+
address=str(existing.get("address") or ""),
|
|
351
|
+
workspace_rel=str(existing.get("workspace_rel") or ""),
|
|
352
|
+
reports_to=str(existing.get("reports_to") or ""),
|
|
353
|
+
skill_name=str(existing.get("skill_name") or ""),
|
|
354
|
+
description=str(existing.get("description") or description),
|
|
355
|
+
created_ms=int(existing.get("created_ms") or 0),
|
|
356
|
+
)
|
|
357
|
+
|
|
339
358
|
now = _now_ms()
|
|
340
359
|
mid = f"m_{uuid.uuid4().hex[:10]}"
|
|
341
360
|
m = OrgMember(
|
|
@@ -2964,7 +2964,27 @@ async def process_repl_slash(
|
|
|
2964
2964
|
out()
|
|
2965
2965
|
return ReplSlashResult(skip_model_turn=True)
|
|
2966
2966
|
except Exception as e:
|
|
2967
|
-
|
|
2967
|
+
# Fallback: use the mesh to run the prompt for this agent
|
|
2968
|
+
out(f"[agent] runtime not reachable, using mesh fallback")
|
|
2969
|
+
try:
|
|
2970
|
+
from gemcode.tools.org_tools import make_org_tools
|
|
2971
|
+
org_delegate = None
|
|
2972
|
+
for t in make_org_tools(cfg):
|
|
2973
|
+
if getattr(t, "__name__", "") == "org_delegate":
|
|
2974
|
+
org_delegate = t
|
|
2975
|
+
break
|
|
2976
|
+
if org_delegate is not None:
|
|
2977
|
+
d = await org_delegate(member=m.name, task=prompt, context="")
|
|
2978
|
+
if isinstance(d, dict) and d.get("ok"):
|
|
2979
|
+
out(f"[agent] delegated via mesh: member={m.name}")
|
|
2980
|
+
if d.get("result"):
|
|
2981
|
+
out(str(d.get("result"))[:2000])
|
|
2982
|
+
else:
|
|
2983
|
+
out(f"[agent] mesh delegation failed: {d.get('error') if isinstance(d, dict) else d}")
|
|
2984
|
+
else:
|
|
2985
|
+
out("[agent] org_delegate tool not available")
|
|
2986
|
+
except Exception as e2:
|
|
2987
|
+
out(f"[agent] fallback failed: {type(e2).__name__}: {e2}")
|
|
2968
2988
|
out()
|
|
2969
2989
|
return ReplSlashResult(skip_model_turn=True)
|
|
2970
2990
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|