Fix plan command support in web bridge (#1018)

* fix: support plan command in web bridge

* fix: preserve queued bridge messages

* fix: avoid duplicate queued plan messages

* fix: preserve plan command semantics

---------

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
ekko
2026-05-25 15:48:17 +08:00
committed by GitHub
parent 6e2e502a75
commit 0eab6a1125
21 changed files with 622 additions and 49 deletions
@@ -1470,6 +1470,80 @@ class AgentPool:
with session.lock:
return {"session_id": session_id, "history": copy.deepcopy(session.history)}
def dispatch_command(self, session_id: str, command: str, profile: str | None = None) -> dict[str, Any]:
raw = str(command or "").strip()
if raw.startswith("/"):
raw = raw[1:].strip()
if not raw:
raise ValueError("command is required")
parts = raw.split(maxsplit=1)
name = parts[0].lstrip("/").strip().lower()
arg = parts[1] if len(parts) > 1 else ""
with _profile_env(profile):
try:
try:
from agent.skill_bundles import (
build_bundle_invocation_message,
resolve_bundle_command_key,
)
bundle_key = resolve_bundle_command_key(name)
if bundle_key:
bundle_result = build_bundle_invocation_message(
bundle_key,
arg,
task_id=session_id,
)
if bundle_result:
message, loaded_names, missing_names = bundle_result
return {
"session_id": session_id,
"command": name,
"handled": True,
"type": "bundle",
"message": message,
"loaded": loaded_names,
"missing": missing_names,
}
except ImportError:
pass
from agent.skill_commands import (
build_skill_invocation_message,
resolve_skill_command_key,
)
key = resolve_skill_command_key(name)
if key:
message = build_skill_invocation_message(
key,
arg,
task_id=session_id,
runtime_note=(
"If you need user clarification, call the clarify tool. "
"Do not output raw JSON question/choices payloads as the final response."
),
)
if message:
return {
"session_id": session_id,
"command": name,
"handled": True,
"type": "skill",
"message": message,
}
except Exception as exc:
raise RuntimeError(f"skill command dispatch failed: {exc}") from exc
return {
"session_id": session_id,
"command": name,
"handled": False,
"message": f"not a supported bridge command: /{name}",
}
def get_result(self, run_id: str) -> dict[str, Any]:
with self._lock:
record = self._runs.get(run_id)
@@ -1701,6 +1775,16 @@ class BridgeServer:
if action == "get_history":
return self.pool.get_history(str(req.get("session_id") or ""))
if action == "command":
session_id = str(req.get("session_id") or "").strip()
if not session_id:
raise ValueError("session_id is required")
return self.pool.dispatch_command(
session_id,
str(req.get("command") or ""),
req.get("profile"),
)
if action == "destroy":
return self.pool.destroy(str(req.get("session_id") or ""))
@@ -2275,7 +2359,7 @@ class BridgeBroker:
profile = self._profile_for_run(str(req.get("run_id") or ""))
return self._forward(profile, req)
if action in {"interrupt", "steer", "get_history", "destroy"}:
if action in {"interrupt", "steer", "command", "get_history", "destroy"}:
session_id = str(req.get("session_id") or "")
profile = self._profile_for_session(session_id, req.get("profile"))
resp = self._forward(profile, req)