feat(agent): hierarchical executor with path resolve, runtime probe, quiet UI
CI / build-gateway (push) Failing after 16s
CI / sync-config (push) Failing after 0s

Make Zed Agent closer to Cursor: deterministic DevOps path index, live Traefik
port probe before blind edits, stop-after-edit, and quieter Russian progress.
This commit is contained in:
2026-08-13 11:41:27 +03:00
parent 6fd2f0e689
commit a2d238d92e
71 changed files with 10617 additions and 449 deletions
+430
View File
@@ -0,0 +1,430 @@
"""Path resolve: deterministic index first, cheap find_* tools if gaps remain."""
from __future__ import annotations
import json
import logging
import re
from typing import Any
log = logging.getLogger("ai-router")
# Absolute Windows root for EventHubDevOps (Zed multi-root).
_DEFAULT_DEVOPS_ROOT = r"C:\Users\alexc\IdeaProjects\eventHub\EventHubDevOps"
# keyword groups → relative paths under DevOps root (first match wins per group).
# Order matters: more specific rules first.
_INDEX_RULES: list[tuple[tuple[str, ...], tuple[str, ...]]] = [
(
("loadtest", "load-test", "load_test"),
(r"ift\traefik\dynamic_conf.loadtest.yml",),
),
(
(
"traefik",
"dynamic_conf",
"router",
"middleware",
"calentiq",
"observer",
"host(",
"ift.calentiq",
"stage.calentiq",
),
(r"ift\traefik\dynamic_conf.yml",),
),
(
("portainer",),
(r"ift\docker-compose.portainer.yml",),
),
(
("grafana", "dashboard"),
(
r"ift\observability\grafana\provisioning\dashboards\dashboard.yml",
r"ift\observability\grafana\provisioning\datasources\prometheus.yml",
),
),
(
("prometheus", "observability"),
(r"ift\observability\prometheus.yml",),
),
(
("admin", "compose.admin", "docker-compose.admin"),
(r"ift\docker-compose.admin.yml",),
),
(
("client", "compose.client", "docker-compose.client"),
(r"ift\docker-compose.client.yml",),
),
(
("compose", "swarm", "stack", "docker-compose", "service"),
(r"ift\docker-compose.core.yml",),
),
]
def _join_under(root: str, rel: str) -> str:
root = root.rstrip("\\/")
rel = rel.replace("/", "\\").lstrip("\\")
return root + "\\" + rel
def _norm(path: str) -> str:
return path.replace("/", "\\").rstrip("\\").lower()
def _blob(plan: dict[str, Any] | None) -> str:
return json.dumps(plan or {}, ensure_ascii=False).lower()
def devops_root_from_plan(plan: dict[str, Any] | None) -> str:
blob = json.dumps(plan or {}, ensure_ascii=False)
m = re.search(r"([A-Za-z]:\\[^\"'\n\r]*?EventHubDevOps)", blob)
if m:
return m.group(1)
return _DEFAULT_DEVOPS_ROOT
def looks_devops(plan: dict[str, Any] | None) -> bool:
b = _blob(plan)
return any(
k in b
for k in (
"traefik",
"calentiq",
"eventhubdevops",
"docker-compose",
"swarm",
"observer",
"ift.",
"devops",
)
)
def match_index_paths(plan: dict[str, Any] | None) -> list[str]:
"""Return absolute paths from keyword index (may be empty)."""
if not looks_devops(plan):
return []
root = devops_root_from_plan(plan)
text = _blob(plan)
found: list[str] = []
seen: set[str] = set()
for keys, rels in _INDEX_RULES:
if not any(k in text for k in keys):
continue
for rel in rels:
abs_p = _join_under(root, rel)
nk = _norm(abs_p)
if nk in seen:
continue
seen.add(nk)
found.append(abs_p)
# one rule group is usually enough for a focused plan
if found:
break
return found
def apply_deterministic_path_index(
plan: dict[str, Any] | None,
) -> tuple[dict[str, Any] | None, list[str]]:
"""Fill plan.paths from index. Returns (plan, unresolved_queries).
unresolved_queries empty ⇒ index covered the goal; else cheap find_* needed.
"""
if not isinstance(plan, dict):
return plan, []
out = dict(plan)
tasks = out.get("subtasks")
if not isinstance(tasks, list) or not tasks:
# still try goal-level match
indexed = match_index_paths(out)
if indexed:
out["subtasks"] = [
{
"id": "1",
"prompt": str(out.get("user_goal") or "edit")[:200],
"worker_tier": "medium_code",
"paths": indexed,
"edit_goal": "",
"constraints": [],
}
]
out["path_resolve"] = "index"
return out, []
if looks_devops(out):
q = _queries_from_text(_blob(out))
out["path_resolve_queries"] = q
return out, q
return out, []
indexed = match_index_paths(out)
unresolved: list[str] = []
fixed: list[Any] = []
for item in tasks:
if not isinstance(item, dict):
fixed.append(item)
continue
it = dict(item)
paths = [
p
for p in (it.get("paths") or [])
if isinstance(p, str) and p.strip()
]
# Prefer index hits when devops; replace empty/weak paths
if indexed:
it["paths"] = indexed
it["path_source"] = "index"
elif not paths:
q = _queries_from_text(
str(it.get("prompt") or "")
+ " "
+ str(it.get("edit_goal") or "")
+ " "
+ _blob(out)
)
unresolved.extend(q)
it["paths"] = []
it["path_source"] = "missing"
else:
it["path_source"] = "planner"
fixed.append(it)
out["subtasks"] = fixed
if indexed:
out["path_resolve"] = "index"
out["path_resolve_queries"] = []
log.info("path_index hit → %s", indexed)
return out, []
# Planner had paths but index missed — if devops, still verify via find
if looks_devops(out) and not indexed:
unresolved = unresolved or _queries_from_text(_blob(out))
out["path_resolve_queries"] = unresolved
out["path_resolve"] = "needed"
return out, unresolved
out["path_resolve_queries"] = unresolved
if unresolved:
out["path_resolve"] = "needed"
return out, unresolved
def _queries_from_text(text: str) -> list[str]:
low = text.lower()
qs: list[str] = []
if any(k in low for k in ("traefik", "router", "calentiq", "observer", "dynamic")):
qs.append("dynamic_conf.yml")
if any(k in low for k in ("compose", "swarm", "docker")):
qs.append("docker-compose.core.yml")
if "portainer" in low:
qs.append("docker-compose.portainer.yml")
if "grafana" in low:
qs.append("grafana")
if "prometheus" in low:
qs.append("prometheus.yml")
if not qs:
qs.append("dynamic_conf.yml")
# unique preserve order
out: list[str] = []
for q in qs:
if q not in out:
out.append(q)
return out[:3]
def enrich_plan_from_discovery_tools(
plan: dict[str, Any] | None,
messages: list[dict[str, Any]] | None,
) -> dict[str, Any] | None:
"""Pull absolute paths from find_path / list_directory tool results into plan."""
if not isinstance(plan, dict):
return plan
# Never overwrite a good deterministic index hit with noisy find parses
if str(plan.get("path_resolve") or "") == "index":
return plan
tasks = plan.get("subtasks") or []
if (
isinstance(tasks, list)
and tasks
and isinstance(tasks[0], dict)
and str(tasks[0].get("path_source") or "") == "index"
):
return plan
found = _paths_from_discovery_messages(messages)
if not found:
return plan
out = dict(plan)
tasks = list(out.get("subtasks") or [])
if not tasks:
out["subtasks"] = [
{
"id": "1",
"prompt": "edit",
"worker_tier": "medium_code",
"paths": found[:3],
"path_source": "find",
}
]
else:
fixed = []
for item in tasks:
if not isinstance(item, dict):
fixed.append(item)
continue
it = dict(item)
it["paths"] = found[:3]
it["path_source"] = "find"
fixed.append(it)
out["subtasks"] = fixed
out["path_resolve"] = "find"
out["path_resolve_queries"] = []
log.info("path_resolve find → %s", found[:3])
return out
def _is_workspace_path(path: str) -> bool:
"""Accept only real EventHub project paths — never /etc or null:/etc artifacts."""
key = _norm(path)
if "eventhubdevops" not in key and "eventhub" not in key:
return False
if "\\etc\\" in key or key.startswith("etc\\"):
return False
if "\\nginx\\" in key:
return False
# Reject 1-letter drive artifacts from YAML like null:/etc → l:\etc
if re.match(r"^[a-z]:\\etc\\", key):
return False
base = key.rsplit("\\", 1)[-1]
if base in ("traefik.yml", "traefik.yaml", "docker-compose.yml"):
if "\\ift\\" not in key and "\\stage\\" not in key:
return False
return True
def _paths_from_discovery_messages(
messages: list[dict[str, Any]] | None,
) -> list[str]:
"""Extract existing file paths from find/list tool results only."""
call_ids: set[str] = set()
for msg in messages or []:
if not isinstance(msg, dict) or msg.get("role") != "assistant":
continue
for tc in msg.get("tool_calls") or []:
if not isinstance(tc, dict):
continue
name = str((tc.get("function") or {}).get("name") or "").lower()
if not any(k in name for k in ("find", "list", "search", "grep", "glob")):
continue
# Do not treat read_file / edit_file as discovery
if any(k in name for k in ("read", "edit", "write", "create", "delete")):
continue
cid = str(tc.get("id") or "")
if cid:
call_ids.add(cid)
# No discovery tool calls → do not scrape read_file bodies (YAML has /etc/… paths)
if not call_ids:
return []
paths: list[str] = []
seen: set[str] = set()
for msg in messages or []:
if not isinstance(msg, dict) or msg.get("role") != "tool":
continue
if str(msg.get("tool_call_id") or "") not in call_ids:
continue
content = str(msg.get("content") or "")
low = content.lower()
if "not found" in low and len(content) < 200:
continue
for m in re.finditer(
r"([A-Za-z]:[\\/][^\s\"'<>\]\n\r]+\.(?:yml|yaml|toml|md|json|conf))",
content,
):
p = m.group(1).replace("/", "\\")
if not _is_workspace_path(p):
continue
nk = _norm(p)
if nk in seen:
continue
seen.add(nk)
paths.append(p)
# Also accept plain relative hits that Zed returns under EventHubDevOps
for m in re.finditer(
r"((?:EventHubDevOps[\\/](?:ift|stage)[\\/][^\s\"'<>\]\n\r]+\.(?:yml|yaml)))",
content,
re.I,
):
p = _join_under(
_DEFAULT_DEVOPS_ROOT,
m.group(1).split("EventHubDevOps", 1)[-1].lstrip("\\/"),
)
if not _is_workspace_path(p):
continue
nk = _norm(p)
if nk in seen:
continue
seen.add(nk)
paths.append(p)
return paths
def discovery_tools_only(tools: list[Any], *, max_tools: int = 3) -> list[Any]:
prefer_keys = ("find_path", "find", "list_directory", "list_dir", "grep", "search", "glob")
ranked: list[Any] = []
for key in prefer_keys:
for t in tools or []:
if not isinstance(t, dict):
continue
fn = t.get("function") if isinstance(t.get("function"), dict) else {}
name = str(fn.get("name") or t.get("name") or "").lower()
if key in name and t not in ranked:
ranked.append(t)
if len(ranked) >= max_tools:
break
return ranked[:max_tools]
def pick_path_resolve_model(cfg: dict[str, Any]) -> str:
"""Cheap model for find_* only — allow a-simple (unlike edit executor)."""
return str(cfg.get("path_resolve_model") or "a-simple")
def path_resolve_needed(plan: dict[str, Any] | None) -> bool:
if not isinstance(plan, dict):
return False
qs = plan.get("path_resolve_queries") or []
if qs:
return True
return str(plan.get("path_resolve") or "") == "needed"
def force_find_path_kickstart(
*,
model: str,
tools: list[Any],
queries: list[str],
) -> dict[str, Any]:
"""Synthetic find_path so Zed searches instead of inventing read_file."""
from agent_hier import _synthetic_tool_completion
tool_name = "find_path"
for t in tools or []:
if not isinstance(t, dict):
continue
fn = t.get("function") if isinstance(t.get("function"), dict) else {}
name = str(fn.get("name") or "")
low = name.lower()
if "find" in low or "glob" in low or ("list" in low and "dir" in low):
tool_name = name
break
q = (queries[0] if queries else "dynamic_conf.yml").strip()
args: dict[str, Any]
if "list" in tool_name.lower():
args = {
"path": _join_under(_DEFAULT_DEVOPS_ROOT, r"ift\traefik"),
}
else:
args = {"query": q, "path": _DEFAULT_DEVOPS_ROOT}
# Zed variants
args["glob"] = f"**/{q}" if "." in q else f"**/*{q}*"
return _synthetic_tool_completion(
model=model, tool_name=tool_name, arguments=args
)