Several sessions of one app: a session's working folder names its agent when that agent runs in the same app (MCP server and hooks), an i_am tool binds a session explicitly, the doctor checks second sessions against their app's install, and an agent that left the roster can still be uninstalled

This commit is contained in:
KeelrLeek committed 2026-10-06 10:06:01 -05:00
1 parent d755ab8a76
commit e283e4cc54
6 files changed
+63 -5

No files matched your search

+13 -2
View File
@@ -208,14 +208,25 @@ def run(project, harbor, port=None, server_known=False):
checks.append(server_check(project, port, server_known))
links = harbor.connections()
servers = running_mcp_servers()
installed = {} # app -> the agent its Harbor install is for (the first one)
for spec in project.agents:
if spec["kind"] != "human" and spec.get("host"):
installed.setdefault(spec["host"], spec["name"])
for spec in project.agents:
if spec["kind"] == "human":
continue
host = spec.get("host")
as_agent = installed.get(host, spec["name"])
if host in HOST_CHECKS:
checks += HOST_CHECKS[host](spec["name"])
checks += HOST_CHECKS[host](as_agent)
elif host == "kimi":
checks += kimi_checks(spec["name"], project)
checks += kimi_checks(as_agent, project)
if as_agent != spec["name"]: # a second session of the same app: its folder names it
ok = bool(spec.get("workdir")) and project.identity_for(spec["workdir"]) == spec["name"]
checks.append(_check(spec["name"], "session identity", "ok" if ok else "fail",
f"shares the {host} install with {as_agent}; its folder {spec.get('workdir') or '?'} "
+ ("names it in [identities]" if ok else "is not in [identities]"),
"" if ok else "map its worktree folder to it in [identities], or have it call i_am"))
seen = {(r["channel"], r["host"]): r for r in links.get(spec["name"], [])}
live = ", ".join(f"{ch}{('/' + h) if h else ''} {r['minutes']} min ago" for (ch, h), r in sorted(seen.items()))
running = servers.get(spec["name"], 0)
+1 -1
View File
@@ -213,7 +213,7 @@ def run(event, project_spec, agent, host="generic"):
if project_spec or os.environ.get("HARBOR_PROJECT"):
raise
return 0 # not a Harbor project: stay out of the way
agent = agent or os.environ.get("HARBOR_AGENT") or project.identity_for(cwd)
agent = project.session_identity(agent or os.environ.get("HARBOR_AGENT"), cwd) # the session's own folder
if not agent or agent not in project.ai_names:
return 0
harbor = Harbor(project, busy_ms=1500, write_attempts=2)
+1 -1
View File
@@ -380,7 +380,7 @@ def install(host, agent=None, project=None, dry_run=False, remove=False, python=
p = projects.resolve()
except projects.ProjectError:
p = None
if p and agent not in p.ai_names:
if p and agent not in p.ai_names and not remove: # an agent that left the roster can still be uninstalled
raise InstallError(f"{agent} is not an agent of project {p.name} ({', '.join(p.ai_names)})")
cmd = launcher(python)
plan = Plan(host, dry_run)
+24 -1
View File
@@ -67,7 +67,9 @@ def harbor():
def me():
h = harbor()
agent = os.environ.get("HARBOR_AGENT") or h.project.identity_for(os.getcwd()) or ""
# a session bound with i_am, else the agent its working folder names (when it runs in the same app as the
# installed one: Codex and Codex2 share the Codex app), else the installed one
agent = _state.get("bound") or h.project.session_identity(os.environ.get("HARBOR_AGENT"), os.getcwd()) or ""
if agent not in h.project.ai_names:
raise HarborError(f"HARBOR_AGENT must be one of {', '.join(h.project.ai_names)} (set it in this server's MCP "
"config)")
@@ -121,6 +123,27 @@ def run(fn):
raise ToolError(str(error)) from None
@mcp.tool
def i_am(agent: str) -> dict:
"""Say which of the project's agents this session is, when one app runs several (Codex and Codex2). Call it once
at the start of the session if your instructions or your working folder name you; only an agent of the same
app is accepted, and a server already bound to another agent refuses."""
def do(h):
pinned = os.environ.get("HARBOR_AGENT") or ""
if agent not in h.project.ai_names:
raise HarborError(f"{agent} is not one of {', '.join(h.project.ai_names)}")
if pinned and h.project.host_of(agent) != h.project.host_of(pinned):
raise HarborError(f"{agent} runs in another app ({h.project.host_of(agent) or 'unknown'}), not this one")
bound = _state.get("bound")
if bound and bound != agent:
raise HarborError(f"this Harbor server is already {bound}: the app shares one server between sessions, "
"so give each session its own app profile instead")
_state["bound"] = agent
return {"you_are": agent, "working_folder": os.getcwd(),
"folder_says": h.project.identity_for(os.getcwd())}
return run(do)
@mcp.tool
def brief(mark_read: bool = False) -> str:
"""What you need at the start of a session: stop flags, unread messages (urgent first), acknowledgements you
+12
View File
@@ -121,6 +121,18 @@ class Project:
return agent
return None
def host_of(self, agent):
return next((a["host"] for a in self.agents if a["name"] == agent), "")
def session_identity(self, pinned, cwd):
"""Who a session is when one app runs several of the project's agents (Codex and Codex2): the agent its
working folder names in [identities], if that agent runs in the same app as the installed (pinned) one;
otherwise the pinned agent. Without a pinned agent, the folder alone decides."""
found = self.identity_for(cwd) if cwd else None
if found and found in self.ai_names and (not pinned or self.host_of(found) == self.host_of(pinned)):
return found
return pinned or found
def threshold(self, key, default):
return int(self.attention.get(key, default))
+12
View File
@@ -340,6 +340,18 @@ class Projects(unittest.TestCase):
with self.assertRaises(projects.ProjectError):
projects.project_from_dict(bad, home=self.dir)
def test_session_identity_for_two_sessions_of_one_app(self):
made = projects.project_from_dict({
"name": "pair", "identities": {"wt-codex": "Codex", "wt-codex2": "Codex2", "wt-deepseek": "DeepSeek"},
"agents": [{"name": "Codex", "host": "codex"}, {"name": "Codex2", "host": "codex"},
{"name": "DeepSeek", "host": "deepseek"}, {"name": "User", "kind": "human"}]}, home=self.dir)
two = self.dir / "wt-codex2" / "src"
two.mkdir(parents=True)
self.assertEqual(made.session_identity("Codex", two), "Codex2") # same app: the folder decides
self.assertEqual(made.session_identity("Codex", self.dir), "Codex") # no folder match: installed one
self.assertEqual(made.session_identity("DeepSeek", two), "DeepSeek") # another app's folder: ignored
self.assertEqual(made.session_identity(None, two), "Codex2") # nothing pinned: the folder
def test_resolution_order_and_identities(self):
made = make_home(self.dir, names=("first", "second"))
self.assertEqual(projects.resolve(home=self.dir, cwd=self.dir).name, "first") # home default