gemcode 0.3.114__tar.gz → 0.3.115__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 (167) hide show
  1. {gemcode-0.3.114/src/gemcode.egg-info → gemcode-0.3.115}/PKG-INFO +1 -1
  2. {gemcode-0.3.114 → gemcode-0.3.115}/pyproject.toml +1 -1
  3. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/invoke.py +9 -2
  4. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/org_tools.py +81 -45
  5. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/subtask.py +101 -1
  6. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tui/scrollback.py +32 -0
  7. {gemcode-0.3.114 → gemcode-0.3.115/src/gemcode.egg-info}/PKG-INFO +1 -1
  8. {gemcode-0.3.114 → gemcode-0.3.115}/LICENSE +0 -0
  9. {gemcode-0.3.114 → gemcode-0.3.115}/MANIFEST.in +0 -0
  10. {gemcode-0.3.114 → gemcode-0.3.115}/README.md +0 -0
  11. {gemcode-0.3.114 → gemcode-0.3.115}/setup.cfg +0 -0
  12. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/__init__.py +0 -0
  13. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/__main__.py +0 -0
  14. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/agent.py +0 -0
  15. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/audit.py +0 -0
  16. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/autocompact.py +0 -0
  17. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/automations.py +0 -0
  18. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/autotune.py +0 -0
  19. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/callbacks.py +0 -0
  20. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/capability_routing.py +0 -0
  21. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/checkpoints.py +0 -0
  22. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/cli.py +0 -0
  23. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/compaction.py +0 -0
  24. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/computer_use/__init__.py +0 -0
  25. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/computer_use/browser_computer.py +0 -0
  26. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/config.py +0 -0
  27. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/context_budget.py +0 -0
  28. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/context_warning.py +0 -0
  29. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/credentials.py +0 -0
  30. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/curated_memory.py +0 -0
  31. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/dynamic_policy.py +0 -0
  32. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/evals/harness.py +0 -0
  33. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/hitl_session.py +0 -0
  34. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/hooks.py +0 -0
  35. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/ide_protocol.py +0 -0
  36. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/ide_stdio.py +0 -0
  37. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/intent_classifier.py +0 -0
  38. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/interactions.py +0 -0
  39. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/kaira_client.py +0 -0
  40. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/kaira_daemon.py +0 -0
  41. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/kaira_ipc.py +0 -0
  42. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/kaira_job_store.py +0 -0
  43. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/learning.py +0 -0
  44. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/limits.py +0 -0
  45. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/live_audio_engine.py +0 -0
  46. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/logging_config.py +0 -0
  47. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/mcp_loader.py +0 -0
  48. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/memory/__init__.py +0 -0
  49. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/memory/embedding_memory_service.py +0 -0
  50. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/memory/file_memory_service.py +0 -0
  51. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/modality_tools.py +0 -0
  52. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/model_errors.py +0 -0
  53. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/model_routing.py +0 -0
  54. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/multimodal_input.py +0 -0
  55. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/openapi_loader.py +0 -0
  56. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/org.py +0 -0
  57. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/output_styles.py +0 -0
  58. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/paths.py +0 -0
  59. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/permissions.py +0 -0
  60. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/plugins/__init__.py +0 -0
  61. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
  62. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
  63. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/policy_profile.py +0 -0
  64. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/pricing.py +0 -0
  65. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/prompt_suggestions.py +0 -0
  66. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/query/__init__.py +0 -0
  67. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/query/config.py +0 -0
  68. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/query/deps.py +0 -0
  69. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/query/engine.py +0 -0
  70. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/query/stop_hooks.py +0 -0
  71. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/query/token_budget.py +0 -0
  72. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/query/transitions.py +0 -0
  73. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/query_sanitizer.py +0 -0
  74. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/refine.py +0 -0
  75. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/repl_commands.py +0 -0
  76. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/repl_slash.py +0 -0
  77. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/review_agent.py +0 -0
  78. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/rules.py +0 -0
  79. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/session_runtime.py +0 -0
  80. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/session_store.py +0 -0
  81. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/session_summariser.py +0 -0
  82. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/skills.py +0 -0
  83. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/slash_commands.py +0 -0
  84. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/thinking.py +0 -0
  85. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tool_prompt_manifest.py +0 -0
  86. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tool_registry.py +0 -0
  87. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tool_result_store.py +0 -0
  88. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/__init__.py +0 -0
  89. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/automations_tools.py +0 -0
  90. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/bash.py +0 -0
  91. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/browser.py +0 -0
  92. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/compress_memory.py +0 -0
  93. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/curated_memory.py +0 -0
  94. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/edit.py +0 -0
  95. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/filesystem.py +0 -0
  96. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/notebook.py +0 -0
  97. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/notes.py +0 -0
  98. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/repo_map.py +0 -0
  99. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/search.py +0 -0
  100. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/shell.py +0 -0
  101. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/shell_gate.py +0 -0
  102. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/skills.py +0 -0
  103. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/tasks.py +0 -0
  104. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/think.py +0 -0
  105. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/todo.py +0 -0
  106. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/user_choice.py +0 -0
  107. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/veomem_tools.py +0 -0
  108. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/web.py +0 -0
  109. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/web_search.py +0 -0
  110. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools_inspector.py +0 -0
  111. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/trust.py +0 -0
  112. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tui/input_handler.py +0 -0
  113. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tui/spinner.py +0 -0
  114. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tui/welcome_banner.py +0 -0
  115. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tui/welcome_rich.py +0 -0
  116. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/veomem_bridge.py +0 -0
  117. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/version.py +0 -0
  118. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/vertex.py +0 -0
  119. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/wal.py +0 -0
  120. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/web/__init__.py +0 -0
  121. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/web/sse_adapter.py +0 -0
  122. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/web/terminal_repl.py +0 -0
  123. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/web/web_sse_compat.py +0 -0
  124. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/workspace_hints.py +0 -0
  125. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode.egg-info/SOURCES.txt +0 -0
  126. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode.egg-info/dependency_links.txt +0 -0
  127. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode.egg-info/entry_points.txt +0 -0
  128. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode.egg-info/requires.txt +0 -0
  129. {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode.egg-info/top_level.txt +0 -0
  130. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_add_dir.py +0 -0
  131. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_agent_instruction.py +0 -0
  132. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_autocompact.py +0 -0
  133. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_automations.py +0 -0
  134. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_capability_routing.py +0 -0
  135. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_checkpoint_diff_command.py +0 -0
  136. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_cli_init.py +0 -0
  137. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_compress_memory_tool.py +0 -0
  138. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_computer_use_permissions.py +0 -0
  139. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_context_budget.py +0 -0
  140. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_context_warning.py +0 -0
  141. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_credentials.py +0 -0
  142. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_eval_harness_layout.py +0 -0
  143. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_ide_stdio_attachments.py +0 -0
  144. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_interactive_permission_ask.py +0 -0
  145. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_kaira_scheduler.py +0 -0
  146. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_modality_tools.py +0 -0
  147. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_model_error_retry.py +0 -0
  148. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_model_errors.py +0 -0
  149. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_model_routing.py +0 -0
  150. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_multimodal_input.py +0 -0
  151. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_output_styles_and_rules.py +0 -0
  152. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_paths.py +0 -0
  153. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_permissions.py +0 -0
  154. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_prompt_suggestions.py +0 -0
  155. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_repl_commands.py +0 -0
  156. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_repl_slash.py +0 -0
  157. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_session_runtime_cache.py +0 -0
  158. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_skills.py +0 -0
  159. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_slash_commands.py +0 -0
  160. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_slash_completion_registry.py +0 -0
  161. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_thinking_config.py +0 -0
  162. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_token_budget.py +0 -0
  163. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_tool_context_circulation.py +0 -0
  164. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_tools.py +0 -0
  165. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_tools_inspector.py +0 -0
  166. {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_web_sse_adapter.py +0 -0
  167. {gemcode-0.3.114 → gemcode-0.3.115}/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.114
3
+ Version: 0.3.115
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.3.114"
7
+ version = "0.3.115"
8
8
  description = "Local-first coding agent on Google Gemini + ADK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -42,7 +42,14 @@ async def _maybe_enqueue_kaira_autopilot(*, cfg: "GemCodeConfig", session_id: st
42
42
  return
43
43
  object.__setattr__(cfg, "_kaira_autopilot_fp", fp)
44
44
 
45
- sock = os.environ.get("GEMCODE_KAIRA_SOCKET") or str(cfg.project_root / ".gemcode" / "ipc.sock")
45
+ try:
46
+ from gemcode.org import resolve_fleet_root
47
+
48
+ fleet_root = resolve_fleet_root(cfg.project_root)
49
+ except Exception:
50
+ fleet_root = cfg.project_root
51
+
52
+ sock = os.environ.get("GEMCODE_KAIRA_SOCKET") or str(fleet_root / ".gemcode" / "ipc.sock")
46
53
  if not Path(sock).exists():
47
54
  return
48
55
  # Heuristic: suggest likely checks, but let the agent choose based on repo.
@@ -70,7 +77,7 @@ async def _maybe_enqueue_kaira_autopilot(*, cfg: "GemCodeConfig", session_id: st
70
77
  )
71
78
  if auto_org:
72
79
  from gemcode.org import find_member
73
- m = find_member(cfg.project_root, "kaira")
80
+ m = find_member(fleet_root, "kaira")
74
81
  if m is not None and m.kind == "kaira_worker":
75
82
  from gemcode.tools.org_tools import make_org_tools
76
83
  tools = make_org_tools(cfg)
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import os
4
5
  from pathlib import Path
5
6
  from typing import Any
6
7
 
@@ -82,42 +83,60 @@ def make_org_tools(cfg: GemCodeConfig) -> list:
82
83
  ) -> None:
83
84
  if not _bus_enabled():
84
85
  return
85
- try:
86
- from gemcode.kaira_client import KairaIpcClient
87
- import os
86
+ fleet_root = resolve_fleet_root(getattr(cfg, "project_root", Path.cwd()))
88
87
 
89
- sock = os.environ.get("GEMCODE_KAIRA_SOCKET") or str(
90
- getattr(cfg, "project_root", Path.cwd()) / ".gemcode" / "ipc.sock"
91
- )
92
- # Only attempt publish if socket exists (avoid noisy failures).
88
+ def _audit_fallback(payload: dict[str, Any], *, why: str) -> None:
93
89
  try:
94
- if not Path(sock).exists():
95
- return
90
+ from gemcode.audit import append_audit
91
+
92
+ append_audit(
93
+ fleet_root,
94
+ {
95
+ "event": "org.report",
96
+ "why": why,
97
+ "payload": payload,
98
+ },
99
+ )
96
100
  except Exception:
97
- pass
101
+ return
102
+
103
+ from_addr = str(getattr(m, "address", "") or getattr(m, "name", "") or "")
104
+ member_dict = (m.to_dict() if hasattr(m, "to_dict") else {})
105
+ # Capabilities snapshot: keep it small and stable.
106
+ caps = {
107
+ "kind": member_dict.get("kind"),
108
+ "address": member_dict.get("address") or from_addr,
109
+ "workspace_rel": member_dict.get("workspace_rel", ""),
110
+ "reports_to": member_dict.get("reports_to", ""),
111
+ }
112
+ chain = _ancestor_addresses_for(m)
113
+ payload: dict[str, Any] = {
114
+ "member": member_dict,
115
+ "capabilities": caps,
116
+ "status": status,
117
+ "task": task,
118
+ "context": context,
119
+ "job_id": job_id,
120
+ "error": error,
121
+ "result": result,
122
+ "notify_chain": chain,
123
+ }
124
+
125
+ sock = os.environ.get("GEMCODE_KAIRA_SOCKET") or str(fleet_root / ".gemcode" / "ipc.sock")
126
+ # If runtime IPC isn't up, still persist the report locally.
127
+ try:
128
+ if not Path(sock).exists():
129
+ _audit_fallback(payload, why="ipc_socket_missing")
130
+ return
131
+ except Exception:
132
+ _audit_fallback(payload, why="ipc_socket_stat_failed")
133
+ return
134
+
135
+ try:
136
+ from gemcode.kaira_client import KairaIpcClient
137
+
98
138
  c = await KairaIpcClient.connect(socket_path=str(sock))
99
139
  try:
100
- from_addr = str(getattr(m, "address", "") or getattr(m, "name", "") or "")
101
- member_dict = (m.to_dict() if hasattr(m, "to_dict") else {})
102
- # Capabilities snapshot: keep it small and stable.
103
- caps = {
104
- "kind": member_dict.get("kind"),
105
- "address": member_dict.get("address") or from_addr,
106
- "workspace_rel": member_dict.get("workspace_rel", ""),
107
- "reports_to": member_dict.get("reports_to", ""),
108
- }
109
- chain = _ancestor_addresses_for(m)
110
- payload = {
111
- "member": member_dict,
112
- "capabilities": caps,
113
- "status": status,
114
- "task": task,
115
- "context": context,
116
- "job_id": job_id,
117
- "error": error,
118
- "result": result,
119
- "notify_chain": chain,
120
- }
121
140
  # Notify parent, grandparent, ... (and manager).
122
141
  for to_addr in chain:
123
142
  await c.publish(
@@ -128,7 +147,8 @@ def make_org_tools(cfg: GemCodeConfig) -> list:
128
147
  )
129
148
  finally:
130
149
  await c.close()
131
- except Exception:
150
+ except Exception as e:
151
+ _audit_fallback(payload, why=f"ipc_publish_failed: {type(e).__name__}: {e}")
132
152
  return
133
153
 
134
154
  def org_list() -> dict:
@@ -191,13 +211,12 @@ def make_org_tools(cfg: GemCodeConfig) -> list:
191
211
  prompt += "\n\nContext:\n" + ctx
192
212
 
193
213
  if m.kind == "kaira_worker":
194
- # Delegate to Kaira via IPC enqueue (background).
214
+ # Delegate to Kaira via IPC enqueue (background). If IPC is unavailable,
215
+ # fall back to an in-process subagent run so delegation still "just works".
195
216
  try:
196
217
  from gemcode.kaira_client import KairaIpcClient
197
- sock = (
198
- getattr(cfg, "project_root", Path.cwd()) / ".gemcode" / "ipc.sock"
199
- )
200
- sock_s = str(sock)
218
+ fleet_root = resolve_fleet_root(getattr(cfg, "project_root", Path.cwd()))
219
+ sock_s = os.environ.get("GEMCODE_KAIRA_SOCKET") or str(fleet_root / ".gemcode" / "ipc.sock")
201
220
  client = await KairaIpcClient.connect(socket_path=sock_s)
202
221
  try:
203
222
  session_id = str(getattr(cfg, "_active_session_id", "") or "")
@@ -229,14 +248,31 @@ def make_org_tools(cfg: GemCodeConfig) -> list:
229
248
  finally:
230
249
  await client.close()
231
250
  except Exception as e:
232
- await _publish_org_report(
233
- m=m,
234
- status="failed",
235
- task=task,
236
- context=ctx,
237
- error=f"kaira_ipc_unavailable: {type(e).__name__}: {e}",
238
- )
239
- return {"ok": False, "error": f"kaira_ipc_unavailable: {type(e).__name__}: {e}"}
251
+ # Fallback: run as a subagent right now (best-effort). This keeps UX
252
+ # consistent when users think "agent delegation" should always work.
253
+ try:
254
+ from gemcode.tools.subtask import make_run_subtask_tool
255
+
256
+ run_subtask = make_run_subtask_tool(cfg)
257
+ out = await run_subtask(prompt, "")
258
+ result = out.get("result") if isinstance(out, dict) else out
259
+ await _publish_org_report(
260
+ m=m,
261
+ status="finished",
262
+ task=task,
263
+ context=ctx,
264
+ result={"kind": "fallback_subagent", "error": f"kaira_ipc_unavailable: {type(e).__name__}: {e}", "result": result},
265
+ )
266
+ return {"ok": True, "delegated_to": m.to_dict(), "result": result, "fallback": "subagent"}
267
+ except Exception as e2:
268
+ await _publish_org_report(
269
+ m=m,
270
+ status="failed",
271
+ task=task,
272
+ context=ctx,
273
+ error=f"kaira_ipc_unavailable: {type(e).__name__}: {e}; fallback_subagent_failed: {type(e2).__name__}: {e2}",
274
+ )
275
+ return {"ok": False, "error": f"kaira_ipc_unavailable: {type(e).__name__}: {e}"}
240
276
 
241
277
  # Delegate to an in-process isolated subagent.
242
278
  try:
@@ -23,6 +23,9 @@ The sub-agent:
23
23
  from __future__ import annotations
24
24
 
25
25
  import asyncio
26
+ import os
27
+ from pathlib import Path
28
+ from typing import Any
26
29
  from gemcode.config import GemCodeConfig
27
30
 
28
31
 
@@ -70,6 +73,60 @@ def _build_sub_tools(cfg: GemCodeConfig) -> list:
70
73
  return tools
71
74
 
72
75
 
76
+ async def _publish_subtask_report(
77
+ *,
78
+ cfg: GemCodeConfig,
79
+ status: str,
80
+ task: str,
81
+ context: str,
82
+ sub_session_id: str,
83
+ result: object | None = None,
84
+ error: str = "",
85
+ ) -> None:
86
+ """
87
+ Durable report path for *all* in-process subagents:
88
+ - Publish to runtime bus when available
89
+ - Otherwise append to fleet-root `.gemcode/audit.log`
90
+ """
91
+ try:
92
+ from gemcode.org import resolve_fleet_root
93
+
94
+ fleet_root = resolve_fleet_root(getattr(cfg, "project_root", Path.cwd()))
95
+ except Exception:
96
+ fleet_root = Path(getattr(cfg, "project_root", Path.cwd()))
97
+
98
+ payload: dict[str, Any] = {
99
+ "kind": "subagent",
100
+ "status": status,
101
+ "task": task,
102
+ "context": context,
103
+ "sub_session_id": sub_session_id,
104
+ "error": error,
105
+ "result": result,
106
+ }
107
+
108
+ sock = os.environ.get("GEMCODE_KAIRA_SOCKET") or str(fleet_root / ".gemcode" / "ipc.sock")
109
+ try:
110
+ if Path(sock).exists():
111
+ from gemcode.kaira_client import KairaIpcClient
112
+
113
+ c = await KairaIpcClient.connect(socket_path=str(sock))
114
+ try:
115
+ await c.publish(topic="agent.report", to="manager", from_addr="subagent", payload=payload)
116
+ return
117
+ finally:
118
+ await c.close()
119
+ except Exception:
120
+ pass
121
+
122
+ try:
123
+ from gemcode.audit import append_audit
124
+
125
+ append_audit(fleet_root, {"event": "agent.report", "payload": payload})
126
+ except Exception:
127
+ return
128
+
129
+
73
130
  def make_run_subtask_tool(cfg: GemCodeConfig):
74
131
  async def run_subtask(task: str, context: str = "") -> dict:
75
132
  """
@@ -155,8 +212,15 @@ def make_run_subtask_tool(cfg: GemCodeConfig):
155
212
  sub_session_id = str(uuid.uuid4())
156
213
 
157
214
  # Compose the sub-agent prompt.
158
- task_clean = task.strip()
215
+ task_clean = (task or "").strip()
159
216
  ctx_clean = (context or "").strip()
217
+ await _publish_subtask_report(
218
+ cfg=cfg,
219
+ status="started",
220
+ task=task_clean,
221
+ context=ctx_clean,
222
+ sub_session_id=sub_session_id,
223
+ )
160
224
  prompt = task_clean
161
225
  if ctx_clean:
162
226
  prompt = f"{task_clean}\n\nAdditional context:\n{ctx_clean}"
@@ -188,6 +252,14 @@ def make_run_subtask_tool(cfg: GemCodeConfig):
188
252
  cfg=cfg,
189
253
  )
190
254
  except Exception as e:
255
+ await _publish_subtask_report(
256
+ cfg=cfg,
257
+ status="failed",
258
+ task=task_clean,
259
+ context=ctx_clean,
260
+ sub_session_id=sub_session_id,
261
+ error=f"{type(e).__name__}: {e}",
262
+ )
191
263
  return {"error": f"Sub-agent error: {type(e).__name__}: {e}"}
192
264
 
193
265
  # Extract only non-thinking text parts from the sub-agent's output.
@@ -226,6 +298,18 @@ def make_run_subtask_tool(cfg: GemCodeConfig):
226
298
  text=result_text,
227
299
  preview_max_chars=max_chars,
228
300
  )
301
+ await _publish_subtask_report(
302
+ cfg=cfg,
303
+ status="finished",
304
+ task=task_clean,
305
+ context=ctx_clean,
306
+ sub_session_id=sub_session_id,
307
+ result={
308
+ "offloaded": True,
309
+ "ref": ref_obj.get("ref"),
310
+ "preview": ref_obj.get("preview", "") or "",
311
+ },
312
+ )
229
313
  return {
230
314
  "result": ref_obj.get("preview", "") or "",
231
315
  "offloaded": True,
@@ -234,8 +318,24 @@ def make_run_subtask_tool(cfg: GemCodeConfig):
234
318
  }
235
319
  except Exception:
236
320
  result_text = result_text[:max_chars] + "\n… [truncated]"
321
+ await _publish_subtask_report(
322
+ cfg=cfg,
323
+ status="finished",
324
+ task=task_clean,
325
+ context=ctx_clean,
326
+ sub_session_id=sub_session_id,
327
+ result={"truncated": True, "result": result_text},
328
+ )
237
329
  return {"result": result_text, "truncated": True}
238
330
 
331
+ await _publish_subtask_report(
332
+ cfg=cfg,
333
+ status="finished",
334
+ task=task_clean,
335
+ context=ctx_clean,
336
+ sub_session_id=sub_session_id,
337
+ result={"result": result_text},
338
+ )
239
339
  return {"result": result_text}
240
340
 
241
341
  return run_subtask
@@ -446,6 +446,38 @@ async def run_gemcode_scrollback_tui(
446
446
  route = ""
447
447
  if from_addr or to:
448
448
  route = f" {from_addr or '?'}→{to or '*'}"
449
+
450
+ # Human-friendly rendering for common fleet-wide agent events.
451
+ try:
452
+ if topic in ("agent.report", "org.report") and isinstance(payload, dict):
453
+ status = str(payload.get("status") or "")
454
+ task = str(payload.get("task") or "").strip().replace("\n", " ")
455
+ task_short = (task[:160] + "…") if len(task) > 160 else task
456
+ if topic == "org.report":
457
+ mem = payload.get("member") or {}
458
+ mem_name = ""
459
+ try:
460
+ if isinstance(mem, dict):
461
+ mem_name = str(mem.get("name") or mem.get("id") or "")
462
+ except Exception:
463
+ mem_name = ""
464
+ jid = str(payload.get("job_id") or "")[:10]
465
+ prefix = f"{mem_name} " if mem_name else ""
466
+ extra = f" job={jid}" if jid else ""
467
+ await _kaira_print(
468
+ f"{ansi.dim}[{label}{route}]{ansi.reset} {prefix}{status}{extra} — {task_short}".rstrip()
469
+ )
470
+ continue
471
+ # agent.report
472
+ sid = str(payload.get("sub_session_id") or "")[:8]
473
+ extra = f" sub={sid}" if sid else ""
474
+ await _kaira_print(
475
+ f"{ansi.dim}[{label}{route}]{ansi.reset} {status}{extra} — {task_short}".rstrip()
476
+ )
477
+ continue
478
+ except Exception:
479
+ pass
480
+
449
481
  try:
450
482
  if isinstance(payload, (dict, list)):
451
483
  import json as _json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemcode
3
- Version: 0.3.114
3
+ Version: 0.3.115
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