Compare commits
2 Commits
f457180777
...
6e3c866461
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e3c866461 | |||
| 7c8267240d |
@@ -305,6 +305,8 @@ def _validate(d):
|
|||||||
raise SystemExit(f"VALIDATE: tmux_sessions[{i}] not a mapping")
|
raise SystemExit(f"VALIDATE: tmux_sessions[{i}] not a mapping")
|
||||||
if not s.get('name') or not s.get('status'):
|
if not s.get('name') or not s.get('status'):
|
||||||
raise SystemExit(f"VALIDATE: tmux_sessions[{i}] missing name/status")
|
raise SystemExit(f"VALIDATE: tmux_sessions[{i}] missing name/status")
|
||||||
|
if s.get('role') is not None and (not isinstance(s['role'], str) or not s['role'].strip()):
|
||||||
|
raise SystemExit(f"VALIDATE: tmux_sessions[{i}] {s.get('name')!r} role must be a non-empty string")
|
||||||
if s['status'] not in valid:
|
if s['status'] not in valid:
|
||||||
raise SystemExit(f"VALIDATE: tmux_sessions[{i}] {s.get('name')!r} bad status {s['status']!r}")
|
raise SystemExit(f"VALIDATE: tmux_sessions[{i}] {s.get('name')!r} bad status {s['status']!r}")
|
||||||
if not isinstance(s.get('pane'), dict):
|
if not isinstance(s.get('pane'), dict):
|
||||||
@@ -366,10 +368,17 @@ try:
|
|||||||
d['tmux_sessions'] = []
|
d['tmux_sessions'] = []
|
||||||
|
|
||||||
old_terminals = get_terminal_set(d)
|
old_terminals = get_terminal_set(d)
|
||||||
|
old_roles = {s.get('name'): s.get('role') for s in db_sessions if s.get('role')}
|
||||||
|
|
||||||
# --- caller mutation (module scope: sees d, yaml, os, glob, subprocess) ---
|
# --- caller mutation (module scope: sees d, yaml, os, glob, subprocess) ---
|
||||||
exec(compile(os.environ['AGENT_SESSIONS_MUTATION'], '<mutation>', 'exec'), globals())
|
exec(compile(os.environ['AGENT_SESSIONS_MUTATION'], '<mutation>', 'exec'), globals())
|
||||||
|
|
||||||
|
# Role immutability check
|
||||||
|
for s in d.get('tmux_sessions', []):
|
||||||
|
name = s.get('name')
|
||||||
|
if name in old_roles and s.get('role') != old_roles[name]:
|
||||||
|
raise SystemExit(f"VALIDATE: role of session {name!r} cannot be modified from {old_roles[name]!r} to {s.get('role')!r}")
|
||||||
|
|
||||||
_validate(d)
|
_validate(d)
|
||||||
|
|
||||||
# Separate globals and sessions for normalization
|
# Separate globals and sessions for normalization
|
||||||
|
|||||||
@@ -74,12 +74,12 @@ To prevent this, you can run this skill inside an **isolated tmux server** using
|
|||||||
```
|
```
|
||||||
2. **Via Option Flag**:
|
2. **Via Option Flag**:
|
||||||
```bash
|
```bash
|
||||||
bash scripts/create_session.sh --workspace /path/to/project --agent claude --tmux-server multi-agent-canary
|
bash scripts/create_session.sh --workspace /path/to/project --agent claude --role developer --tmux-server multi-agent-canary
|
||||||
```
|
```
|
||||||
3. **Submit Job Integration**:
|
3. **Submit Job Integration**:
|
||||||
You can automatically register a delegated job with a prompt when creating a session:
|
You can automatically register a delegated job with a prompt when creating a session:
|
||||||
```bash
|
```bash
|
||||||
bash scripts/create_session.sh --workspace /path/to/project --agent claude --submit-job "Task prompt here"
|
bash scripts/create_session.sh --workspace /path/to/project --agent claude --role developer --submit-job "Task prompt here"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Recommended Alias
|
### Recommended Alias
|
||||||
@@ -173,7 +173,7 @@ Use the `agent-sessions-yaml-edit` script in `scripts/` to safely append (preser
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash .agents/skills/multi-agent-mux-create/scripts/create_session.sh \
|
bash .agents/skills/multi-agent-mux-create/scripts/create_session.sh \
|
||||||
--workspace "$WORKSPACE" --agent "$AGENT" --session "$SESSION_NAME"
|
--workspace "$WORKSPACE" --agent "$AGENT" --role "$ROLE" --session "$SESSION_NAME"
|
||||||
```
|
```
|
||||||
|
|
||||||
The script handles the YAML append, pane capture, and the `last_visible_status` placeholder.
|
The script handles the YAML append, pane capture, and the `last_visible_status` placeholder.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# create_session.sh — multi-agent-mux-create 의 부속 스크립트
|
# create_session.sh — multi-agent-mux-create 의 부속 스크립트
|
||||||
# Usage:
|
# Usage:
|
||||||
# bash create_session.sh --workspace <path> --agent <claude|agy> [--session <name>] [--wrapper]
|
# bash create_session.sh --workspace <path> --agent <claude|agy> --role <role> [--session <name>] [--wrapper]
|
||||||
#
|
#
|
||||||
# 동작:
|
# 동작:
|
||||||
# 1) preflight: tmux/claude/agy 가용성, workspace 존재
|
# 1) preflight: tmux/claude/agy 가용성, workspace 존재
|
||||||
@@ -23,11 +23,12 @@ source "$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)/lib.sh"
|
|||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: $0 --workspace <path> --agent <claude|agy|hermes|cline> [options]
|
Usage: $0 --workspace <path> --agent <claude|agy|hermes|cline> --role <role> [options]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--workspace PATH project directory (required)
|
--workspace PATH project directory (required)
|
||||||
--agent AGENT claude | agy | hermes | cline (required)
|
--agent AGENT claude | agy | hermes | cline (required)
|
||||||
|
--role ROLE assigned role (required)
|
||||||
--session NAME tmux session name (default: derived from workspace)
|
--session NAME tmux session name (default: derived from workspace)
|
||||||
--wrapper force use of ~/.local/bin/<session> wrapper even if not present
|
--wrapper force use of ~/.local/bin/<session> wrapper even if not present
|
||||||
--dry-run print commands without executing
|
--dry-run print commands without executing
|
||||||
@@ -39,6 +40,7 @@ EOF
|
|||||||
|
|
||||||
WORKSPACE=""
|
WORKSPACE=""
|
||||||
AGENT=""
|
AGENT=""
|
||||||
|
ROLE=""
|
||||||
SESSION_NAME=""
|
SESSION_NAME=""
|
||||||
USE_WRAPPER=0
|
USE_WRAPPER=0
|
||||||
DRY_RUN=0
|
DRY_RUN=0
|
||||||
@@ -49,6 +51,7 @@ while [ $# -gt 0 ]; do
|
|||||||
case "$1" in
|
case "$1" in
|
||||||
--workspace) WORKSPACE="$2"; shift 2 ;;
|
--workspace) WORKSPACE="$2"; shift 2 ;;
|
||||||
--agent) AGENT="$2"; shift 2 ;;
|
--agent) AGENT="$2"; shift 2 ;;
|
||||||
|
--role) ROLE="$2"; shift 2 ;;
|
||||||
--session) SESSION_NAME="$2"; shift 2 ;;
|
--session) SESSION_NAME="$2"; shift 2 ;;
|
||||||
--wrapper) USE_WRAPPER=1; shift ;;
|
--wrapper) USE_WRAPPER=1; shift ;;
|
||||||
--dry-run) DRY_RUN=1; shift ;;
|
--dry-run) DRY_RUN=1; shift ;;
|
||||||
@@ -66,6 +69,7 @@ fi
|
|||||||
# Preflight
|
# Preflight
|
||||||
[ -n "$WORKSPACE" ] || { echo "ERROR: --workspace required" >&2; usage; exit 2; }
|
[ -n "$WORKSPACE" ] || { echo "ERROR: --workspace required" >&2; usage; exit 2; }
|
||||||
[ -n "$AGENT" ] || { echo "ERROR: --agent required" >&2; usage; exit 2; }
|
[ -n "$AGENT" ] || { echo "ERROR: --agent required" >&2; usage; exit 2; }
|
||||||
|
[ -n "$ROLE" ] || { echo "ERROR: --role required" >&2; usage; exit 2; }
|
||||||
[ -d "$WORKSPACE" ] || { echo "ERROR: workspace $WORKSPACE not a directory" >&2; exit 1; }
|
[ -d "$WORKSPACE" ] || { echo "ERROR: workspace $WORKSPACE not a directory" >&2; exit 1; }
|
||||||
command -v tmux >/dev/null || { echo "ERROR: tmux not installed" >&2; exit 1; }
|
command -v tmux >/dev/null || { echo "ERROR: tmux not installed" >&2; exit 1; }
|
||||||
command -v "$AGENT" >/dev/null || { echo "ERROR: $AGENT CLI not in PATH" >&2; exit 1; }
|
command -v "$AGENT" >/dev/null || { echo "ERROR: $AGENT CLI not in PATH" >&2; exit 1; }
|
||||||
@@ -212,9 +216,10 @@ atomic_dump_yaml "$AGENT_SESSIONS_YAML" \
|
|||||||
TMUX_EPOCH="$TMUX_EPOCH" PANE_PID="$PANE_PID" PANE_CWD="$PANE_CWD" \
|
TMUX_EPOCH="$TMUX_EPOCH" PANE_PID="$PANE_PID" PANE_CWD="$PANE_CWD" \
|
||||||
CMD_FULL="$CMD_FULL" START_CMD="$START_CMD" CHILD_PID="$CHILD_PID" \
|
CMD_FULL="$CMD_FULL" START_CMD="$START_CMD" CHILD_PID="$CHILD_PID" \
|
||||||
TMUX_SERVER_NAME="${TMUX_SERVER_NAME:-default}" \
|
TMUX_SERVER_NAME="${TMUX_SERVER_NAME:-default}" \
|
||||||
DELEGATE_JOB_ID="$DELEGATE_JOB_ID" <<'PYEOF'
|
DELEGATE_JOB_ID="$DELEGATE_JOB_ID" ROLE="$ROLE" <<'PYEOF'
|
||||||
name = os.environ['SESSION_NAME']
|
name = os.environ['SESSION_NAME']
|
||||||
agent = os.environ['AGENT']
|
agent = os.environ['AGENT']
|
||||||
|
role = os.environ['ROLE']
|
||||||
pid = os.environ.get('PANE_PID', '')
|
pid = os.environ.get('PANE_PID', '')
|
||||||
epoch = os.environ.get('TMUX_EPOCH', '')
|
epoch = os.environ.get('TMUX_EPOCH', '')
|
||||||
server_name = os.environ.get('TMUX_SERVER_NAME', 'default')
|
server_name = os.environ.get('TMUX_SERVER_NAME', 'default')
|
||||||
@@ -233,6 +238,7 @@ sessions[:] = [s for s in sessions if s.get('name') != name]
|
|||||||
entry = {
|
entry = {
|
||||||
'name': name,
|
'name': name,
|
||||||
'status': 'running',
|
'status': 'running',
|
||||||
|
'role': role,
|
||||||
'tmux_session_created_at': os.environ['NOW_ISO'],
|
'tmux_session_created_at': os.environ['NOW_ISO'],
|
||||||
'tmux_session_epoch': int(epoch) if epoch.isdigit() else 0,
|
'tmux_session_epoch': int(epoch) if epoch.isdigit() else 0,
|
||||||
'tmux_server': server_name,
|
'tmux_server': server_name,
|
||||||
|
|||||||
+2
-1
@@ -44,7 +44,8 @@
|
|||||||
* `hermes`: `hermes status`를 통한 연동 상태 검증
|
* `hermes`: `hermes status`를 통한 연동 상태 검증
|
||||||
* `cline`: `cline history --json` 동작 및 설정 상태 사전 검증
|
* `cline`: `cline history --json` 동작 및 설정 상태 사전 검증
|
||||||
* **Tmux 세션 생성 및 초기화**: 에이전트별 최적화된 화면 크기(`-x 140 -y 40`) 및 작업 디렉터리(`-c`)를 적용해 세션 백그라운드 생성.
|
* **Tmux 세션 생성 및 초기화**: 에이전트별 최적화된 화면 크기(`-x 140 -y 40`) 및 작업 디렉터리(`-c`)를 적용해 세션 백그라운드 생성.
|
||||||
* **초기 상태 YAML 등록**: `status: running`, `pane` 세부정보(인덱스, PID, CWD, CMD_FULL), 시작 명령 및 `mcp_attachments` 기록.
|
* **초기 상태 YAML 등록**: 사용자 필수 지정 역할(`--role`), `status: running`, `pane` 세부정보(인덱스, PID, CWD, CMD_FULL), 시작 명령 및 `mcp_attachments` 기록.
|
||||||
|
* **역할 불변성 보장**: 에이전트 생성 시 부여된 역할(`role`)은 사후 수정이 불가하며, 임의 변경 시도 시 데이터 검증(`atomic_dump_yaml`) 단계에서 예외 처리되어 방어됨.
|
||||||
|
|
||||||
### 3.2. `multi-agent-mux-resume` (재개 스킬)
|
### 3.2. `multi-agent-mux-resume` (재개 스킬)
|
||||||
* **용도**: 중지되었거나 유실된 에이전트의 이전 컨텍스트 그대로 Tmux 세션 및 TUI 연결 복원.
|
* **용도**: 중지되었거나 유실된 에이전트의 이전 컨텍스트 그대로 Tmux 세션 및 TUI 연결 복원.
|
||||||
|
|||||||
Reference in New Issue
Block a user