feat(box): passkey fetch, agent key-approval flow, unified lookups, tmux agent UX
- box passkey [show|fetch] (+ muse passkey): documents VM-only passkey (/srv/box/passkey.txt, fallback /etc/netvm/passkey.txt on 34.139.37.135), probes VM over SSH with graceful fallback; --json supported. No secrets on bl. - approvals: request_key_approval / check_node_key_request; KEY_APPROVAL status surfaced in `box approvals check`; allow/deny resolve + audit to box-ctl.jsonl; never auto-approved. New `box approvals request-key <node> --reason`. - box lookup (summary|fleet|threads|unread|approvals|key|docs) and docs-lookup engine with lookup_internal/ database (docs_internal symlink). - muse-tmux: non-TTY attach falls back to scrollback capture; prune NameError fix. - box/muse passthrough for tmux/muse/docs; thread list/view alias + prefix resolve. - Docs: AGENTS.md, AGENT-TOOLING.md, BOX-WEB-SURFACE-GUIDE.md, README. - Tests: key-approval + passkey tests; sync stale sidechat UUIDs and manifest name. - .gitignore runtime trackers (subagent-sessions, conversation-nudge-tracker).
This commit is contained in:
@@ -503,6 +503,26 @@ def main():
|
||||
elif job.get("target"):
|
||||
target = job.get("target").strip()
|
||||
|
||||
# Pre-dispatch approval & input check (auto-approve trusted; warn if blocked)
|
||||
if not dry_run:
|
||||
try:
|
||||
import approvals
|
||||
app_info = approvals.inspect_node_approvals(agent)
|
||||
if app_info.get("has_pending"):
|
||||
if app_info.get("is_trusted"):
|
||||
print(f"Pre-dispatch: auto-approving trusted request for {agent} ({app_info.get('target')})")
|
||||
approvals.allow_node_approval(agent, always=True, caller="job-dispatch")
|
||||
else:
|
||||
print(f"Warning: Agent '{agent}' has untrusted pending approval ({app_info.get('target')}). Dispatch may stall.", file=sys.stderr)
|
||||
log_event("job_dispatch_approval_blocked", {"job_id": job_id, "agent": agent, "target": app_info.get("target")})
|
||||
elif app_info.get("status") == "INPUT_WAIT":
|
||||
waits = app_info.get("input_waits", [])
|
||||
w_desc = "; ".join(w.get("task", "") for w in waits)[:80]
|
||||
print(f"Notice: Agent '{agent}' has task waiting for input ({w_desc}).", file=sys.stderr)
|
||||
log_event("job_dispatch_agent_input_wait", {"job_id": job_id, "agent": agent, "waits": w_desc})
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Sidechat-first policy (2026-10-04): refuse to dispatch to main chat
|
||||
# unless the job explicitly opts in. Never fall back to main silently.
|
||||
allow_main = bool(job.get("allow_main_chat")) or args.allow_main_chat
|
||||
|
||||
Reference in New Issue
Block a user