fix(herdr): resolve shim routing defects, add workspace scoping, and bump to v3.0.1
- Resolve Herdr shim 5 routing & paste defects (ISSUE-1 ~ ISSUE-5):
* paste-buffer: use pane send-text without auto-enter, propagate rc=3 to send_keys_safe
* exact-match pane resolution: remove substring matching ('in tn') across all branches
* workspace scoping: introduce HERDR_WORKSPACE_ID and .mam/herdr_workspace_id persistence
* unified resolver: single _resolve_herdr_pane_id helper across shim commands
- Resolve dialog token false-positive on 'Yes, try it' tip and isolate fullscreen modal rejection
- Sync mock Herdr CLI contracts in tests/conftest.py
- Add contract tests H-15~H-23 and regression tests D-4~D-7 (412 tests, 100% PASS)
- Add multi-agent loop plans, review reports, and bug report
- Update framework and skill packages to v3.0.1
This commit is contained in:
+239
-50
@@ -59,7 +59,7 @@ for dir in /home/linuxbrew/.linuxbrew/bin /home/linuxbrew/.linuxbrew/sbin "$HOME
|
||||
done
|
||||
|
||||
# Central TUI dialog and readiness validation tokens (OP-6)
|
||||
_MAM_DIALOG_TOKENS='Do you trust the files|Yes, proceed|No, exit|Allow this|Press Enter to continue|browser to authenticate|Use arrow keys|Esc to cancel|Resuming the full session|Resume from summary|Yes, try it'
|
||||
_MAM_DIALOG_TOKENS='Do you trust the files|Yes, proceed|No, exit|Allow this|Press Enter to continue|browser to authenticate|Use arrow keys|Esc to cancel|Resuming the full session|Resume from summary'
|
||||
_MAM_READY_TOKENS_CLAUDE='Anthropic|Assistant|Chat|Welcome|Claude Code|Opus|Sonnet|Haiku'
|
||||
|
||||
# Workspace-relative defaults with environment overrides (Phase Z)
|
||||
@@ -235,6 +235,152 @@ _sanitize_herdr_agent_name() {
|
||||
fi
|
||||
printf '%s\n' "${s:0:32}"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Workspace scoping (ISSUE-3).
|
||||
#
|
||||
# Hard filter when HERDR_WORKSPACE_ID is set, else the workspace id this
|
||||
# shim last persisted under $WORKSPACE_ROOT/.mam/herdr_workspace_id.
|
||||
# Do not infer from cwd — that silently blocks legitimate cross-workspace
|
||||
# lookups. No env and no file keeps the previous server-global lookup.
|
||||
# ---------------------------------------------------------------------------
|
||||
_herdr_ws_id_file() {
|
||||
if [ -n "${WORKSPACE_ROOT:-}" ]; then
|
||||
printf '%s\n' "$WORKSPACE_ROOT/.mam/herdr_workspace_id"
|
||||
fi
|
||||
}
|
||||
|
||||
_herdr_persist_ws_id() {
|
||||
local id="$1" f
|
||||
[ -n "$id" ] || return 0
|
||||
f=$(_herdr_ws_id_file)
|
||||
[ -n "$f" ] || return 0
|
||||
mkdir -p "$(dirname "$f")" 2>/dev/null || true
|
||||
printf '%s\n' "$id" > "$f" 2>/dev/null || true
|
||||
}
|
||||
|
||||
_herdr_ws_scope() {
|
||||
if [ -n "${HERDR_WORKSPACE_ID:-}" ]; then
|
||||
printf '%s\n' "$HERDR_WORKSPACE_ID"
|
||||
return 0
|
||||
fi
|
||||
local f id=""
|
||||
f=$(_herdr_ws_id_file)
|
||||
if [ -n "$f" ] && [ -f "$f" ]; then
|
||||
id=$(tr -d '[:space:]' < "$f" 2>/dev/null || true)
|
||||
fi
|
||||
printf '%s\n' "$id"
|
||||
}
|
||||
|
||||
# _herdr_agent_get_scoped <name> [workspace_id]
|
||||
#
|
||||
# agent get with the same workspace predicate as _resolve_herdr_pane_id.
|
||||
# Prints pane_id on stdout and returns 0 when the agent exists and is in
|
||||
# scope. Returns 1 (prints nothing) if missing or in another workspace.
|
||||
# Callers MUST wrap with `|| true`.
|
||||
_herdr_agent_get_scoped() {
|
||||
local name="$1"
|
||||
local target_ws pid=""
|
||||
# Default scope is the explicit env var only. The persisted workspace
|
||||
# file is NOT used here: one MAM workspace can own several herdr
|
||||
# workspaces (layout overflow / parallel create), so a single file
|
||||
# must not reject a uniquely named agent. File scope applies to pane
|
||||
# list / label lookup via _herdr_ws_scope.
|
||||
if [ "$#" -ge 2 ]; then
|
||||
target_ws="$2"
|
||||
else
|
||||
target_ws="${HERDR_WORKSPACE_ID:-}"
|
||||
fi
|
||||
[ -n "$name" ] || return 1
|
||||
pid=$(_real_herdr agent get "$name" 2>/dev/null | TARGET_WS="$target_ws" python3 -c "
|
||||
import sys, json, os
|
||||
tws = os.environ.get('TARGET_WS', '')
|
||||
try:
|
||||
a = json.load(sys.stdin).get('result', {}).get('agent', {})
|
||||
if tws and a.get('workspace_id') and a.get('workspace_id') != tws:
|
||||
sys.exit(1)
|
||||
pid = a.get('pane_id') or ''
|
||||
if pid:
|
||||
print(pid)
|
||||
sys.exit(0)
|
||||
except Exception:
|
||||
pass
|
||||
sys.exit(1)
|
||||
" 2>/dev/null || echo "")
|
||||
if [ -n "$pid" ]; then
|
||||
printf '%s\n' "$pid"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# _resolve_herdr_pane_id <target> [workspace_id]
|
||||
#
|
||||
# Resolve a session name / label to a real pane_id ("wN:pM").
|
||||
# Strict order (no substring matching at any step):
|
||||
# 1. herdr agent get <sanitized_name> (workspace-scoped)
|
||||
# 2. herdr agent get <raw_name> (workspace-scoped)
|
||||
# 3. herdr pane list [--workspace WS] matching
|
||||
# 3-a. label exact
|
||||
# 3-b. name exact
|
||||
# 3-c. agent exact
|
||||
# On success print pane_id on stdout and return 0; on failure print nothing
|
||||
# and return 1. Callers MUST wrap with `|| true` (shim runs under set -e).
|
||||
_resolve_herdr_pane_id() {
|
||||
local target="$1"
|
||||
local target_ws="${2:-$(_herdr_ws_scope)}"
|
||||
local sat pid=""
|
||||
sat=$(_sanitize_herdr_agent_name "$target")
|
||||
|
||||
local cand get_ws
|
||||
if [ "$#" -ge 2 ]; then
|
||||
get_ws="$target_ws"
|
||||
else
|
||||
get_ws="${HERDR_WORKSPACE_ID:-}"
|
||||
fi
|
||||
for cand in "$sat" "$target"; do
|
||||
[ -n "$cand" ] || continue
|
||||
pid=$(_herdr_agent_get_scoped "$cand" "$get_ws" 2>/dev/null || true)
|
||||
[ -n "$pid" ] && break
|
||||
done
|
||||
|
||||
if [ -z "$pid" ]; then
|
||||
local ws_flag=()
|
||||
[ -n "$target_ws" ] && ws_flag=(--workspace "$target_ws")
|
||||
pid=$(_real_herdr pane list "${ws_flag[@]+"${ws_flag[@]}"}" 2>/dev/null \
|
||||
| TARGET_NAME="$target" TARGET_SAN="$sat" TARGET_WS="$target_ws" python3 -c "
|
||||
import sys, json, os
|
||||
tn = os.environ.get('TARGET_NAME', '')
|
||||
tsa = os.environ.get('TARGET_SAN', '')
|
||||
tws = os.environ.get('TARGET_WS', '')
|
||||
try:
|
||||
panes = json.load(sys.stdin).get('result', {}).get('panes', [])
|
||||
# Client-side filter in case an older herdr ignores --workspace.
|
||||
if tws:
|
||||
panes = [p for p in panes if p.get('workspace_id') == tws]
|
||||
# ISSUE-2: exact match only.
|
||||
for key in ('label', 'name', 'agent'):
|
||||
for p in panes:
|
||||
v = p.get(key)
|
||||
if v and (v == tn or v == tsa):
|
||||
pid = p.get('pane_id') or ''
|
||||
if pid:
|
||||
print(pid)
|
||||
sys.exit(0)
|
||||
except Exception:
|
||||
pass
|
||||
sys.exit(1)
|
||||
" 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
# Real pane_id values look like 'w1E:p1' — the workspace segment is
|
||||
# alphanumeric. A digits-only pattern would reject every live pane_id.
|
||||
if [[ "$pid" =~ ^w[A-Za-z0-9]+:p[A-Za-z0-9]+$ ]]; then
|
||||
printf '%s\n' "$pid"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
cmd="${1:-}"
|
||||
if [ -z "$cmd" ]; then
|
||||
echo "herdr shim: no command specified" >&2
|
||||
@@ -250,26 +396,29 @@ case "$cmd" in
|
||||
local raw="$1"
|
||||
local sat
|
||||
sat=$(_sanitize_herdr_agent_name "$raw")
|
||||
if _real_herdr agent get "$sat" >/dev/null 2>&1; then
|
||||
if [ -n "$(_herdr_agent_get_scoped "$sat" 2>/dev/null || true)" ]; then
|
||||
echo "$sat"
|
||||
return 0
|
||||
fi
|
||||
if _real_herdr agent get "$raw" >/dev/null 2>&1; then
|
||||
if [ "$raw" != "$sat" ] && [ -n "$(_herdr_agent_get_scoped "$raw" 2>/dev/null || true)" ]; then
|
||||
echo "$raw"
|
||||
return 0
|
||||
fi
|
||||
local from_list
|
||||
from_list=$(_real_herdr agent list 2>/dev/null | TARGET_NAME="$raw" python3 -c '
|
||||
from_list=$(_real_herdr agent list 2>/dev/null | TARGET_NAME="$raw" TARGET_SAN="$sat" TARGET_WS="$(_herdr_ws_scope)" python3 -c '
|
||||
import sys, json, os
|
||||
tn = os.environ.get("TARGET_NAME", "")
|
||||
tn = os.environ.get("TARGET_NAME", "")
|
||||
tsa = os.environ.get("TARGET_SAN", "")
|
||||
tws = os.environ.get("TARGET_WS", "")
|
||||
try:
|
||||
d = json.loads(sys.stdin.read())
|
||||
for a in d.get("result", {}).get("agents", []):
|
||||
agents = d.get("result", {}).get("agents", [])
|
||||
if tws:
|
||||
agents = [a for a in agents if a.get("workspace_id") == tws]
|
||||
for a in agents:
|
||||
name = a.get("name", "")
|
||||
agent = a.get("agent", "")
|
||||
pane_id = a.get("pane_id", "")
|
||||
if name == tn or (not name and agent and agent in tn):
|
||||
print(pane_id or agent)
|
||||
if name and (name == tn or name == tsa):
|
||||
print(a.get("pane_id") or name)
|
||||
sys.exit(0)
|
||||
except Exception:
|
||||
pass
|
||||
@@ -279,6 +428,12 @@ sys.exit(1)
|
||||
echo "$from_list"
|
||||
return 0
|
||||
fi
|
||||
# Explicit-env miss: do not fall through to a name that agent prompt
|
||||
# would deliver into another workspace (ISSUE-3 / F-2b). File-only
|
||||
# scope does not trip this — see _herdr_agent_get_scoped.
|
||||
if [ -n "${HERDR_WORKSPACE_ID:-}" ]; then
|
||||
return 1
|
||||
fi
|
||||
echo "$raw"
|
||||
}
|
||||
|
||||
@@ -286,17 +441,29 @@ sys.exit(1)
|
||||
t="$1"
|
||||
txt="$2"
|
||||
shift 2
|
||||
tgt=$(_resolve_herdr_target "$t")
|
||||
tgt=$(_resolve_herdr_target "$t" || true)
|
||||
if [ -z "$tgt" ]; then
|
||||
echo "Error: no in-scope herdr agent for '$t'" >&2
|
||||
exit 1
|
||||
fi
|
||||
_real_herdr agent prompt "$tgt" "$txt" "$@"
|
||||
elif [ "$sub" = "get" ] && [ $# -ge 1 ]; then
|
||||
t="$1"
|
||||
shift
|
||||
tgt=$(_resolve_herdr_target "$t")
|
||||
tgt=$(_resolve_herdr_target "$t" || true)
|
||||
if [ -z "$tgt" ]; then
|
||||
echo "Error: no in-scope herdr agent for '$t'" >&2
|
||||
exit 1
|
||||
fi
|
||||
_real_herdr agent get "$tgt" "$@"
|
||||
elif [ "$sub" = "read" ] && [ $# -ge 1 ]; then
|
||||
t="$1"
|
||||
shift
|
||||
tgt=$(_resolve_herdr_target "$t")
|
||||
tgt=$(_resolve_herdr_target "$t" || true)
|
||||
if [ -z "$tgt" ]; then
|
||||
echo "Error: no in-scope herdr agent for '$t'" >&2
|
||||
exit 1
|
||||
fi
|
||||
_real_herdr agent read "$tgt" "$@"
|
||||
else
|
||||
_real_herdr agent "$sub" "$@"
|
||||
@@ -317,10 +484,11 @@ sys.exit(1)
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
if _real_herdr agent get "$(_sanitize_herdr_agent_name "$sess")" >/dev/null 2>&1 || _real_herdr agent get "$sess" >/dev/null 2>&1; then
|
||||
if [ -n "$(_herdr_agent_get_scoped "$(_sanitize_herdr_agent_name "$sess")" 2>/dev/null || true)" ] \
|
||||
|| [ -n "$(_herdr_agent_get_scoped "$sess" 2>/dev/null || true)" ]; then
|
||||
exit 0
|
||||
fi
|
||||
if _real_herdr agent list 2>/dev/null | TARGET_NAME="$sess" python3 -c '
|
||||
if _real_herdr agent list 2>/dev/null | TARGET_NAME="$sess" TARGET_WS="$(_herdr_ws_scope)" python3 -c '
|
||||
import sys, json, os
|
||||
try:
|
||||
sys.path.insert(0, os.path.abspath(".agents/skills"))
|
||||
@@ -329,12 +497,15 @@ except Exception:
|
||||
sanitize_herdr_agent_name = lambda s: s.lower()[:32] if s else "agent"
|
||||
tn = os.environ.get("TARGET_NAME", "")
|
||||
stn = sanitize_herdr_agent_name(tn)
|
||||
tws = os.environ.get("TARGET_WS", "")
|
||||
try:
|
||||
d = json.loads(sys.stdin.read())
|
||||
agents = d.get("result", {}).get("agents", [])
|
||||
if tws:
|
||||
agents = [a for a in agents if a.get("workspace_id") == tws]
|
||||
for a in agents:
|
||||
an = a.get("name", "")
|
||||
if (an and (an == tn or an == stn)) or (not an and a.get("agent") and a.get("agent") in tn):
|
||||
if an and (an == tn or an == stn):
|
||||
sys.exit(0)
|
||||
except Exception:
|
||||
pass
|
||||
@@ -342,6 +513,9 @@ sys.exit(1)
|
||||
'; then
|
||||
exit 0
|
||||
fi
|
||||
if [ -n "$(_resolve_herdr_pane_id "$sess" 2>/dev/null || true)" ]; then
|
||||
exit 0
|
||||
fi
|
||||
exit 1
|
||||
;;
|
||||
new-session)
|
||||
@@ -478,7 +652,7 @@ except Exception:
|
||||
fi
|
||||
|
||||
if [ "$split_dir" = "right" ] || [ "$split_dir" = "down" ]; then
|
||||
split_json=$(_real_herdr pane split --pane "$sample_pane" --direction "$split_dir" --cwd "${ws:-.}" $env_flags --no-focus 2>/dev/null || echo "")
|
||||
split_json=$(_real_herdr pane split --pane "$sample_pane" --direction "$split_dir" --cwd "${ws:-.}" $env_flags --env HERDR_WORKSPACE_ID="$existing_ws" --no-focus 2>/dev/null || echo "")
|
||||
target_pane=$(echo "$split_json" | python3 -c "
|
||||
import sys, json
|
||||
try:
|
||||
@@ -492,7 +666,7 @@ except Exception:
|
||||
# W2b: Overflow threshold reached — force create fresh workspace
|
||||
existing_ws=""
|
||||
else
|
||||
split_json=$(_real_herdr pane split --pane "$sample_pane" --direction right --cwd "${ws:-.}" $env_flags --no-focus 2>/dev/null || echo "")
|
||||
split_json=$(_real_herdr pane split --pane "$sample_pane" --direction right --cwd "${ws:-.}" $env_flags --env HERDR_WORKSPACE_ID="$existing_ws" --no-focus 2>/dev/null || echo "")
|
||||
target_pane=$(echo "$split_json" | python3 -c "
|
||||
import sys, json
|
||||
try:
|
||||
@@ -516,11 +690,25 @@ try:
|
||||
print(res.get('root_pane', {}).get('pane_id') or w_obj.get('root_pane_id') or res.get('root_pane_id') or res.get('pane_id', ''))
|
||||
except Exception:
|
||||
pass
|
||||
" 2>/dev/null || echo "")
|
||||
ws_id=$(echo "$ws_json" | python3 -c "
|
||||
import sys, json
|
||||
try:
|
||||
d = json.loads(sys.stdin.read())
|
||||
res = d.get('result', {})
|
||||
print(res.get('workspace', {}).get('workspace_id') or '')
|
||||
except Exception:
|
||||
pass
|
||||
" 2>/dev/null || echo "")
|
||||
else
|
||||
if [ -n "${MAM_WS_LABEL:-}" ]; then
|
||||
_real_herdr workspace rename "$existing_ws" "$MAM_WS_LABEL" >/dev/null 2>&1 || true
|
||||
fi
|
||||
ws_id="$existing_ws"
|
||||
fi
|
||||
if [ -n "${ws_id:-}" ]; then
|
||||
export HERDR_WORKSPACE_ID="$ws_id"
|
||||
_herdr_persist_ws_id "$ws_id"
|
||||
fi
|
||||
|
||||
if [ -z "$target_pane" ]; then
|
||||
@@ -584,23 +772,12 @@ except Exception:
|
||||
# "$sess"` with a MAM session name always fails (silently, via `|| true`).
|
||||
# Resolve the real pane_id via `agent get` and close just that pane instead.
|
||||
agent_target=$(_sanitize_herdr_agent_name "$sess")
|
||||
pane_id=$(_real_herdr agent get "$agent_target" 2>/dev/null | python3 -c "
|
||||
import sys, json
|
||||
try:
|
||||
print(json.load(sys.stdin).get('result', {}).get('agent', {}).get('pane_id', ''))
|
||||
except Exception:
|
||||
pass
|
||||
" 2>/dev/null || _real_herdr agent get "$sess" 2>/dev/null | python3 -c "
|
||||
import sys, json
|
||||
try:
|
||||
print(json.load(sys.stdin).get('result', {}).get('agent', {}).get('pane_id', ''))
|
||||
except Exception:
|
||||
pass
|
||||
" 2>/dev/null)
|
||||
pane_id=$(_resolve_herdr_pane_id "$sess" 2>/dev/null || true)
|
||||
if [ -n "$pane_id" ]; then
|
||||
_real_herdr pane close "$pane_id" >/dev/null 2>&1 || true
|
||||
fi
|
||||
_real_herdr kill-session -t "$agent_target" >/dev/null 2>&1 || _real_herdr kill-session -t "$sess" >/dev/null 2>&1 || true
|
||||
_real_herdr kill-session -t "$agent_target" >/dev/null 2>&1 \
|
||||
|| _real_herdr kill-session -t "$sess" >/dev/null 2>&1 || true
|
||||
;;
|
||||
list-panes)
|
||||
sess="" format=""
|
||||
@@ -700,7 +877,15 @@ except Exception:
|
||||
esac
|
||||
done
|
||||
agent_target=$(_sanitize_herdr_agent_name "$sess")
|
||||
_real_herdr agent read "$agent_target" --source visible --lines 100 2>/dev/null || _real_herdr agent read "$sess" --source visible --lines 100 2>/dev/null || true
|
||||
pane_id=$(_resolve_herdr_pane_id "$sess" 2>/dev/null || true)
|
||||
if [ -n "$pane_id" ]; then
|
||||
_real_herdr pane read "$pane_id" --source visible --lines 100 2>/dev/null \
|
||||
|| _real_herdr agent read "$agent_target" --source visible --lines 100 2>/dev/null \
|
||||
|| _real_herdr agent read "$sess" --source visible --lines 100 2>/dev/null || true
|
||||
else
|
||||
_real_herdr agent read "$agent_target" --source visible --lines 100 2>/dev/null \
|
||||
|| _real_herdr agent read "$sess" --source visible --lines 100 2>/dev/null || true
|
||||
fi
|
||||
;;
|
||||
send-keys)
|
||||
sess="" key=""
|
||||
@@ -723,19 +908,7 @@ except Exception:
|
||||
# `pane send-keys` requires a real pane_id ("wN:pN"), not an agent name —
|
||||
# resolve it via `agent get` first (agent-level commands accept names).
|
||||
agent_target=$(_sanitize_herdr_agent_name "$sess")
|
||||
pane_id=$(_real_herdr agent get "$agent_target" 2>/dev/null | python3 -c "
|
||||
import sys, json
|
||||
try:
|
||||
print(json.load(sys.stdin).get('result', {}).get('agent', {}).get('pane_id', ''))
|
||||
except Exception:
|
||||
pass
|
||||
" 2>/dev/null || _real_herdr agent get "$sess" 2>/dev/null | python3 -c "
|
||||
import sys, json
|
||||
try:
|
||||
print(json.load(sys.stdin).get('result', {}).get('agent', {}).get('pane_id', ''))
|
||||
except Exception:
|
||||
pass
|
||||
" 2>/dev/null)
|
||||
pane_id=$(_resolve_herdr_pane_id "$sess" 2>/dev/null || true)
|
||||
if [ -n "$pane_id" ]; then
|
||||
_real_herdr pane send-keys "$pane_id" "$key" >/dev/null 2>&1 || true
|
||||
else
|
||||
@@ -818,12 +991,23 @@ except Exception:
|
||||
done
|
||||
buffer_dir="${WORKSPACE_ROOT:+$WORKSPACE_ROOT/.mam/buffers}"
|
||||
buffer_dir="${buffer_dir:-${TMPDIR:-/tmp}/mam_buffers}"
|
||||
if [ -f "$buffer_dir/$buf" ]; then
|
||||
_real_herdr agent send "$sess" "$(cat "$buffer_dir/$buf")" >/dev/null 2>&1 || true
|
||||
else
|
||||
if [ ! -f "$buffer_dir/$buf" ]; then
|
||||
echo "Error: buffer $buf not found ($buffer_dir/$buf)" >&2
|
||||
exit 1
|
||||
fi
|
||||
pane_id=$(_resolve_herdr_pane_id "$sess" 2>/dev/null || true)
|
||||
if [ -z "$pane_id" ]; then
|
||||
# herdr has no `agent send` subcommand. Returning success here would let
|
||||
# send_keys_safe submit an empty prompt.
|
||||
echo "Error: paste-buffer could not resolve a pane for '$sess'" >&2
|
||||
exit 1
|
||||
fi
|
||||
# Insert only. Submission is owned exclusively by send_keys_safe (ISSUE-1).
|
||||
# A combined insert-and-submit command would double-submit.
|
||||
if ! _real_herdr pane send-text "$pane_id" "$(cat "$buffer_dir/$buf")" >/dev/null 2>&1; then
|
||||
echo "Error: pane send-text failed for '$sess' ($pane_id)" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
delete-buffer)
|
||||
buf="tmp_buffer"
|
||||
@@ -1802,8 +1986,13 @@ send_keys_safe() {
|
||||
|
||||
local sks_buf="sks_${sess}_${job_id}_$$_${RANDOM}_$(date +%s%N 2>/dev/null || date +%s)"
|
||||
_sks_herdr set-buffer -b "$sks_buf" "$text"
|
||||
_sks_herdr paste-buffer -b "$sks_buf" -t "$sess"
|
||||
local _paste_rc=0
|
||||
_sks_herdr paste-buffer -b "$sks_buf" -t "$sess" || _paste_rc=$?
|
||||
_sks_herdr delete-buffer -b "$sks_buf" 2>/dev/null || true
|
||||
if [ "$_paste_rc" != "0" ]; then
|
||||
echo "send_keys_safe: paste-buffer failed rc=$_paste_rc ($sess)" >&2
|
||||
return 3
|
||||
fi
|
||||
local was_popup=0
|
||||
if [[ "$sess" =~ "cline" ]] || [[ "$sess" =~ "claude" ]] || [[ "$sess" =~ "agy" ]] || [[ "$sess" =~ "grok" ]]; then
|
||||
# Skip strict paste check due to scrollout false-positives, proceed to C-m submission loop
|
||||
|
||||
Reference in New Issue
Block a user