fix(issue-10,11,13,15): complete agy purge, orchestrator purge, workspace-anchored registry
Issue #10 - agy purge_artifacts left resumable pieces behind: it now also removes the SQLite sidecars (-wal/-shm/-journal), the title annotation and the presence lock (the lock only once no live process references the conversation), and residual_artifacts() reports anything under ~/.gemini/antigravity-cli still carrying the uuid. Issue #13 - the orchestrator has no herdr_sessions row, so its conversation could not be purged by any skill. `lib_py.agents purge <agent|auto> <uuid>` resolves the adapter from what is on disk (orchestrator_uuids stores bare uuids), refuses while a live process names the uuid (conversation_in_use: process list minus the caller's own lineage, plus lsof), and prints what it purged. `orc_onboard.sh --remove <uuid> --purge-conversation --yes [--agent]` runs it before the registry update so a refusal changes nothing (exit 4 without --yes, 5 while in use). Issue #11 - "Successfully removeed": conjugate add/remove explicitly. Issue #15 - the job registry and audit log followed the caller's cwd, so the pytest suite (create_session.sh with its default onboarding job, run from the repo root) had left 593 test records in the real .mam/jobs. Defaults now resolve MAM_JOBS_DIR / DELEGATE_JOB_LOGS_DIR, then $WORKSPACE_ROOT/.mam/..., then the cwd (mqtt_common.default_registry_dir / get_logs_dir; registry.py, publish_event.py, service.py, create_session.sh, stop_session.sh); the sandbox fixture pins both. The tier suites that used to leak now leave the real registry untouched (881 records before and after). Tests: test_issue10_13_purge.py (13), test_issue15_registry_isolation.py (7); T-O13 pins the corrected message; the R1 helper test and B-9 logs-dir test now assert the workspace-anchored contract; publish_event snapshot updated. Refs #10 #11 #13 #15 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
This commit is contained in:
1 parent
bd3df890dc
commit
c82ea75a15
20 files changed
+723
-33
No files matched your search
@@ -1,11 +1,48 @@
|
||||
# __main__.py — CLI bridge for lib_py.agents facts and resolution (N4)
|
||||
|
||||
import sys, json, shlex
|
||||
import os, sys, json, shlex
|
||||
from lib_py.agents.registry import get_adapter, own_key, agent_of_row
|
||||
|
||||
# `purge auto` probes adapters in this order; opencode last because its
|
||||
# artifact path is one shared DB that exists for every uuid.
|
||||
PURGE_PROBE_ORDER = ('agy', 'claude', 'hermes', 'grok', 'codex', 'opencode')
|
||||
|
||||
|
||||
def _resolve_purge_target(agent_hint: str, uuid: str, workspace: str):
|
||||
"""(agent, adapter, ctx) whose on-disk artifact carries `uuid`, else None.
|
||||
|
||||
An explicit agent is trusted as long as its artifact exists. `auto` tries
|
||||
each adapter's own verification first (revalidate mode: an orchestrator
|
||||
conversation is not in any workspace cache), then a plain path check for
|
||||
adapters whose artifact path embeds the uuid.
|
||||
"""
|
||||
from lib_py.agents.base import DiscoveryContext
|
||||
names = [agent_hint] if agent_hint and agent_hint != 'auto' else list(PURGE_PROBE_ORDER)
|
||||
probes = []
|
||||
for name in names:
|
||||
adapter = get_adapter(name)
|
||||
if not adapter:
|
||||
continue
|
||||
probes.append((name, adapter, DiscoveryContext(workspace, name, mode="revalidate")))
|
||||
for name, adapter, ctx in probes:
|
||||
try:
|
||||
if adapter.verify_artifact(uuid, ctx):
|
||||
return name, adapter, ctx
|
||||
except Exception:
|
||||
pass
|
||||
for name, adapter, ctx in probes:
|
||||
try:
|
||||
path = adapter.artifact_path(uuid, ctx)
|
||||
except Exception:
|
||||
continue
|
||||
if uuid in path and os.path.exists(path):
|
||||
return name, adapter, ctx
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python -m lib_py.agents <facts|resolve> [args...]", file=sys.stderr)
|
||||
print("Usage: python -m lib_py.agents <facts|resolve|purge> [args...]", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
cmd = sys.argv[1]
|
||||
@@ -83,6 +120,31 @@ def main():
|
||||
print('/exit')
|
||||
sys.exit(0)
|
||||
|
||||
elif cmd == 'purge':
|
||||
# purge <agent|auto> <uuid> [workspace] (Issue #13)
|
||||
# Deletes a conversation's on-disk artifacts through its adapter and
|
||||
# prints one JSON object: {"agent", "purged", "residual", "in_use"}.
|
||||
# Exit 0 purged; 4 refused, conversation still in use (nothing
|
||||
# removed); 5 no adapter has an artifact for this uuid.
|
||||
agent_hint = sys.argv[2] if len(sys.argv) > 2 else 'auto'
|
||||
uuid = sys.argv[3] if len(sys.argv) > 3 else ''
|
||||
workspace = sys.argv[4] if len(sys.argv) > 4 and sys.argv[4] else os.getcwd()
|
||||
if not uuid:
|
||||
print("ERROR: purge requires <agent|auto> <uuid>", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
target = _resolve_purge_target(agent_hint, uuid, workspace)
|
||||
if not target:
|
||||
print(json.dumps({"agent": None, "purged": [], "residual": [], "in_use": False}))
|
||||
sys.exit(5)
|
||||
name, adapter, ctx = target
|
||||
if adapter.conversation_in_use(uuid, ctx):
|
||||
print(json.dumps({"agent": name, "purged": [], "residual": [], "in_use": True}))
|
||||
sys.exit(4)
|
||||
purged = adapter.purge_artifacts(uuid, ctx)
|
||||
residual = adapter.residual_artifacts(uuid, ctx)
|
||||
print(json.dumps({"agent": name, "purged": purged, "residual": residual, "in_use": False}))
|
||||
sys.exit(0)
|
||||
|
||||
else:
|
||||
print(f"ERROR: Unknown CLI command {cmd!r}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
Reference in new issue
Block a user