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.
- {gemcode-0.3.114/src/gemcode.egg-info → gemcode-0.3.115}/PKG-INFO +1 -1
- {gemcode-0.3.114 → gemcode-0.3.115}/pyproject.toml +1 -1
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/invoke.py +9 -2
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/org_tools.py +81 -45
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/subtask.py +101 -1
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tui/scrollback.py +32 -0
- {gemcode-0.3.114 → gemcode-0.3.115/src/gemcode.egg-info}/PKG-INFO +1 -1
- {gemcode-0.3.114 → gemcode-0.3.115}/LICENSE +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/MANIFEST.in +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/README.md +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/setup.cfg +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/__init__.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/__main__.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/agent.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/audit.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/autocompact.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/automations.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/autotune.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/callbacks.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/capability_routing.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/checkpoints.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/cli.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/compaction.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/computer_use/__init__.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/computer_use/browser_computer.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/config.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/context_budget.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/context_warning.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/credentials.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/curated_memory.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/dynamic_policy.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/evals/harness.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/hitl_session.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/hooks.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/ide_protocol.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/ide_stdio.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/intent_classifier.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/interactions.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/kaira_client.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/kaira_daemon.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/kaira_ipc.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/kaira_job_store.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/learning.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/limits.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/live_audio_engine.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/logging_config.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/mcp_loader.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/memory/__init__.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/memory/embedding_memory_service.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/memory/file_memory_service.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/modality_tools.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/model_errors.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/model_routing.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/multimodal_input.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/openapi_loader.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/org.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/output_styles.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/paths.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/permissions.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/plugins/__init__.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/policy_profile.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/pricing.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/prompt_suggestions.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/query/__init__.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/query/config.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/query/deps.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/query/engine.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/query/stop_hooks.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/query/token_budget.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/query/transitions.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/query_sanitizer.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/refine.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/repl_commands.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/repl_slash.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/review_agent.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/rules.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/session_runtime.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/session_store.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/session_summariser.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/skills.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/slash_commands.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/thinking.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tool_prompt_manifest.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tool_registry.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tool_result_store.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/__init__.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/automations_tools.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/bash.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/browser.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/compress_memory.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/curated_memory.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/edit.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/filesystem.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/notebook.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/notes.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/repo_map.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/search.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/shell.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/shell_gate.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/skills.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/tasks.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/think.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/todo.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/user_choice.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/veomem_tools.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/web.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools/web_search.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tools_inspector.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/trust.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tui/input_handler.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tui/spinner.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tui/welcome_banner.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/tui/welcome_rich.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/veomem_bridge.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/version.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/vertex.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/wal.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/web/__init__.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/web/sse_adapter.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/web/terminal_repl.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/web/web_sse_compat.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode/workspace_hints.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode.egg-info/SOURCES.txt +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode.egg-info/dependency_links.txt +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode.egg-info/entry_points.txt +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode.egg-info/requires.txt +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/src/gemcode.egg-info/top_level.txt +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_add_dir.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_agent_instruction.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_autocompact.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_automations.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_capability_routing.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_checkpoint_diff_command.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_cli_init.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_compress_memory_tool.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_computer_use_permissions.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_context_budget.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_context_warning.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_credentials.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_eval_harness_layout.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_ide_stdio_attachments.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_interactive_permission_ask.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_kaira_scheduler.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_modality_tools.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_model_error_retry.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_model_errors.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_model_routing.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_multimodal_input.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_output_styles_and_rules.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_paths.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_permissions.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_prompt_suggestions.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_repl_commands.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_repl_slash.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_session_runtime_cache.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_skills.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_slash_commands.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_slash_completion_registry.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_thinking_config.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_token_budget.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_tool_context_circulation.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_tools.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_tools_inspector.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_web_sse_adapter.py +0 -0
- {gemcode-0.3.114 → gemcode-0.3.115}/tests/test_workspace_hints.py +0 -0
|
@@ -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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
95
|
-
|
|
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
|
-
|
|
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
|
-
|
|
198
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
|
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
|