meshcode 2.11.132__tar.gz → 2.11.133__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {meshcode-2.11.132 → meshcode-2.11.133}/PKG-INFO +1 -1
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/__init__.py +1 -1
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/protocol_handler.py +414 -20
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.11.132 → meshcode-2.11.133}/pyproject.toml +1 -1
- {meshcode-2.11.132 → meshcode-2.11.133}/README.md +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/__main__.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/_session_handoff_template.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/_stop_hook_template.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/ascii_art.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/atomic_push.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/claude_update.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/cli.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/comms_v4.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/compat.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/daemon.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/date_parse.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/doctor.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/error_hints.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/exceptions.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/hooks/__init__.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/hooks/repo_path_lock.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/hostd.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/invites.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/launcher.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/launcher_install.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/meshcode_mcp/swarm.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/meshcode_mcp/test_swarm.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/preferences.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/quickstart.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/rpc_allowlist.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/run_agent.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/scripts/check_secrets.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/scripts/race_rate_harness.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/secrets.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/self_update.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/setup_clients.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/supervisor.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/up.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode/upload.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/setup.cfg +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_auto_update_hardening.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_autonomous_closegap_1.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_autonomous_closegap_2.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_autonomous_closegap_3.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_autonomous_prompt_inject.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_boot_bug_regression.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_color_truecolor.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_core.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_date_parse.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_doctor.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_epistemic_v1_python_sdk.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_epistemic_v1_stop_conditions.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_exceptions.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_file_upload.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_hostd_zombie_sessions.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_init_device_code.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_install_guard.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_lease_sigterm_release.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_live_mesh_guard.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_marketplace_ratings.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_migration_integrity.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_rpc_grants.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_run_agent_dry_run.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_run_agent_no_server_import.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_security_regressions.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_self_update_user_site.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_sentinel.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_session_replay_gate.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_setup_path.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_sleep_signals.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_stay_on_loop_hook.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_stop_ghost_terminal.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_swarm_events.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_terminal_lifecycle.py +0 -0
- {meshcode-2.11.132 → meshcode-2.11.133}/tests/test_wait_open_tasks_contradiction.py +0 -0
|
@@ -175,16 +175,28 @@ def _launcher_label(cmd: str) -> str:
|
|
|
175
175
|
# Samuel 22c9f31d: "Launch All de N agentes = 1 ventana con N tabs, NO N
|
|
176
176
|
# ventanas sueltas")
|
|
177
177
|
# ============================================================
|
|
178
|
-
# macOS Terminal.app has NO reliable programmatic tab API —
|
|
179
|
-
# routes were tested live on the target box 2026-06-11 and
|
|
178
|
+
# macOS Terminal.app has NO reliable programmatic tab API — SEVEN native
|
|
179
|
+
# routes were tested live on the target box (macOS 26.4.1, 2026-06-11) and
|
|
180
|
+
# ALL fail (task 2ac3f111 first four; task 4df5a126 the last three):
|
|
180
181
|
# - AppleScript `do script ... in window id N` reuses the selected tab
|
|
181
182
|
# (never creates one)
|
|
182
183
|
# - AppleScript `make new tab` errors -10000 (unsupported by Terminal)
|
|
183
184
|
# - `defaults write com.apple.Terminal AppleWindowTabbingMode always` is
|
|
184
185
|
# ignored (Terminal's tab model predates NSWindow tabbing)
|
|
186
|
+
# - `defaults write -g AppleWindowTabbingMode always` (global domain) is
|
|
187
|
+
# ALSO ignored for `open -a Terminal x.command` — still N windows
|
|
185
188
|
# - System Events Cmd+T opened a NEW WINDOW even with Accessibility
|
|
186
189
|
# granted, the target window raised AND frontmost (flaky beyond repair;
|
|
187
190
|
# also needs Accessibility + steals focus = two extra failure modes)
|
|
191
|
+
# - System Events click of menu item Shell > New Tab > <profile> (the
|
|
192
|
+
# deterministic cousin of Cmd+T) ALSO creates a NEW WINDOW, with the
|
|
193
|
+
# target window verified frontmost at click time
|
|
194
|
+
# - AppleScript bare `do script` (new-window form) with tabbing=always
|
|
195
|
+
# set in either domain still creates separate windows
|
|
196
|
+
# (Window > Merge All Windows would coerce tabs but swallows the user's
|
|
197
|
+
# PERSONAL Terminal windows — prohibited.) Native tabs on Terminal.app can
|
|
198
|
+
# only be created by a human gesture; if true native tabs are required, the
|
|
199
|
+
# supported path is a terminal with a real tab API (e.g. iTerm2).
|
|
188
200
|
# So the fleet window is a tmux session inside ONE Terminal window: each
|
|
189
201
|
# agent = a named tmux window = a clickable tab in the status bar. This buys
|
|
190
202
|
# exactly the spec, with zero Apple Events from the daemon (the TCC
|
|
@@ -202,6 +214,43 @@ def _launcher_label(cmd: str) -> str:
|
|
|
202
214
|
_FLEET_SESSION = "meshcode-fleet"
|
|
203
215
|
|
|
204
216
|
|
|
217
|
+
def _fleet_tab_name(cmd: str) -> str:
|
|
218
|
+
"""Short per-agent tab name for the fleet bar (task 4df5a126).
|
|
219
|
+
|
|
220
|
+
hostd targets are `<project>/<agent>` so _launcher_label yields
|
|
221
|
+
`meshcode-self-improve_backend` — at 3+ agents those long names overflow
|
|
222
|
+
the status bar and Samuel sees ONE truncated tab ("la barra verde
|
|
223
|
+
truncada"). The tab must read like a native Terminal tab: just the agent.
|
|
224
|
+
Take the part after the LAST '/' of the raw `meshcode run` target
|
|
225
|
+
(project names can't contain '/'; launch-batch passes bare agent names,
|
|
226
|
+
which are unchanged). Falls back to the full launcher label.
|
|
227
|
+
"""
|
|
228
|
+
m = re.search(r"meshcode\s+run\s+(?:'([^']*)'|\"([^\"]*)\"|(\S+))", cmd)
|
|
229
|
+
raw = ((m.group(1) or m.group(2) or m.group(3)) if m else "") or ""
|
|
230
|
+
agent = raw.rsplit("/", 1)[-1].strip()
|
|
231
|
+
safe = re.sub(r"[^A-Za-z0-9_.-]", "_", agent).strip("_")
|
|
232
|
+
return safe[:24] or _launcher_label(cmd)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# Native-looking tab bar (task 4df5a126: Samuel rejected the default tmux
|
|
236
|
+
# status line — invisible/truncated). Top-positioned (where Terminal tabs
|
|
237
|
+
# live), one padded block per agent, active tab high-contrast, all
|
|
238
|
+
# mouse-clickable (mouse on). Plain ASCII, no emojis (Samuel rule 52b291b9).
|
|
239
|
+
# Applied idempotently on EVERY spawn so an already-running fleet session
|
|
240
|
+
# picks the style up the next time any agent launches.
|
|
241
|
+
_FLEET_STYLE: tuple = (
|
|
242
|
+
("status-position", "top"),
|
|
243
|
+
("status-style", "bg=#16161e,fg=#a9b1d6"),
|
|
244
|
+
("status-left", "#[bg=#3d59a1,fg=#ffffff,bold] MESHCODE #[default] "),
|
|
245
|
+
("status-left-length", "14"),
|
|
246
|
+
("status-right", "#[fg=#565f89] #{session_windows} agents | click: switch | right-click: stop "),
|
|
247
|
+
("status-right-length", "48"),
|
|
248
|
+
("window-status-format", "#[bg=#24283b,fg=#a9b1d6] #W #[default]"),
|
|
249
|
+
("window-status-current-format", "#[bg=#7aa2f7,fg=#16161e,bold] #W #[default]"),
|
|
250
|
+
("window-status-separator", " "),
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
|
|
205
254
|
def _find_tmux() -> Optional[str]:
|
|
206
255
|
"""tmux binary or None. The launchd daemon's PATH is minimal, so probe the
|
|
207
256
|
common install prefixes + the daemon venv bin explicitly."""
|
|
@@ -227,11 +276,23 @@ def _fleet_wrap(cmd: str) -> str:
|
|
|
227
276
|
Drop the LAST `exec ` (same trick as the .command launcher) so the shell
|
|
228
277
|
survives the agent's exit and can translate rc 143 (SIGTERM = hostd
|
|
229
278
|
stop/ghost-kill, task 91201315: that's the job FINISHING) to 0 — otherwise
|
|
230
|
-
`remain-on-exit failed` would keep a dead pane for every plain Stop.
|
|
279
|
+
`remain-on-exit failed` would keep a dead pane for every plain Stop.
|
|
280
|
+
|
|
281
|
+
CLOSE-TAB=STOP (task 4df5a126, Samuel 68b1c17b: "cierro su terminal y
|
|
282
|
+
ya"): export MESHCODE_CLOSE_STOP_SIGHUP=1 so the MCP server's gated POSIX
|
|
283
|
+
close=stop handler (b6da0d54, default OFF) arms INSIDE fleet tabs only.
|
|
284
|
+
In a tmux pane SIGHUP is unambiguous — only kill-window / kill-session /
|
|
285
|
+
tmux-server death close the pty (a window-close of the shared Terminal
|
|
286
|
+
just DETACHES; ESC never closes the pty) — so the false-stop risk that
|
|
287
|
+
kept the flag off for plain windows (task dfa17461) does not exist here.
|
|
288
|
+
Right-click a tab -> Kill => SIGHUP => agent flips desired_state=stopped
|
|
289
|
+
server-side and exits clean: offline, NO hostd ghost-respawn (mig 494
|
|
290
|
+
coherent). A real crash never runs the handler => crash-respawn intact."""
|
|
231
291
|
if "exec " in cmd:
|
|
232
292
|
head, _, tail = cmd.rpartition("exec ")
|
|
233
293
|
cmd = head + tail
|
|
234
|
-
return (f'
|
|
294
|
+
return (f'export MESHCODE_CLOSE_STOP_SIGHUP=1; '
|
|
295
|
+
f'{cmd}; MC_RC=$?; if [ "$MC_RC" = "143" ]; then exit 0; fi; '
|
|
235
296
|
f'echo "[meshcode] agent exited rc=$MC_RC — tab kept for debugging"; exit $MC_RC')
|
|
236
297
|
|
|
237
298
|
|
|
@@ -296,7 +357,7 @@ def _spawn_fleet_tab(cmd: str) -> tuple[bool, str]:
|
|
|
296
357
|
tmux = _find_tmux()
|
|
297
358
|
if not tmux:
|
|
298
359
|
return False, "tmux not installed (brew install tmux -> tabbed fleet window)"
|
|
299
|
-
label =
|
|
360
|
+
label = _fleet_tab_name(cmd)
|
|
300
361
|
wrapped = _fleet_wrap(cmd)
|
|
301
362
|
try:
|
|
302
363
|
fresh_session = _tmux(tmux, "has-session", "-t", f"={_FLEET_SESSION}").returncode != 0
|
|
@@ -317,13 +378,14 @@ def _spawn_fleet_tab(cmd: str) -> tuple[bool, str]:
|
|
|
317
378
|
"-n", label, "-P", "-F", "#{window_id}", wrapped)
|
|
318
379
|
if r.returncode != 0:
|
|
319
380
|
return False, f"tmux new-window: {(r.stderr or '').strip()}"
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
381
|
+
# Session options, applied on EVERY spawn (idempotent): mouse = click a
|
|
382
|
+
# tab in the bar to switch; detach-on-destroy = when the last tab
|
|
383
|
+
# closes, the attach client exits and the fleet.command closes its own
|
|
384
|
+
# window; _FLEET_STYLE = the native-looking top tab bar. Always-apply
|
|
385
|
+
# (was bootstrap-only) so a session created by an OLDER wheel restyles
|
|
386
|
+
# the moment any agent launches through this code.
|
|
387
|
+
for opt in (("mouse", "on"), ("detach-on-destroy", "on"), *_FLEET_STYLE):
|
|
388
|
+
_tmux(tmux, "set-option", "-t", f"={_FLEET_SESSION}:", *opt)
|
|
327
389
|
win_id = (r.stdout or "").strip()
|
|
328
390
|
if win_id.startswith("@"):
|
|
329
391
|
# Per-tab window options: a CRASHED agent's pane stays (rc!=0 ->
|
|
@@ -360,8 +422,11 @@ def _spawn_fleet_tab(cmd: str) -> tuple[bool, str]:
|
|
|
360
422
|
ok, info = _fleet_attach_linux(tmux)
|
|
361
423
|
if not ok:
|
|
362
424
|
# Tab exists + agent runs, but invisibly — that breaks the
|
|
363
|
-
# visibility contract. Kill the tab and let legacy spawn a
|
|
364
|
-
|
|
425
|
+
# visibility contract. Kill the tab and let legacy spawn a
|
|
426
|
+
# window. Target by @id when we have it (tab names are now
|
|
427
|
+
# short agent names and CAN collide across projects).
|
|
428
|
+
_tmux(tmux, "kill-window", "-t",
|
|
429
|
+
win_id if win_id.startswith("@") else f"={_FLEET_SESSION}:{label}")
|
|
365
430
|
return False, f"fleet attach window failed: {info}"
|
|
366
431
|
return True, f"{info}(fleet-window+tab)"
|
|
367
432
|
return True, "fleet-tab"
|
|
@@ -369,6 +434,326 @@ def _spawn_fleet_tab(cmd: str) -> tuple[bool, str]:
|
|
|
369
434
|
return False, f"tmux fleet failed: {e}"
|
|
370
435
|
|
|
371
436
|
|
|
437
|
+
# ============================================================
|
|
438
|
+
# fleet NATIVE Terminal.app tabs — macOS (task 4df5a126, Samuel GO 9a6e0f02)
|
|
439
|
+
# ============================================================
|
|
440
|
+
# Samuel visually CONFIRMED (screenshot 2026-06-11 11:47) that the System
|
|
441
|
+
# Events click of Shell > New Tab > <profile> creates a REAL native tab.
|
|
442
|
+
# Why every scripted probe said "window": macOS native tabs are SEPARATE
|
|
443
|
+
# NSWindows grouped into one frame — Terminal's AppleScript `windows`
|
|
444
|
+
# enumerates each grouped tab as its own window object with `tabs=1`, so
|
|
445
|
+
# AppleScript COUNTS CANNOT distinguish a grouped tab from a loose window.
|
|
446
|
+
# All earlier "FAIL: new window created" verdicts were this measurement
|
|
447
|
+
# blindness, not failures.
|
|
448
|
+
#
|
|
449
|
+
# Architecture (TCC-safe — the back-2 Mac poisoning note in
|
|
450
|
+
# _spawn_terminal_macos forbids Apple Events FROM THE DAEMON):
|
|
451
|
+
# - The FIRST agent launches via `open -a Terminal <agent>.fleet.command`
|
|
452
|
+
# (the blessed no-Apple-Events path) and becomes the ANCHOR: its shell
|
|
453
|
+
# starts the lock-gated fleet WATCHER in the background. The watcher
|
|
454
|
+
# runs INSIDE Terminal, so Terminal stays its own TCC responsible
|
|
455
|
+
# process for both Apple Events and the Accessibility click (verified
|
|
456
|
+
# granted + working live on the target box 2026-06-11).
|
|
457
|
+
# - Later agents are SPOOLED (~/.meshcode/launchers/fleet-spool/): the
|
|
458
|
+
# watcher runs the GREEN RECIPE per spool entry — raise anchor window,
|
|
459
|
+
# READ the New Tab submenu, click profile item 1, find what appeared
|
|
460
|
+
# (same-window tab OR new grouped NSWindow), `do script` the agent
|
|
461
|
+
# into it. hostd blocks on a .done marker; "fallback"/timeout cascades
|
|
462
|
+
# to the tmux fleet bar, then to legacy one-window-per-agent.
|
|
463
|
+
# - EVERY fleet agent shell offers to become watcher (lock-gated, stale
|
|
464
|
+
# lock stolen after 12s) so closing the anchor tab only moves the
|
|
465
|
+
# watcher to another tab within seconds.
|
|
466
|
+
# - close-tab=STOP (Samuel 68b1c17b): tabs export
|
|
467
|
+
# MESHCODE_CLOSE_STOP_SIGHUP=1 — closing a native tab closes its pty,
|
|
468
|
+
# SIGHUP arms the MCP close=stop handler (b6da0d54), agent flips
|
|
469
|
+
# desired_state=stopped and exits clean (no hostd ghost-respawn,
|
|
470
|
+
# mig 494 coherent). Clean exits (rc 0/143) self-close ONLY their tab;
|
|
471
|
+
# crashes keep the tab + scrollback for debugging.
|
|
472
|
+
# Known trade-off: each spooled spawn raises Terminal + the fleet frame
|
|
473
|
+
# (the menu click needs frontmost) — focus moves during a Launch All burst,
|
|
474
|
+
# which is the moment the spec wants focus anyway.
|
|
475
|
+
|
|
476
|
+
_FLEET_NATIVE_DISABLED_TTL_S = 1800 # back off after a watcher "fallback"
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def _fleet_native_paths() -> dict:
|
|
480
|
+
d = Path.home() / ".meshcode" / "launchers"
|
|
481
|
+
return {
|
|
482
|
+
"dir": d,
|
|
483
|
+
"spool": d / "fleet-spool",
|
|
484
|
+
"alive": d / "fleet-native-alive",
|
|
485
|
+
"pending": d / "fleet-native-pending",
|
|
486
|
+
"disabled": d / "fleet-native-disabled",
|
|
487
|
+
"watcher": d / "meshcode-fleet-watcher.sh",
|
|
488
|
+
"log": d / "fleet-native.log",
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
_FLEET_NATIVE_DRIVER_AS = r'''on run argv
|
|
493
|
+
set myTTY to item 1 of argv
|
|
494
|
+
set agentCmd to item 2 of argv
|
|
495
|
+
tell application "Terminal"
|
|
496
|
+
set myWin to missing value
|
|
497
|
+
repeat with w in windows
|
|
498
|
+
repeat with t in tabs of w
|
|
499
|
+
try
|
|
500
|
+
if (tty of t) is myTTY then set myWin to w
|
|
501
|
+
end try
|
|
502
|
+
end repeat
|
|
503
|
+
end repeat
|
|
504
|
+
if myWin is missing value then error "anchor window not found"
|
|
505
|
+
set idsBefore to id of every window
|
|
506
|
+
set tabsBefore to count of tabs of myWin
|
|
507
|
+
activate
|
|
508
|
+
set index of myWin to 1
|
|
509
|
+
try
|
|
510
|
+
set frontmost of myWin to true
|
|
511
|
+
end try
|
|
512
|
+
end tell
|
|
513
|
+
delay 0.8
|
|
514
|
+
tell application "System Events"
|
|
515
|
+
tell process "Terminal"
|
|
516
|
+
set frontmost to true
|
|
517
|
+
delay 0.3
|
|
518
|
+
-- GREEN-RECIPE RC (commander 9a6e0f02, Samuel screenshot 11:47):
|
|
519
|
+
-- READ the New Tab submenu BEFORE clicking. The read forces AppKit to
|
|
520
|
+
-- populate the menu; the click on an unpopulated menu intermittently
|
|
521
|
+
-- degraded to a loose window. DO NOT REMOVE THIS LINE.
|
|
522
|
+
set subItems to name of menu items of menu 1 of menu item "New Tab" of menu "Shell" of menu bar 1
|
|
523
|
+
click menu item 1 of menu 1 of menu item "New Tab" of menu "Shell" of menu bar 1
|
|
524
|
+
end tell
|
|
525
|
+
end tell
|
|
526
|
+
set targetId to 0
|
|
527
|
+
set targetKind to ""
|
|
528
|
+
repeat with i from 1 to 12
|
|
529
|
+
delay 0.5
|
|
530
|
+
tell application "Terminal"
|
|
531
|
+
if (count of tabs of myWin) > tabsBefore then
|
|
532
|
+
set targetKind to "tab"
|
|
533
|
+
else
|
|
534
|
+
repeat with w in windows
|
|
535
|
+
if idsBefore does not contain (id of w) then
|
|
536
|
+
set targetId to id of w
|
|
537
|
+
set targetKind to "win"
|
|
538
|
+
end if
|
|
539
|
+
end repeat
|
|
540
|
+
end if
|
|
541
|
+
end tell
|
|
542
|
+
if targetKind is not "" then exit repeat
|
|
543
|
+
end repeat
|
|
544
|
+
if targetKind is "" then error "no new tab appeared after menu click"
|
|
545
|
+
tell application "Terminal"
|
|
546
|
+
if targetKind is "tab" then
|
|
547
|
+
do script agentCmd in tab (tabsBefore + 1) of myWin
|
|
548
|
+
else
|
|
549
|
+
-- grouped NSWindow tab: `do script in window id` reuses its fresh
|
|
550
|
+
-- selected shell (verified live 2026-06-11) — exactly what we want.
|
|
551
|
+
do script agentCmd in window id targetId
|
|
552
|
+
end if
|
|
553
|
+
end tell
|
|
554
|
+
return "ok"
|
|
555
|
+
end run'''
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
def _write_fleet_native_watcher() -> Path:
|
|
559
|
+
"""Write (idempotently) the lock-gated spool watcher that creates native
|
|
560
|
+
tabs from INSIDE Terminal. See the architecture block above."""
|
|
561
|
+
p = _fleet_native_paths()
|
|
562
|
+
body = '''#!/bin/bash
|
|
563
|
+
# meshcode fleet native-tabs watcher — GENERATED by protocol_handler.py
|
|
564
|
+
# (task 4df5a126). Runs INSIDE a Terminal tab (TCC: Terminal stays its own
|
|
565
|
+
# responsible process). One holder at a time (lock dir, stale-steal 12s).
|
|
566
|
+
LAUNCH="$HOME/.meshcode/launchers"
|
|
567
|
+
SPOOL="$LAUNCH/fleet-spool"
|
|
568
|
+
LOCK="$LAUNCH/fleet-native-lock"
|
|
569
|
+
ALIVE="$LAUNCH/fleet-native-alive"
|
|
570
|
+
MYTTY="$1"
|
|
571
|
+
exec >>"$LAUNCH/fleet-native.log" 2>&1
|
|
572
|
+
mkdir -p "$SPOOL"
|
|
573
|
+
while :; do
|
|
574
|
+
if mkdir "$LOCK" 2>/dev/null; then
|
|
575
|
+
echo "$$ $MYTTY $(date)" > "$LOCK/owner"
|
|
576
|
+
break
|
|
577
|
+
fi
|
|
578
|
+
AGE=999
|
|
579
|
+
if [ -f "$ALIVE" ]; then
|
|
580
|
+
AGE=$(( $(date +%s) - $(stat -f %m "$ALIVE" 2>/dev/null || echo 0) ))
|
|
581
|
+
fi
|
|
582
|
+
if [ "$AGE" -gt 12 ]; then rm -rf "$LOCK"; continue; fi
|
|
583
|
+
sleep 3
|
|
584
|
+
done
|
|
585
|
+
trap 'rm -rf "$LOCK"; exit 0' EXIT HUP TERM INT
|
|
586
|
+
echo "[watcher] $$ holds lock (tty $MYTTY)"
|
|
587
|
+
N=0
|
|
588
|
+
while :; do
|
|
589
|
+
date +%s > "$ALIVE"
|
|
590
|
+
for f in "$SPOOL"/*.cmd; do
|
|
591
|
+
[ -e "$f" ] || continue
|
|
592
|
+
CMD="$(cat "$f")"
|
|
593
|
+
BASE="${f%.cmd}"
|
|
594
|
+
echo "[watcher] tab spawn: $CMD"
|
|
595
|
+
if /usr/bin/osascript "$LAUNCH/fleet-native-driver.applescript" "$MYTTY" "$CMD"
|
|
596
|
+
then
|
|
597
|
+
echo ok > "$BASE.done"
|
|
598
|
+
else
|
|
599
|
+
echo fallback > "$BASE.done"
|
|
600
|
+
fi
|
|
601
|
+
rm -f "$f"
|
|
602
|
+
done
|
|
603
|
+
N=$((N+1))
|
|
604
|
+
if [ $((N % 300)) -eq 0 ]; then
|
|
605
|
+
find "$SPOOL" -name '*.done' -mmin +60 -delete 2>/dev/null
|
|
606
|
+
fi
|
|
607
|
+
sleep 1
|
|
608
|
+
done
|
|
609
|
+
'''
|
|
610
|
+
p["dir"].mkdir(parents=True, exist_ok=True)
|
|
611
|
+
driver = p["dir"] / "fleet-native-driver.applescript"
|
|
612
|
+
driver.write_text(_FLEET_NATIVE_DRIVER_AS + "\n", encoding="utf-8")
|
|
613
|
+
p["watcher"].write_text(body, encoding="utf-8")
|
|
614
|
+
os.chmod(p["watcher"], 0o755)
|
|
615
|
+
return p["watcher"]
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
def _write_fleet_native_agent(cmd: str) -> Path:
|
|
619
|
+
"""Write the per-agent fleet command file (runs the agent inside its
|
|
620
|
+
native tab; offers to become watcher; close-tab=stop; clean exits close
|
|
621
|
+
ONLY their own tab). Filename keyed by _launcher_label (unique per
|
|
622
|
+
project/agent target); the visible TAB TITLE is the short agent name."""
|
|
623
|
+
p = _fleet_native_paths()
|
|
624
|
+
title = _fleet_tab_name(cmd)
|
|
625
|
+
script_path = p["dir"] / f"{_launcher_label(cmd)}.fleet.command"
|
|
626
|
+
try:
|
|
627
|
+
venv_bin = str(Path(sys.executable).parent)
|
|
628
|
+
except Exception:
|
|
629
|
+
venv_bin = ""
|
|
630
|
+
if "exec " in cmd:
|
|
631
|
+
_head, _, _tail = cmd.rpartition("exec ")
|
|
632
|
+
run_line = _head + _tail # agent as CHILD so bash survives to close the tab
|
|
633
|
+
else:
|
|
634
|
+
run_line = cmd
|
|
635
|
+
lines = [
|
|
636
|
+
"#!/bin/bash",
|
|
637
|
+
'cd "$HOME" 2>/dev/null || cd /',
|
|
638
|
+
rf"printf '\033]0;{title}\007\033]1;{title}\007'",
|
|
639
|
+
]
|
|
640
|
+
if venv_bin:
|
|
641
|
+
lines.append(f'export PATH={shlex.quote(venv_bin)}:"$PATH"')
|
|
642
|
+
lines += [
|
|
643
|
+
# close-tab=STOP (Samuel 68b1c17b) — see architecture block.
|
|
644
|
+
"export MESHCODE_CLOSE_STOP_SIGHUP=1",
|
|
645
|
+
'MC_TTY="$(tty 2>/dev/null)"',
|
|
646
|
+
# every fleet tab offers to run the watcher (lock-gated)
|
|
647
|
+
f'/bin/bash {shlex.quote(str(p["watcher"]))} "$MC_TTY" >/dev/null 2>&1 &',
|
|
648
|
+
"disown",
|
|
649
|
+
run_line,
|
|
650
|
+
"MC_RC=$?",
|
|
651
|
+
# clean exit (0, or 143 = hostd stop sweep): close ONLY this tab.
|
|
652
|
+
# Crash: keep tab + scrollback (debugging > clean window).
|
|
653
|
+
'if { [ "$MC_RC" = "0" ] || [ "$MC_RC" = "143" ]; } && [ -n "$MC_TTY" ]; then',
|
|
654
|
+
" /usr/bin/osascript"
|
|
655
|
+
" -e 'on run argv'"
|
|
656
|
+
" -e 'tell application \"Terminal\"'"
|
|
657
|
+
" -e 'repeat with w in windows'"
|
|
658
|
+
" -e 'repeat with t in tabs of w'"
|
|
659
|
+
" -e 'try'"
|
|
660
|
+
" -e 'if (tty of t) is (item 1 of argv) then close t saving no'"
|
|
661
|
+
" -e 'end try'"
|
|
662
|
+
" -e 'end repeat'"
|
|
663
|
+
" -e 'end repeat'"
|
|
664
|
+
" -e 'end tell'"
|
|
665
|
+
" -e 'end run' \"$MC_TTY\" >/dev/null 2>&1",
|
|
666
|
+
"else",
|
|
667
|
+
' echo "[meshcode] agent exited rc=$MC_RC — tab kept for debugging"',
|
|
668
|
+
"fi",
|
|
669
|
+
]
|
|
670
|
+
p["dir"].mkdir(parents=True, exist_ok=True)
|
|
671
|
+
script_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
672
|
+
os.chmod(script_path, 0o755)
|
|
673
|
+
return script_path
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
def _spawn_fleet_native_macos(cmd: str) -> tuple[bool, str]:
|
|
677
|
+
"""Spawn `cmd` as a NATIVE Terminal.app tab of the shared fleet window.
|
|
678
|
+
|
|
679
|
+
Returns (False, reason) on ANY failure — caller cascades to the tmux
|
|
680
|
+
fleet bar, then legacy windows, so this can never make launches worse."""
|
|
681
|
+
p = _fleet_native_paths()
|
|
682
|
+
# back-off marker: a recent watcher "fallback" (Accessibility revoked,
|
|
683
|
+
# menu shape changed, ...) disables the native path for a while instead
|
|
684
|
+
# of paying a focus-steal + multi-second failure on every launch.
|
|
685
|
+
try:
|
|
686
|
+
if time.time() - p["disabled"].stat().st_mtime < _FLEET_NATIVE_DISABLED_TTL_S:
|
|
687
|
+
return False, "fleet-native recently failed (back-off)"
|
|
688
|
+
except OSError:
|
|
689
|
+
pass
|
|
690
|
+
try:
|
|
691
|
+
p["spool"].mkdir(parents=True, exist_ok=True)
|
|
692
|
+
_write_fleet_native_watcher()
|
|
693
|
+
agent_file = _write_fleet_native_agent(cmd)
|
|
694
|
+
except Exception as e:
|
|
695
|
+
return False, f"could not write fleet-native scripts: {e}"
|
|
696
|
+
|
|
697
|
+
def _alive_age() -> float:
|
|
698
|
+
try:
|
|
699
|
+
return time.time() - p["alive"].stat().st_mtime
|
|
700
|
+
except OSError:
|
|
701
|
+
return 1e9
|
|
702
|
+
|
|
703
|
+
if _alive_age() > 8:
|
|
704
|
+
pend_fresh = False
|
|
705
|
+
try:
|
|
706
|
+
pend_fresh = (time.time() - p["pending"].stat().st_mtime) < 25
|
|
707
|
+
except OSError:
|
|
708
|
+
pass
|
|
709
|
+
if not pend_fresh:
|
|
710
|
+
# become the ANCHOR: first visible tab, watcher host
|
|
711
|
+
try:
|
|
712
|
+
p["pending"].touch()
|
|
713
|
+
except OSError:
|
|
714
|
+
pass
|
|
715
|
+
r = subprocess.run(["open", "-a", "Terminal", str(agent_file)],
|
|
716
|
+
capture_output=True, text=True)
|
|
717
|
+
if r.returncode == 0:
|
|
718
|
+
return True, "terminal(fleet-native-anchor)"
|
|
719
|
+
return False, (r.stderr or "open failed").strip()
|
|
720
|
+
# an anchor is booting — wait for its watcher before spooling
|
|
721
|
+
deadline = time.time() + 25
|
|
722
|
+
while time.time() < deadline and _alive_age() > 8:
|
|
723
|
+
time.sleep(0.5)
|
|
724
|
+
if _alive_age() > 8:
|
|
725
|
+
return False, "fleet-native anchor never came alive"
|
|
726
|
+
|
|
727
|
+
sp = p["spool"] / f"{time.time_ns()}-{_launcher_label(cmd)}.cmd"
|
|
728
|
+
done = sp.with_suffix(".done")
|
|
729
|
+
try:
|
|
730
|
+
sp.write_text(f"/bin/bash {shlex.quote(str(agent_file))}\n", encoding="utf-8")
|
|
731
|
+
except Exception as e:
|
|
732
|
+
return False, f"fleet-native spool write failed: {e}"
|
|
733
|
+
deadline = time.time() + 20
|
|
734
|
+
while time.time() < deadline:
|
|
735
|
+
if done.exists():
|
|
736
|
+
try:
|
|
737
|
+
verdict = done.read_text().strip()
|
|
738
|
+
done.unlink()
|
|
739
|
+
except OSError:
|
|
740
|
+
verdict = ""
|
|
741
|
+
if verdict == "ok":
|
|
742
|
+
return True, "fleet-native-tab"
|
|
743
|
+
try:
|
|
744
|
+
p["disabled"].touch()
|
|
745
|
+
except OSError:
|
|
746
|
+
pass
|
|
747
|
+
return False, f"fleet-native watcher reported {verdict or 'error'}"
|
|
748
|
+
time.sleep(0.4)
|
|
749
|
+
# timeout: pull the spool entry so a late watcher can't double-spawn
|
|
750
|
+
try:
|
|
751
|
+
sp.unlink()
|
|
752
|
+
except OSError:
|
|
753
|
+
pass
|
|
754
|
+
return False, "fleet-native spool timeout"
|
|
755
|
+
|
|
756
|
+
|
|
372
757
|
def _spawn_terminal_macos(cmd: str) -> tuple[bool, str]:
|
|
373
758
|
"""Spawn `cmd` in a new VISIBLE Terminal/iTerm window (detached).
|
|
374
759
|
|
|
@@ -595,7 +980,10 @@ def _spawn_terminal_windows(cmd: str) -> tuple[bool, str]:
|
|
|
595
980
|
# carries one → the cmdline splits and wt fails with 0x80070002.
|
|
596
981
|
# Escape ONLY here; the cmd.exe fallback below stays unescaped
|
|
597
982
|
# (cmd.exe does not split on ';').
|
|
598
|
-
|
|
983
|
+
# Short agent-only tab title (task 4df5a126) — same readability fix
|
|
984
|
+
# as the macOS fleet bar; sanitized [A-Za-z0-9_.-] so it can't
|
|
985
|
+
# smuggle wt args.
|
|
986
|
+
label = _fleet_tab_name(cmd)
|
|
599
987
|
subprocess.Popen([wt, "-w", "meshcode-fleet", "nt",
|
|
600
988
|
"--title", label or "meshcode-agent",
|
|
601
989
|
"--suppressApplicationTitle",
|
|
@@ -612,16 +1000,22 @@ def _spawn_terminal_windows(cmd: str) -> tuple[bool, str]:
|
|
|
612
1000
|
|
|
613
1001
|
def _spawn_terminal(cmd: str) -> tuple[bool, str]:
|
|
614
1002
|
p = platform.system()
|
|
615
|
-
if p
|
|
616
|
-
#
|
|
617
|
-
#
|
|
618
|
-
|
|
1003
|
+
if p == "Darwin":
|
|
1004
|
+
# 1) NATIVE Terminal tabs (task 4df5a126, Samuel GO) — one window,
|
|
1005
|
+
# one real tab per agent, close-tab=stop.
|
|
1006
|
+
ok, info = _spawn_fleet_native_macos(cmd)
|
|
1007
|
+
if ok:
|
|
1008
|
+
return True, info
|
|
1009
|
+
# 2) tmux fleet bar (task 2ac3f111) — shared window, styled tab bar.
|
|
619
1010
|
ok, info = _spawn_fleet_tab(cmd)
|
|
620
1011
|
if ok:
|
|
621
1012
|
return True, info
|
|
622
|
-
|
|
1013
|
+
# 3) legacy one-window-per-agent — never worse than before.
|
|
623
1014
|
return _spawn_terminal_macos(cmd)
|
|
624
1015
|
if p == "Linux":
|
|
1016
|
+
ok, info = _spawn_fleet_tab(cmd)
|
|
1017
|
+
if ok:
|
|
1018
|
+
return True, info
|
|
625
1019
|
return _spawn_terminal_linux(cmd)
|
|
626
1020
|
if p == "Windows":
|
|
627
1021
|
# Windows Terminal tabs are native: _spawn_terminal_windows targets the
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|