feat(multi-agent-mux): integrate cline agent support, fix sqlite3 naming collision, simplify delegation docs, and add SKILL_FEATURES.md
This commit is contained in:
@@ -282,7 +282,7 @@ mkdir -p "$STATE_DIR"
|
||||
# atomic_dump_yaml(flock + temp+rename) 로 같은 소스를 돌린다. atomic 래퍼에서는
|
||||
# 'actions' 가 없으면 SystemExit(0) 으로 쓰기를 건너뛴다 (불필요한 재포맷 방지).
|
||||
read -r -d '' RECON_SRC <<'PYEOF' || true
|
||||
import os, json, glob, subprocess, time
|
||||
import os, json, glob, subprocess, time, sqlite3
|
||||
from datetime import datetime, timezone
|
||||
import yaml
|
||||
|
||||
@@ -403,14 +403,28 @@ if tmux_confirmed:
|
||||
name = t['name']
|
||||
if name in yaml_session_names:
|
||||
continue
|
||||
if not (name.endswith('-creator-claude') or name.endswith('-creator-agy')):
|
||||
if name.endswith('-creator-claude'):
|
||||
agent = 'claude'
|
||||
elif name.endswith('-creator-agy'):
|
||||
agent = 'agy'
|
||||
elif name.endswith('-creator-hermes'):
|
||||
agent = 'hermes'
|
||||
elif name.endswith('-creator-cline'):
|
||||
agent = 'cline'
|
||||
else:
|
||||
continue
|
||||
srv = t.get('server', 'default')
|
||||
pm = pane_meta(name, srv)
|
||||
if not pm:
|
||||
continue
|
||||
agent = 'claude' if name.endswith('-creator-claude') else 'agy'
|
||||
cmd_full = 'claude --dangerously-skip-permissions' if agent == 'claude' else 'agy --dangerously-skip-permissions'
|
||||
if agent == 'claude':
|
||||
cmd_full = 'claude --dangerously-skip-permissions'
|
||||
elif agent == 'agy':
|
||||
cmd_full = 'agy --dangerously-skip-permissions'
|
||||
elif agent == 'hermes':
|
||||
cmd_full = 'hermes'
|
||||
elif agent == 'cline':
|
||||
cmd_full = 'cline -i'
|
||||
server_opt = f"-L {srv} " if srv != 'default' else ""
|
||||
entry = {
|
||||
'name': name,
|
||||
@@ -430,7 +444,7 @@ if tmux_confirmed:
|
||||
entry['tui'] = {'model': '(unknown — capture after first message)', 'provider': 'anthropic',
|
||||
'plan': '(unknown)', 'account': '(unknown)', 'version': '(unknown)'}
|
||||
entry['claude_session_id_own'] = None
|
||||
else:
|
||||
elif agent == 'agy':
|
||||
entry['child_pid'] = 0
|
||||
entry['agy_conversation_id_own'] = None
|
||||
entry['mcp_attachments'] = [
|
||||
@@ -440,6 +454,12 @@ if tmux_confirmed:
|
||||
'endpoint': 'https://stitch.googleapis.com/mcp'
|
||||
}
|
||||
]
|
||||
elif agent == 'hermes':
|
||||
entry['child_pid'] = 0
|
||||
entry['hermes_conversation_id_own'] = None
|
||||
elif agent == 'cline':
|
||||
entry['child_pid'] = 0
|
||||
entry['cline_conversation_id_own'] = None
|
||||
d.setdefault('tmux_sessions', []).append(entry)
|
||||
yaml_session_names.add(name)
|
||||
drifts.append({'class': 'B', 'name': name,
|
||||
@@ -505,6 +525,66 @@ for s in d.get('tmux_sessions', []):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# === drift C (hermes): hermes 새 session id materialize (per-row own id) ===
|
||||
for s in d.get('tmux_sessions', []):
|
||||
if not s.get('name', '').endswith('-creator-hermes'):
|
||||
continue
|
||||
if s.get('status') != 'running':
|
||||
continue
|
||||
if s.get('hermes_conversation_id_own'):
|
||||
continue
|
||||
cwd = (s.get('pane') or {}).get('cwd', '')
|
||||
if not cwd:
|
||||
continue
|
||||
hdb = f"{home}/.hermes/state.db"
|
||||
if os.path.exists(hdb):
|
||||
try:
|
||||
conn = sqlite3.connect(hdb)
|
||||
r = conn.execute("SELECT id FROM sessions WHERE cwd=? ORDER BY started_at DESC LIMIT 1", (cwd,)).fetchone()
|
||||
conn.close()
|
||||
if r:
|
||||
cid = r[0]
|
||||
s['hermes_conversation_id_own'] = cid
|
||||
drifts.append({'class': 'C', 'name': s['name'], 'msg': f"{s['name']}: conversation id materialized: {cid}"})
|
||||
actions.append(f"updated conversation id: {cid}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# === drift C (cline): cline 새 session id materialize (per-row own id) ===
|
||||
for s in d.get('tmux_sessions', []):
|
||||
if not s.get('name', '').endswith('-creator-cline'):
|
||||
continue
|
||||
if s.get('status') != 'running':
|
||||
continue
|
||||
if s.get('cline_conversation_id_own'):
|
||||
continue
|
||||
cwd = (s.get('pane') or {}).get('cwd', '')
|
||||
if not cwd:
|
||||
continue
|
||||
sessions_dir = f"{home}/.cline/data/sessions"
|
||||
if os.path.isdir(sessions_dir):
|
||||
candidates = []
|
||||
for session_folder in glob.glob(f"{sessions_dir}/*"):
|
||||
if os.path.isdir(session_folder):
|
||||
folder_name = os.path.basename(session_folder)
|
||||
json_file = f"{session_folder}/{folder_name}.json"
|
||||
if os.path.exists(json_file):
|
||||
candidates.append(json_file)
|
||||
candidates.sort(key=os.path.getmtime, reverse=True)
|
||||
for j in candidates:
|
||||
try:
|
||||
with open(j) as f:
|
||||
sdata = json.load(f)
|
||||
if sdata.get('cwd') == cwd or sdata.get('workspace_root') == cwd:
|
||||
cid = sdata.get('session_id')
|
||||
if cid:
|
||||
s['cline_conversation_id_own'] = cid
|
||||
drifts.append({'class': 'C', 'name': s['name'], 'msg': f"{s['name']}: session id materialized: {cid}"})
|
||||
actions.append(f"updated session id: {cid}")
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# === drift D: stale UUID (cache 의 artifact 가 사라짐) — 보고만, 변경 없음 ===
|
||||
ai = d.get('agent_identities', {}) or {}
|
||||
cl = (ai.get('claude') or {})
|
||||
@@ -519,6 +599,28 @@ if ag.get('conversation_id'):
|
||||
if not os.path.exists(f"{home}/.gemini/antigravity-cli/conversations/{cid}.db"):
|
||||
drifts.append({'class': 'D', 'name': '(agy identity cache)',
|
||||
'msg': f"stale UUID in agent_identities.agy.conversation_id: {cid} (.db missing)"})
|
||||
hr = (ai.get('hermes') or {})
|
||||
if hr.get('session_id'):
|
||||
sid = hr['session_id']
|
||||
hdb = f"{home}/.hermes/state.db"
|
||||
has_session = False
|
||||
if os.path.exists(hdb):
|
||||
try:
|
||||
conn = sqlite3.connect(hdb)
|
||||
r = conn.execute("SELECT 1 FROM sessions WHERE id=?", (sid,)).fetchone()
|
||||
conn.close()
|
||||
has_session = r is not None
|
||||
except Exception:
|
||||
pass
|
||||
if not has_session:
|
||||
drifts.append({'class': 'D', 'name': '(hermes identity cache)',
|
||||
'msg': f"stale UUID in agent_identities.hermes.session_id: {sid} (session missing from db)"})
|
||||
cn = (ai.get('cline') or {})
|
||||
if cn.get('session_id'):
|
||||
sid = cn['session_id']
|
||||
if not os.path.exists(f"{home}/.cline/data/sessions/{sid}/{sid}.json"):
|
||||
drifts.append({'class': 'D', 'name': '(cline identity cache)',
|
||||
'msg': f"stale UUID in agent_identities.cline.session_id: {sid} (session file missing)"})
|
||||
|
||||
result = {
|
||||
'timestamp': now_iso,
|
||||
|
||||
Reference in New Issue
Block a user