gemcode 0.4.6__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.
Files changed (183) hide show
  1. {gemcode-0.4.6/src/gemcode.egg-info → gemcode-0.4.7}/PKG-INFO +1 -1
  2. {gemcode-0.4.6 → gemcode-0.4.7}/pyproject.toml +1 -1
  3. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/agent_mesh.py +60 -16
  4. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/invoke.py +1 -4
  5. {gemcode-0.4.6 → gemcode-0.4.7/src/gemcode.egg-info}/PKG-INFO +1 -1
  6. {gemcode-0.4.6 → gemcode-0.4.7}/LICENSE +0 -0
  7. {gemcode-0.4.6 → gemcode-0.4.7}/MANIFEST.in +0 -0
  8. {gemcode-0.4.6 → gemcode-0.4.7}/README.md +0 -0
  9. {gemcode-0.4.6 → gemcode-0.4.7}/setup.cfg +0 -0
  10. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/__init__.py +0 -0
  11. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/__main__.py +0 -0
  12. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/a2a_bridge.py +0 -0
  13. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/agent.py +0 -0
  14. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/agent_habits.py +0 -0
  15. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/agent_intelligence.py +0 -0
  16. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/agent_triggers.py +0 -0
  17. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/audit.py +0 -0
  18. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/autocompact.py +0 -0
  19. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/automations.py +0 -0
  20. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/autotune.py +0 -0
  21. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/callbacks.py +0 -0
  22. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/capability_routing.py +0 -0
  23. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/checkpoints.py +0 -0
  24. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/cli.py +0 -0
  25. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/codebase_awareness.py +0 -0
  26. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/compaction.py +0 -0
  27. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/computer_use/__init__.py +0 -0
  28. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/computer_use/browser_computer.py +0 -0
  29. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/config.py +0 -0
  30. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/context_budget.py +0 -0
  31. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/context_warning.py +0 -0
  32. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/credentials.py +0 -0
  33. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/curated_memory.py +0 -0
  34. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/delegation_learning.py +0 -0
  35. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/dynamic_policy.py +0 -0
  36. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/evals/harness.py +0 -0
  37. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/event_bus.py +0 -0
  38. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/fleet_reports.py +0 -0
  39. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/hitl_session.py +0 -0
  40. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/hooks.py +0 -0
  41. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/ide_protocol.py +0 -0
  42. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/ide_stdio.py +0 -0
  43. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/intent_classifier.py +0 -0
  44. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/interactions.py +0 -0
  45. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/kaira_client.py +0 -0
  46. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/kaira_daemon.py +0 -0
  47. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/kaira_ipc.py +0 -0
  48. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/kaira_job_store.py +0 -0
  49. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/learning.py +0 -0
  50. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/limits.py +0 -0
  51. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/live_audio_engine.py +0 -0
  52. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/logging_config.py +0 -0
  53. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/mcp_loader.py +0 -0
  54. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/memory/__init__.py +0 -0
  55. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/memory/embedding_memory_service.py +0 -0
  56. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/memory/file_memory_service.py +0 -0
  57. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/modality_tools.py +0 -0
  58. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/model_errors.py +0 -0
  59. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/model_routing.py +0 -0
  60. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/multimodal_input.py +0 -0
  61. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/openapi_loader.py +0 -0
  62. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/org.py +0 -0
  63. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/output_styles.py +0 -0
  64. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/paths.py +0 -0
  65. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/permissions.py +0 -0
  66. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/plugins/__init__.py +0 -0
  67. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
  68. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
  69. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/policy_profile.py +0 -0
  70. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/pricing.py +0 -0
  71. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/prompt_suggestions.py +0 -0
  72. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/query/__init__.py +0 -0
  73. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/query/config.py +0 -0
  74. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/query/deps.py +0 -0
  75. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/query/engine.py +0 -0
  76. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/query/stop_hooks.py +0 -0
  77. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/query/token_budget.py +0 -0
  78. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/query/transitions.py +0 -0
  79. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/query_sanitizer.py +0 -0
  80. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/refine.py +0 -0
  81. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/repl_commands.py +0 -0
  82. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/repl_slash.py +0 -0
  83. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/review_agent.py +0 -0
  84. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/rules.py +0 -0
  85. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/self_healing.py +0 -0
  86. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/session_runtime.py +0 -0
  87. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/session_store.py +0 -0
  88. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/session_summariser.py +0 -0
  89. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/skills.py +0 -0
  90. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/slash_commands.py +0 -0
  91. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/thinking.py +0 -0
  92. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tool_prompt_manifest.py +0 -0
  93. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tool_registry.py +0 -0
  94. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tool_result_store.py +0 -0
  95. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tool_synthesis.py +0 -0
  96. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/__init__.py +0 -0
  97. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/automations_tools.py +0 -0
  98. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/bash.py +0 -0
  99. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/browser.py +0 -0
  100. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/compress_memory.py +0 -0
  101. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/curated_memory.py +0 -0
  102. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/edit.py +0 -0
  103. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/filesystem.py +0 -0
  104. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/notebook.py +0 -0
  105. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/notes.py +0 -0
  106. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/org_tools.py +0 -0
  107. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/repo_map.py +0 -0
  108. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/search.py +0 -0
  109. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/shell.py +0 -0
  110. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/shell_gate.py +0 -0
  111. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/skills.py +0 -0
  112. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/subtask.py +0 -0
  113. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/tasks.py +0 -0
  114. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/think.py +0 -0
  115. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/todo.py +0 -0
  116. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/user_choice.py +0 -0
  117. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/veomem_tools.py +0 -0
  118. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/web.py +0 -0
  119. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools/web_search.py +0 -0
  120. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tools_inspector.py +0 -0
  121. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/trust.py +0 -0
  122. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tui/input_handler.py +0 -0
  123. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tui/scrollback.py +0 -0
  124. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tui/spinner.py +0 -0
  125. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tui/welcome_banner.py +0 -0
  126. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/tui/welcome_rich.py +0 -0
  127. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/veomem_bridge.py +0 -0
  128. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/version.py +0 -0
  129. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/vertex.py +0 -0
  130. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/wal.py +0 -0
  131. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/web/__init__.py +0 -0
  132. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/web/sse_adapter.py +0 -0
  133. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/web/terminal_repl.py +0 -0
  134. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/web/web_sse_compat.py +0 -0
  135. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode/workspace_hints.py +0 -0
  136. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode.egg-info/SOURCES.txt +0 -0
  137. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode.egg-info/dependency_links.txt +0 -0
  138. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode.egg-info/entry_points.txt +0 -0
  139. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode.egg-info/requires.txt +0 -0
  140. {gemcode-0.4.6 → gemcode-0.4.7}/src/gemcode.egg-info/top_level.txt +0 -0
  141. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_add_dir.py +0 -0
  142. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_agent_habits.py +0 -0
  143. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_agent_instruction.py +0 -0
  144. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_agent_mesh.py +0 -0
  145. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_autocompact.py +0 -0
  146. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_automations.py +0 -0
  147. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_capability_routing.py +0 -0
  148. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_checkpoint_diff_command.py +0 -0
  149. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_cli_init.py +0 -0
  150. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_compress_memory_tool.py +0 -0
  151. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_computer_use_permissions.py +0 -0
  152. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_context_budget.py +0 -0
  153. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_context_warning.py +0 -0
  154. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_credentials.py +0 -0
  155. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_eval_harness_layout.py +0 -0
  156. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_event_bus.py +0 -0
  157. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_fleet_reports.py +0 -0
  158. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_ide_stdio_attachments.py +0 -0
  159. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_interactive_permission_ask.py +0 -0
  160. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_kaira_ipc_paths.py +0 -0
  161. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_kaira_scheduler.py +0 -0
  162. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_modality_tools.py +0 -0
  163. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_model_error_retry.py +0 -0
  164. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_model_errors.py +0 -0
  165. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_model_routing.py +0 -0
  166. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_multimodal_input.py +0 -0
  167. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_output_styles_and_rules.py +0 -0
  168. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_paths.py +0 -0
  169. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_permissions.py +0 -0
  170. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_prompt_suggestions.py +0 -0
  171. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_repl_commands.py +0 -0
  172. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_repl_slash.py +0 -0
  173. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_session_runtime_cache.py +0 -0
  174. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_skills.py +0 -0
  175. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_slash_commands.py +0 -0
  176. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_slash_completion_registry.py +0 -0
  177. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_thinking_config.py +0 -0
  178. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_token_budget.py +0 -0
  179. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_tool_context_circulation.py +0 -0
  180. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_tools.py +0 -0
  181. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_tools_inspector.py +0 -0
  182. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_web_sse_adapter.py +0 -0
  183. {gemcode-0.4.6 → gemcode-0.4.7}/tests/test_workspace_hints.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemcode
3
- Version: 0.4.6
3
+ Version: 0.4.7
4
4
  Summary: Local-first coding agent on Google Gemini + ADK
5
5
  Author: GemCode Contributors
6
6
  License: Apache License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gemcode"
7
- version = "0.4.6"
7
+ version = "0.4.7"
8
8
  description = "Local-first coding agent on Google Gemini + ADK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -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 = asyncio.Event()
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 scheduler loop and trigger engine."""
116
- if self._scheduler_task is None or self._scheduler_task.done():
117
- self._scheduler_task = asyncio.create_task(self._scheduler_loop())
118
- # Start self-triggering agents
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.set()
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 scheduler if not running
167
- try:
168
- loop = asyncio.get_running_loop()
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
- try:
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemcode
3
- Version: 0.4.6
3
+ Version: 0.4.7
4
4
  Summary: Local-first coding agent on Google Gemini + ADK
5
5
  Author: GemCode Contributors
6
6
  License: Apache License
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