Skip to content

fix(agent): a Mirasim preset is read by whether Mirasim is here, not by a mirasim on PATH - #1040

Merged
LivXue merged 5 commits into
EverMind-AI:mainfrom
Handsome-wzw:fix/mirasim_installed_check
Oct 21, 2026
Merged

LivXue merged 5 commits into
EverMind-AI:mainfrom
Handsome-wzw:fix/mirasim_installed_check

Conversation

@Handsome-wzw

@Handsome-wzw Handsome-wzw commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Every <agent>-Mirasim preset read as Not installed on this computer (and still not found after Check again) on a computer that had Mirasim installed and running. The probe held those rows to a mirasim executable on the login shell's PATH (SHIM_REQUIRED_EXECUTABLES), and Mirasim's installer never puts one there: it writes its CLI to ~/.mirasim/bin/mirasim at every start of its host and adds that directory to no PATH (measured on macOS against 0.0.571 to 0.0.585: nothing in ~/.zprofile / ~/.zshrc, nothing under /etc/paths.d, no mirasim subcommand that would). The adapter runs no such executable anyway; it reaches the host over its WebSocket.

  • mirasim_presets.presence(env, path) answers what a row needs to know, in the order the adapter looks for its host: running for a host named in RAVEN_MIRASIM_HOST, the socket of a host Mirasim runs over ssh, or the desktop app's port with its token file written and something listening; installed for the launcher under Mirasim's home, mirasim on the login shell's PATH, or a token file nothing answers behind; absent otherwise. The three places are spelled in this package as well as in the adapter -- raven.agent.subagent may not import raven.acp, a served surface -- and a test pins them equal (HOST_ENV, DESKTOP_PORT, mirasim_home, remote_socket, the token file the adapter's port_token / locate_host read).
  • A killed host leaves its token file behind (measured against 0.0.571 started in isolation: a SIGTERM empties run/, a SIGKILL does not), so the file alone is evidence of an install and only an open loopback port of a host. The listen check is one loopback connect with a 0.3 s cap.
  • _probe_acp reads a Mirasim row by that: absent is missing with the download link (probe_missing: mirasim, which the page already maps to mirasim.ai/download), installed is attention with Mirasim is installed (<where>) but its host is not running: open the Mirasim app, then check again, running goes on to the snapshot as any row does. The boot's auto-verify list asks the same question once for all nine rows and handshakes them only with a host to reach.
  • SHIM_REQUIRED_EXECUTABLES keeps pi alone (the two tests that hold that table are unchanged); the mirasim_<agent> row in docs/agent-integrations (en, zh), the CONTEXT entry and the CHANGELOG say the new rule.
  • Review round: a host is one that answers. The socket of a server Mirasim runs here over ssh counts as a host only when a bounded unix connect is accepted (_answers, the twin of _listening), since a killed server, or a rebooted machine, leaves the file behind and Mirasim's own start script removes it before every start; what the desktop client (0.0.587 bundle) leaves on a machine it delivered a server to -- servers/<version>/, current/start.sh, or a dead server's run/ files -- counts as installed (remote_install), so a stopped remote server no longer reads absent; the desktop token is read as the adapter's port_token reads it (blank is no token), and the test pinning the two packages equal now pins the decision for a valid, an empty and a whitespace-only token; the launcher is looked for as mirasim, mirasim.cmd and mirasim.exe; both probe sentences name the second way a host comes to be here (open the Mirasim app, or let the app on another computer reach this one again), as the adapter's NO_HOST does.

Independent of #1039, which makes the adapter's connection to a current host succeed once a row is probed as running; together they take a Mirasim row from Not installed to ready. The two PRs touch disjoint source files and the same CHANGELOG section.

Type

  • Fix
  • Feature
  • Docs
  • CI / tooling
  • Refactor
  • Other

Verification

  • New tests: tests/test_subagent_mirasim.py -- the places the presence check looks are the adapter's (constants, home, remote socket, the token file port_token and locate_host read); absent; the launcher under the home or mirasim on the PATH is installed with no host; a token file counts as a host only while something listens, read off a real loopback listener and again after it closed; a host named in the environment; the socket of a host run over ssh (POSIX). tests/test_subagent_probe.py -- a Mirasim row through probe_one in the three states (absent: missing, absent == "mirasim", the download link in the detail; installed: attention naming the launcher and "open the Mirasim app"; running: on to the snapshot), the row's own env over the login shell's; the auto-verify list asks presence once, in the login shell's environment and against its PATH, and offers the rows only when running.
  • Old code (origin/main) with the two test files copied over: 3 failed, 6 errors (every new case); green here.
  • Related suites at -n 0 (test_subagent_mirasim, test_subagent_probe, test_subagent_third_party, test_acp_mirasim_server, test_acp_mirasim_host, test_import_cycle_budget, test_docs_site): 548 + passed; diff coverage 100.00% (65/65 executable changed lines).
  • Real machine (this Mac, Mirasim 0.0.585 running, ~/.local/bin/mirasim removed for the run): a gateway of a scratch tree holding this change on top of fix(acp): the Mirasim adapter sends the host its token where the host reads it #1039, isolated home, channels off, the page's own subagents.list with refresh_login_env -- under a scratch HOME with nothing of Mirasim in it, both rows answer missing with probe_missing: mirasim and Mirasim is not installed on this computer; install the Mirasim app from https://mirasim.ai/download and open it; under a scratch HOME holding only .mirasim/bin/mirasim, attention with Mirasim is installed (<that launcher>) but its host is not running: open the Mirasim app, then check again; under the real home with the host up, the rows go on to the snapshot, and the page's Test on Claude Code-Mirasim answers ok with connected to mirasim-claude 0.0.585 over ACP v1; sessions: resume, load; models: 21; modes: low, medium, high, xhigh, max, ultra (15 s). Each gateway was stopped by its pid with nothing left behind, and the symlink was put back after. On main's code the same computer answers Not installed for every row, which is the report this started from.
  • Full Python suite on this tree (after merging main at d9c0a1d): 34161 passed, 3 failed -- the three that fail on main on this machine as well (one libreoffice case, two simulation cases); no idle-ceiling warning from the touched files.
  • The Windows job caught the PATH half of the launcher test naming an extensionless file, which shutil.which on Windows does not find (it resolves a command only under a PATHEXT extension): the case names mirasim.cmd there and compares the path found case-insensitively. Test-only; the presence check itself passed on Windows.
  • Review round: new tests -- the equality test with the adapter pins the decision for a valid, an empty and a whitespace-only token (presence is running exactly when locate_host finds a host, with a listener standing in); a blank token beside a listener is installed; a listener on the desktop port with no token file is absent; mirasim.cmd / mirasim.exe under the home are found; a server installed over ssh and stopped is installed, naming current/start.sh, and still installed as a delivered version alone; the socket of such a server is a host only while a real AF_UNIX listener answers, installed once it is closed and the file is left, and installed as a dead server's run/ leftovers alone; a row whose own env names no home is judged in the login shell's MIRASIM_HOME. Old code (cc1016915) with the two test files copied over: 3 failed, 10 errors; green here. Six mutants against the two test files (the login-shell half of the environment dropped, the token half of the desktop test dropped, a blank token counted, the socket read without a connect, the remote install not counted, the launcher suffixes dropped): each caught. Related suites 431 + 553 passed; diff coverage 99.08% (108/109; the uncovered line is the return for a Python without AF_UNIX). Real gateway (a scratch tree of this change on fix(acp): the Mirasim adapter sends the host its token where the host reads it #1039, isolated home, the page's own subagents.list): the desktop host running -> both rows go on to the snapshot and the Test on Claude Code-Mirasim answers ok (connected to mirasim-claude 0.0.587 over ACP v1; ... models: 21); a scratch HOME holding the 0.0.587 client's remote layout with a live AF_UNIX listener on run/server.sock -> on to the snapshot; the listener closed with the socket file left (a killed server), and then the stopped layout without it -> attention, Mirasim is installed (<home>/.mirasim-remote/current/start.sh) but no host of it is running: open the Mirasim app, or let the Mirasim app on another computer reach this one again, then check again; a scratch HOME with a whitespace-only local-4970.token while the real host listens on 4970 -> installed, not running. On the previous head the same remote layouts read absent (stopped) and running (socket left behind). Not measured: a real remote server -- this build's mirasim ssh connect has no download source for the server package (MIRASIM_SERVER_DL_ROOT is empty in the bundle, nothing cached), so none could be delivered to a test machine; the layout is read off the client bundle's constants and stop command.
  • ruff check, ruff format --check, ty check, lint-imports and the import-cycle budget clean; the three range gates pass over origin/main..HEAD; commitlint passes.

Risk

  • Security impact considered: the probe reads two files' existence under Mirasim's home and opens one loopback connection to the desktop port with a 0.3 s cap; it sends nothing, reads nothing back and logs nothing. No token is read.
  • Backward compatibility considered: a computer with mirasim on its PATH keeps reading as installed; a row with its own RAVEN_MIRASIM_HOST / MIRASIM_HOME in env is judged in that environment, as it is launched. The probe_missing: mirasim the page maps to the download site is kept. No contract or page change.
  • Rollback path is clear for risky changes: revert the commit; nothing is persisted.

Related Issues

N/A

Handsome-wzw and others added 2 commits October 21, 2026 11:59
…by a `mirasim` on PATH

Every `<agent>-Mirasim` row read as "Not installed on this computer" on a
computer that had Mirasim running. The probe held the rows to a `mirasim`
executable on the login shell's PATH (`SHIM_REQUIRED_EXECUTABLES`), which
Mirasim's installer never puts there -- it writes its CLI to
`~/.mirasim/bin/mirasim` at every start of its host and adds that
directory to no PATH (measured on macOS against 0.0.571 to 0.0.585) -- and
the adapter runs no such executable anyway: it reaches the host.

- `mirasim_presets.presence(env, path)` answers what the row needs to
  know, in the order the adapter looks for its host: `running` for a host
  named in `RAVEN_MIRASIM_HOST`, the socket of a host Mirasim runs over
  ssh, or the desktop app's port with its token file written and something
  listening; `installed` for the launcher under Mirasim's home, `mirasim`
  on the PATH, or a token file nothing answers behind; `absent` otherwise.
  The three places are spelled here as well as in the adapter (this
  package may not import a served surface) and a test pins them equal.
- A killed host leaves its token file behind (SIGTERM empties `run/`,
  SIGKILL does not; measured against 0.0.571), so the file alone is
  evidence of an install and only an open loopback port of a host.
- `_probe_acp` reads a Mirasim row by that: `absent` is missing with the
  download link (`probe_missing: mirasim`, as before), `installed` is
  attention with "open the Mirasim app, then check again", `running` goes
  on to the snapshot. The boot's auto-verify list asks the same question
  once for all nine rows and handshakes them only with a host to reach.
- `SHIM_REQUIRED_EXECUTABLES` keeps `pi` alone; the docs, the CONTEXT
  entry and the CHANGELOG say the new rule.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
@Handsome-wzw
Handsome-wzw requested a review from LivXue as a code owner October 21, 2026 04:12

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; this can merge as far as I am concerned.

I reviewed the refreshed github/main...HEAD diff and the surrounding individual-probe and boot-time auto-verification paths. The new three-state presence check follows the adapter's host lookup order, preserves row-specific environment precedence, avoids treating a stale desktop token as a live host, and keeps remote-machine probing on the existing remote path. I also checked the change history, compatibility of SHIM_REQUIRED_EXECUTABLES and its callers, that tests were added rather than weakened, the served-surface import boundary, the canonical Mirasim terminology in CONTEXT.md, source-language exemptions, and repository asset rules.

Verification:

  • uv run --frozen --python 3.12 --extra dev pytest tests/test_subagent_mirasim.py tests/test_subagent_probe.py::test_a_shim_preset_whose_agent_is_absent_is_not_offered_for_verification tests/test_subagent_probe.py::test_a_mirasim_row_is_read_by_whether_mirasim_is_here_not_by_an_executable -x -o addopts='': 24 passed.
  • PYTHONPATH=. uv run --frozen --python 3.12 --extra dev python scripts/check_source_language.py github/main..HEAD: passed.
  • PYTHONPATH=. uv run --frozen --python 3.12 --extra dev python scripts/check_large_files.py github/main..HEAD: passed.
  • The broader two-file Python 3.12 run reached 65 passes before one reproducible failure in untouched test_a_cli_test_whose_directory_stays_in_use_reports_the_reply; its cleanup assertion records no refused removal on this runner. The same two-file suite passed all 116 tests under Python 3.13, and the failing test and its implementation paths are outside this diff.

`shutil.which` on Windows resolves a command only under one of the
PATHEXT extensions, so the extensionless `mirasim` the case wrote was
found on POSIX and not on the Windows job; it is `mirasim.cmd` there,
and the path found is compared case-insensitively, as Windows reports it.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; this can merge as far as I am concerned.

The new revision only corrects the launcher-on-PATH test for Windows: mirasim.cmd is discoverable through PATHEXT, and the resolved-path comparison now accounts for Windows case folding while retaining the presence-state assertion. I re-read the full PR diff and checked that this delta does not alter callers, runtime behavior, backward compatibility, architecture boundaries, or the earlier test coverage; the new comment and commit also follow the repository's language and commit rules.

Verification:

  • uv run --frozen --python 3.12 --extra dev pytest tests/test_subagent_mirasim.py -x -o addopts='': 22 passed.
  • PYTHONPATH=. uv run --frozen --python 3.12 --extra dev python scripts/check_source_language.py f93884e66818..HEAD: passed.
  • git diff --check f93884e66818..HEAD: passed.

@LivXue

LivXue commented Oct 21, 2026

Copy link
Copy Markdown
Member

Windows compatibility review (head cc1016915a3b179f8a03cf11b4cdc95b20ea723f, base d9c0a1d74222825ad621a0fd138b691c0edd537d).

Scope: Inspected all 9 changed files (CHANGELOG.md, CONTEXT.md, docs-site/docs/agent-integrations.md, docs-site/docs/agent-integrations.zh.md, raven/agent/subagent/mirasim_presets.py, raven/agent/subagent/presets.py, raven/agent/subagent/probe.py, tests/test_subagent_mirasim.py, tests/test_subagent_probe.py) and verified exact-head source extraction and reverse patch applicability on native Windows (verify-source.ps1).

Findings

  • No actionable Windows compatibility issue was found in the inspected scope. mirasim_presets.presence checks RAVEN_MIRASIM_HOST, guards remote_socket().is_socket() with contextlib.suppress(OSError), checks the desktop token file mirasim_home(env) / "run" / f"local-{DESKTOP_PORT}.token" alongside a bounded socket.create_connection(("127.0.0.1", port), timeout=0.3), and checks shutil.which("mirasim", path=...). In tests/test_subagent_mirasim.py, the PATH launcher test uses mirasim.cmd on win32 (for PATHEXT resolution) with case-insensitive path comparison, and the Unix socket test is guarded with @pytest.mark.skipif(sys.platform == "win32", reason="a unix socket").
  • Low / Non-blocking observation (raven/agent/subagent/mirasim_presets.py:113,175-177): LAUNCHER = ("bin", "mirasim") checks home.joinpath("bin", "mirasim").is_file() without a Windows extension (.cmd / .exe). On Windows, if Mirasim is installed with its host stopped and is not on PATH and has no leftover run/local-4970.token file, a Windows launcher named mirasim.cmd or mirasim.exe under ~/.mirasim/bin/ would not match launcher.is_file() unless an extensionless mirasim wrapper also exists.

Verification and limitations

  • Native Windows 11 / Python 3.13.12 (uv run pytest ... -n 0 -x): 51 passed, 4 skipped, 0 failed across tests/test_subagent_mirasim.py, tests/test_acp_mirasim_host.py, and the Mirasim preset/probe cases in tests/test_subagent_probe.py (the 4 skipped cases are the AF_UNIX socket tests).
  • Live Mirasim desktop host verification was not run on this machine.
Exact verification commands
# suite: 51 passed, 0 failed, 0 errors, 4 skipped; exit 0
# Working directory: C:\Users\livxu\AppData\Local\Temp\raven-windows-review\6660f04c145d4dfbb1a1faea794a77b6\pr1040\source\head
uv run pytest tests/test_subagent_mirasim.py tests/test_acp_mirasim_host.py tests/test_subagent_probe.py -k 'mirasim or presence or shim_preset or nowhere or listening' -n 0 -x --tb=short --basetemp 'C:\Users\livxu\AppData\Local\Temp\raven-windows-review\6660f04c145d4dfbb1a1faea794a77b6\checks\1040-suite\pytest' --junitxml 'C:\Users\livxu\AppData\Local\Temp\raven-windows-review\6660f04c145d4dfbb1a1faea794a77b6\checks\1040-suite\junit.xml'

@0xKT

0xKT commented Oct 21, 2026

Copy link
Copy Markdown
Member

Not a blocker (review of cc101691, which already merges main d9c0a1d7): an installed Mirasim with no mirasim on PATH now reads installed or running instead of absent, as the PR says. There is one thing worth changing, and two test gaps. Line numbers are in this head.

Severity of this one finding, not a verdict on the pull request. Any blocking findings from this pass are review threads on the changed files; GitHub renders those collapsed, as a file name with no text.

  1. presence() is a second copy of the adapter's host search, and the two already disagree.

    • Where the host is found is decided by raven/acp/mirasim/host.py: locate_host (:132) with port_token (:106). This PR restates it in raven/agent/subagent/mirasim_presets.py: HOST_ENV, DESKTOP_PORT, mirasim_home, remote_socket, token_file, and the order inside presence (:154-184).
    • test_the_places_the_presence_check_looks_are_the_adapters pins the names and paths equal, but not the decision. presence counts a token file by is_file() (:172). port_token reads the file and treats an empty or whitespace-only one as no token.
    • With a stand-in listening on the port (patched on both sides, never 4970):
      • A valid token gives running from presence and a host from locate_host.
      • An empty token, or a whitespace-only one, gives running from presence and None from locate_host.
    • The copy is not forced. raven.agent may not import raven.acp, but both may import an inner leaf module, which could own the search once.
  2. Test gaps. I ran each mutant against test_subagent_mirasim.py, test_subagent_probe.py and test_rpc_subagents.py in a venv whose spawned children import this tree. The unmutated control gives 278 passed:

    • With _mirasim_env reduced to the row's own env (the login shell's dropped), all 278 still pass. A user who exports MIRASIM_HOME only in their shell rc depends on that half.
    • With has_token and removed at :173 (a listener alone reads running), all 278 still pass, although locate_host finds no host in that case.

@gloryfromca

Copy link
Copy Markdown
Member

No blockers; suggestions only, and they are marked inline.

I confirmed the nonblocking observation in the review above. With a whitespace-only desktop token and a listener on the selected loopback port, mirasim_presets.presence() returned running while the adapters host.locate_host() returned None. That makes the duplicated decision logic a real follow-up concern, but it does not prevent this improvement from merging. The two named mutation survivors are coverage gaps rather than additional demonstrated runtime failures.

Verification: uv run --frozen --python 3.12 --extra dev pytest tests/test_subagent_mirasim.py tests/test_acp_mirasim_host.py -x -o addopts='' passed all 49 tests; the focused reproduction printed presence ('running', ...) and locate_host None.

Comment thread raven/agent/subagent/mirasim_presets.py Resolved
Comment thread raven/agent/subagent/mirasim_presets.py Resolved
@LivXue

LivXue commented Oct 21, 2026

Copy link
Copy Markdown
Member

No blockers in the reviewed scope.

Scope: I reviewed only the raven/agent/** portion of this PR -- 3 of 9 changed files (raven/agent/subagent/mirasim_presets.py, presets.py, probe.py). The remaining 6 files (the two test files, CHANGELOG.md, CONTEXT.md and the two docs-site pages) were read as context but are not covered by this verdict.

I examined the new three-state presence() against the adapter's locate_host and port_token; the removal of the Mirasim keys from SHIM_REQUIRED_EXECUTABLES and every reader of that table (shim_requirement_for, the boot-time filter, the contract tests in test_subagent_third_party.py); the Mirasim branch in _probe_acp with _mirasim_env, and how _group() files the new attention rows; and the boot-time filter in _unconfigured_acp_preset_rows, including what the extra verification handshakes do on the host (the adapter's new_session creates a local session only). All four changes are fine as designed: the presence check fits next to the existing Mirasim presets in raven/agent/subagent/, the table removal and the _probe_acp branch need each other and land together, and selecting Mirasim presets for boot-time verification only when a host is reachable is the right condition. The PR does not touch spawn, run_subagent_dag or the DAG orchestration skill, and changes no tool registration or schema visibility.

Confirmed findings:

  • Nonblocking (inline, mirasim_presets.py:169): a leftover ~/.mirasim-remote/run/server.sock from a killed or rebooted remote server reads as running, so a row recorded ready stays ready; the desktop branch already requires a listener for the same case.
  • Question (inline, mirasim_presets.py:182): on a machine Mirasim reaches over ssh, a stopped remote server reads as absent and the row sends the owner to the desktop download page, because no sign of the remote install is counted.

Neither is a regression against the base; the main target of the fix, the desktop app installed, running, or installed but closed, holds in the code and the tests.

Already raised on this PR and not filed again here:

  • The Windows launcher extension (LAUNCHER matches no mirasim.cmd / mirasim.exe under ~/.mirasim/bin/) is the Low / Non-blocking observation in the earlier PR-level Windows compatibility comment; I point to it rather than open a thread.
  • presence() counting an empty or whitespace-only token file where port_token does not, and the two surviving mutants, are covered by 0xKT's comment and gloryfromca's confirmation. A single shared host search would not close the first inline note on its own: the adapter is just as willing to pick a leftover socket, and it is the connect that tells them apart.

Verification:

  • At cc1016915a3b, from an archive of the head outside any checkout (raven/agent/subagent/mirasim_presets.py imported from that tree): a scenario script with HOME pointed at a scratch directory, covering a closed AF_UNIX socket left on disk, a desktop token with no listener as the control, and the 0.0.565 ssh layout with the server stopped. Results are quoted in the two inline notes.
  • pytest tests/test_subagent_mirasim.py tests/test_subagent_probe.py -n 0 -o addopts='' on the same tree: 116 passed.
  • The ring-boundary checks (raven.agent.subagent importing only the standard library and its own package here) are code-reading plus the green python lint CI job; I did not run lint-imports locally.

Checked and not reported: RAVEN_MIRASIM_HOST read as running without a probe (an explicit owner setting the adapter also takes as given); the remote-socket check not being skipped on Windows (guarded by suppress(OSError), and no such file exists there); the 0.3 s loopback connect on the probe path (taken only when the 4970 token exists; loopback accepts or refuses at once); a launcher or token left behind after uninstalling the app reading as installed (a reasonable call for evidence of an install).

…d over ssh counts as installed

Review of cc10169. `presence()` held the desktop host to a token file plus
something listening, but read the socket of a server Mirasim runs over ssh
as a host as soon as the file existed; a server that is killed, or loses
its machine to a reboot, leaves `~/.mirasim-remote/run/server.sock` behind,
so the row went on to its snapshot and kept reading ready. And once such a
server stopped cleanly, nothing counted the install: the row fell through
to absent and sent that owner to the desktop app's download page.

- The remote socket is a host only when a bounded unix connect is accepted
  (`_answers`, the twin of `_listening`); Mirasim's own start script removes
  the socket before every start and counts the server running only with its
  pid alive beside one.
- What the desktop client (0.0.587) leaves on a machine it delivered a
  server to -- `servers/<version>/`, `current/start.sh`, or what a dead
  server left under `run/` -- counts as installed (`remote_install`).
- The desktop token is read as the adapter's `port_token` reads it: stripped,
  a blank file no token, so a listener behind a blank file is not a host.
  The test that pins the two packages equal now pins the decision as well.
- The launcher is looked for by the names a Windows command carries too
  (`mirasim.cmd`, `mirasim.exe`).
- Both probe sentences say the second way a host comes to be here: open the
  Mirasim app, or let the app on another computer reach this one (again).
- Tests for the two mutants the review found surviving: the login shell's
  half of the environment, and the token file beside a listener.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
@Handsome-wzw

Copy link
Copy Markdown
Contributor Author

Both points taken, in 2bd59cf.

  • The token decision now follows port_token: desktop_token reads the file and treats a blank one as no token, so a listener behind a blank file is installed, as locate_host finds nothing. The test that pins the two packages equal pins the decision too: for desk, an empty file and a whitespace-only file, presence() is running exactly when locate_host() finds a host.
  • The two survivors are covered: a row whose own env names no home is judged in the login shell's MIRASIM_HOME (the launcher there is found), and a listener on the desktop port with no token file is absent. Re-ran both mutants plus four more (socket file without a connect, remote install not counted, blank token counted, launcher suffixes dropped) against the two test files: each is caught.
  • On the shared search: agreed that the copy is not forced -- an inner leaf module both packages may import could own locate_host/port_token once. That moves the adapter's code (feat(*): stream Mirasim turns over the running host's WebSocket #996), so I would do it as its own PR rather than fold it in here; as LivXue notes, it would not by itself close the leftover-socket case, which the connect does.

Also the Windows launcher note from the compatibility review: the launcher is looked for as mirasim, mirasim.cmd and mirasim.exe under ~/.mirasim/bin/.

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; this can merge as far as I am concerned.

This revision closes the prior nonblocking concerns: blank desktop tokens now match the adapter's decision, stale remote sockets require an accepted connection, stopped SSH-installed servers remain installed, Windows launcher suffixes are recognized, and the two surviving test mutants are covered. I reviewed the full PR plus the new delta, the individual and boot-time probe callers, the adapter lookup history, backward compatibility, test-strength changes, the served-surface import boundary, canonical terminology, source-language rules, and repository asset rules. I found no new concrete failure worth raising.

Verification:

  • uv run --frozen --python 3.12 --extra dev pytest tests/test_subagent_mirasim.py tests/test_acp_mirasim_host.py tests/test_subagent_probe.py::test_a_mirasim_row_is_read_by_whether_mirasim_is_here_not_by_an_executable tests/test_subagent_probe.py::test_a_mirasim_row_is_judged_in_the_login_shells_environment_when_its_own_names_no_home -x -o addopts='': 55 passed.
  • The broader three-file run reached 87 passes before the previously reported untouched Python 3.12 cleanup assertion stopped it.
  • scripts/check_source_language.py github/main..HEAD, scripts/check_large_files.py github/main..HEAD, and git diff --check github/main...HEAD: passed.

@LivXue

LivXue commented Oct 21, 2026

Copy link
Copy Markdown
Member

No blockers in the reviewed scope.

Scope: re-review of the delta from cc1016915a3b to 2bd59cf34a71, over the raven/agent/** portion of this PR (3 of 9 changed files: raven/agent/subagent/mirasim_presets.py, presets.py, probe.py). The two test files, CHANGELOG.md, CONTEXT.md and the two docs-site pages were read as context and are not covered by this verdict.

The two prior findings are fixed at this head, each confirmed by re-running the reproduction it was filed with. A leftover ~/.mirasim-remote/run/server.sock (inline note on mirasim_presets.py:169) no longer reads as running: the remote socket is a host only when a bounded unix connect is accepted (_answers, matching _listening for the desktop port), and that case now returns ('installed', <home>/.mirasim-remote), with a row holding a ready snapshot moving to attention. The stopped-remote-server case (inline note on mirasim_presets.py:258) now reads ('installed', .../servers) through remote_install, and both probe messages name the ssh route. Breaking either fix in turn makes the new tests fail, and the restored files compare byte-identical to the head.

One remaining observation, not filed as a thread and left for a follow-up change. presence() and the adapter no longer agree when a dead remote socket and a live desktop host are present together. On a machine Mirasim has reached over ssh, where a killed server or a reboot left ~/.mirasim-remote/run/server.sock behind, and where the desktop app is also running, presence() now passes over the unresponsive socket and reads running at the desktop port (where='127.0.0.1:4970'). The adapter's locate_host still selects that socket on is_socket() alone, and checks it before the desktop port, so it never reaches the desktop host: the row reads ready (with a snapshot) while every dispatch fails with "cannot reach the Mirasim host at unix socket ...". Reproduced at this head with a dead socket, a desktop token and a TCP listener standing in for the desktop port: presence() returns ('running', '127.0.0.1:<port>'), locate_host() returns the unix socket, connecting to it gives ECONNREFUSED, probe_one gives ready with a ready snapshot, and server._print_installed() in the same process exits 3. Deleting the socket makes locate_host() return 127.0.0.1:<port>. This is not a regression against the base or against cc1016915a3b, and it needs both conditions at once, so it need not hold this PR. The better place to fix it is the adapter: give locate_host the same bounded connect and fall through to the desktop port when it fails, which also fits the shared host lookup you have said you plan to extract. Until then, the presence() docstring's statement that it follows the adapter's host order does not hold in this combination.

Checked and not reported: a non-UTF-8 desktop token file makes desktop_token raise UnicodeDecodeError, which escapes the per-row try in _verify_missing_snapshots and skips that boot's entire verification, configured non-Mirasim rows included; the file is written by Mirasim and breaks the adapter the same way, so it is not reported here. A NUL-byte token is read the same on both sides. The synchronous connect costs nothing for a dead socket and refused at once in every run here. The new tests stub remote_root, so no other suite depends on machine state.

Verification: both prior reproductions re-run at 2bd59cf34a71 from an archive of the head outside any checkout, with HOME pointed at a scratch directory. GitHub reports the PR mergeable.

@gloryfromca

Copy link
Copy Markdown
Member

No blockers; suggestions only, and they are marked inline.

I independently confirmed the remaining follow-up identified above. With a dead remote socket left on disk and a live token-backed desktop listener, presence() reports the desktop port as running, while the adapters locate_host() still selects the dead Unix socket. This needs both states to coincide and has a direct recovery, so it does not hold this revision; the adapter lookup/shared-host-search follow-up is the right place to reconcile it.

Verification: uv run --frozen --python 3.12 --extra dev pytest tests/test_subagent_mirasim.py -x -o addopts='' passed all 26 tests. The focused reproduction returned presence ('running', '127.0.0.1:<port>') while locate_host() returned the dead Unix socket.

@0xKT

0xKT commented Oct 21, 2026

Copy link
Copy Markdown
Member

Merge conflict: @Handsome-wzw this PR no longer merges onto main. git merge-tree of 2bd59cf3 onto the current tip 4ec77de0 conflicts in CHANGELOG.md only.

@gloryfromca

Copy link
Copy Markdown
Member

Blocking: rebase onto current main and resolve the CHANGELOG.md conflict.

Confirmed against current github/main at 4ec77de05c81: git merge-tree --write-tree HEAD github/main exits 1 with a content conflict in CHANGELOG.md. The code-level follow-up remains nonblocking, but this exact revision cannot merge until the branch is refreshed.

Verification: uv run --frozen --python 3.12 --extra dev pytest tests/test_subagent_mirasim.py -x -o addopts='' passed all 26 tests.

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; suggestions only, and they are marked inline.

The merge from current main resolves the CHANGELOG.md conflict correctly: both Mirasim fixes remain documented, the PR diff retains its nine-file feature scope, and git merge-tree --write-tree HEAD github/main is clean. I checked the merge resolution, the refreshed full diff, affected probe/host callers, compatibility, test-strength changes, architecture boundary, and repository policy gates. The previously named dead-remote-socket plus live-desktop-host adapter follow-up remains nonblocking; I found no new issue in this revision.

Verification:

  • uv run --frozen --python 3.12 --extra dev pytest tests/test_subagent_mirasim.py tests/test_acp_mirasim_host.py tests/test_subagent_probe.py::test_a_mirasim_row_is_read_by_whether_mirasim_is_here_not_by_an_executable tests/test_subagent_probe.py::test_a_mirasim_row_is_judged_in_the_login_shells_environment_when_its_own_names_no_home -x -o addopts='': 61 passed.
  • Source-language, large-file, and git diff --check gates passed.

@LivXue LivXue left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving the conflict resolution at head d1cd50c (merge of origin/main 4ec77de into 2bd59cf).

  • Re-ran the same merge locally: every file except CHANGELOG.md is byte-identical to the pushed tree; CHANGELOG.md keeps both Mirasim entries verbatim (main's first, this PR's second).
  • No main regression: git diff origin/main..head and origin/main...head are the same 9 files (+545/-27), and each file's +/- lines hash identically to this PR's own pre-merge diff. raven/acp/, tests/mirasim_fake_host.py and tests/test_acp_mirasim_host.py have zero diff against origin/main, so #1039's query-token fix in raven/acp/mirasim/host.py is intact.
  • PR intent still in place at the new head: presence() running/installed/absent, _answers() requires an accepted unix-socket connect, remote_install() counts an ssh install as installed, desktop_token() reads like host.port_token (strip, blank is no token), launcher suffixes "", ".cmd", ".exe".
  • Re-ran tests/test_subagent_mirasim.py, test_subagent_probe.py, test_subagent_third_party.py and test_rpc_subagents.py at this head, serial: 574 passed.
  • CI at this head: 21 pass, 2 skipping, 0 fail (unit shard 3/4 first tripped the idle-ceiling gate on a test this PR does not touch, 2.44s locally on both main and this head; its re-run passed).

@LivXue
LivXue merged commit 2f76813 into EverMind-AI:main Oct 21, 2026
40 of 41 checks passed
0xKT pushed a commit that referenced this pull request Oct 22, 2026
…ort (#1050)

## Summary

Raven runs several tasks through its agents at once, and each result
comes back as its own turn. The host then asks the user what to do next,
and the user's reply often settles only some of those decisions. The
rest used to drop out of the conversation. This PR keeps them in it.

- The host role (`## Your role`) gains one bullet: a decision the host
put to the user stays open until the user makes it, drops it or says to
leave it for later; when a message settles only some, the host acts on
those and ends the reply by asking about each one still open, a line
each, on every reply. An offer the user passes over is declined, not
open.
- The reply rules (`## How you reply`) gain two lines. Open decisions
close every reply, a line each, also after a one-line answer or a
hand-off, and they are content rather than a recap or a sign-off; one
the user dropped or put off is no longer open, and an offer the user
passed over is declined. A reply that reports the results of several
tasks gives each task its own section under a markdown heading on a line
of its own (`### <task>`), with the decisions after the last section.
- A `spawn` result carries the rule as this system's note after the
result's untrusted fence (`render.open_decisions_note`, added to the
`trusted_note` channel beside `worker_table_note`). The host writes its
closing text right after it reads the receipt, and inside the fence the
note would be data.

Why two homes for one rule: with the role bullet alone, a one-line
answer to an unrelated question closed with the open decisions in 0 of 6
runs, most likely because the simple-answer and stop rules in `## How
you reply` read the reminder as a closing recap; the reply-rules line is
what moved it. The reply rules also render where the role does not: the
role needs a dispatch path, and the turn that generates a Persona, or a
turn on an install that withholds both `spawn` and `run_subagent_dag`,
holds none. There the reply-rules line is the rule's only statement, so
it carries the declined-offer sentence in the role's own words. The
tests pin both places, on both kinds of turn.

Measured on glm-5.3-flash, the default host model, with synthetic
conversations sent through the real per-turn context assembler and the
live tool list (34 tools); tool calls were answered by stubs and never
executed. n = 6 per cell unless stated:

| Situation | Before | After |
|---|---|---|
| one reply asked three, the user settled one | 0/6 | 6/6 |
| three replies asked one each, the user settled the latest | 3/6 | 6/6
|
| a one-line answer to an unrelated question, decisions open | 1/12 |
5/6 |
| a new result arrives while an older decision is open | 0/6 | 6/6 |
| a question about one task, handed on to its instance | 4/6 | 6/6 |
| an offer the user passed over, then an unrelated request | 6/6 | 6/6 |
| all settled / "the rest later" / a fresh chat | 6/6 each | 6/6 each |
| a report on three tasks, every task under a heading | 3/34 | 11/11 |
| a single task's result, no heading added | 12/12 | 6/6 |

The first six rows were re-run with the final wording, whose reply-rules
line carries the declined-offer sentence; against the same prompt
without that sentence no cell fell (two rows went from 5/6 to 6/6 and
the hand-off pair from 3/6 and 5/6 to 4/6 and 6/6, which at n = 6 is
noise, not a gain). The last three rows were measured before the
sentence was added and were not re-run. The hand-off row compares the
prompt with and without the spawn note, both with every tool result
fenced as production fences it; the other rows came from runs whose
stored tool results were not fenced, where the hand-off row read 1/6
without the note. Routing a question to its task's instance held at 6/6
in every arm. The heading row counts replies that reported all three
tasks; the wording had to name the syntax, since "a short heading on a
line of its own" kept the task name inline ("**Pricing** -- ...") in 4
of 7. The replay harness lives outside the repository because it calls a
live model.

## Type

- [ ] Fix
- [x] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

Each new test was watched failing before the change that makes it pass.

- After the rebase onto #1040, #1030, #1051 and #1054: `nice -n 19
.venv/bin/python -m pytest -n 0 -q -p no:randomly tests/test_segments.py
tests/test_agent_loop_subagent_role.py tests/test_agent_loop_run_emit.py
tests/test_agent_loop_token_budget.py tests/test_watch_work.py
tests/test_security_untrusted_context.py
tests/test_unified_playbook_e2e.py tests/test_release_plugin_list.py
tests/test_skill_forge_loader_parity.py tests/test_skill_hub_tools.py
tests/test_subagent_mirasim.py tests/test_subagent_probe.py
tests/test_updates_upgrade.py`, the last six being the test files those
four commits changed: 767 passed.
- The full suite was last run before the declined-offer sentence and
before that rebase, on a base that touched none of the files above; it
was not re-run for the one-sentence prompt change, which the files above
cover. `nice -n 19 .venv/bin/python -m pytest -q -p no:randomly -n 4` in
a venv synced with `uv sync --frozen --python 3.12 --all-extras`: 34158
passed, 181 skipped, 2 xfailed, 6 failed. Three are this machine's
standing failures, red on main here as well:
`test_install_script.py::test_resolve_node_dir_answers_each_case_it_exists_for`,
`test_knowledge_parser_excel.py::test_a_legacy_workbook_is_converted_and_read`
and
`test_subagent_node_runtime.py::test_what_cannot_be_read_names_nothing`
(run as root). The other three were two xdist worker crashes and one
timeout while other jobs held the load near 30; those three tests passed
alone, and their files passed serially, 134 of 134.
- `ruff check` and `ruff format --check` over the changed Python files:
clean.
- `scripts/check_large_files.py`, `scripts/check_source_language.py` and
`scripts/check_commit_messages.py` over `origin/main..HEAD`, and
`commitlint --from origin/main --to HEAD` plus the prospective squash
header: all exit 0; a malformed control message makes commitlint exit 1.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [x] User-facing docs or screenshots are updated when needed

## Risk

- Every host reply can now end with a short list of the decisions still
open, on every lane the host replies on, including scheduled and
result-announcement turns. A reply that reports several tasks now uses
markdown headings, which a channel that does not render markdown shows
as `###`. WeChat is one: its adapter sends the reply text as written.
- Cost: 220 tokens on every host turn's identity (99 for the role
bullet, 74 and 47 for the reply lines), and about 20 tokens on each
`spawn` result, which stay on the stored message.
- `available_history` in `tests/test_agent_loop_token_budget.py` goes
from 126_000 to 125_500 (measured 125_984). #1031 lowers the same bound
at the same lines for `context_retrieve`. This PR is to land first;
#1031 then keeps both comment paragraphs and re-measures with both
prompt additions in.
- Not covered: `run_subagent_dag` results and the DAG result
announcement carry no note, and the spawn result announcement keeps its
"1-2 sentences" instruction.
- Rollback: revert the squash commit. Nothing is migrated or stored in a
new shape.

- [x] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

The note uses the `trusted_note` channel, which only carries text a
Raven module composed; no tool output passes through it.

## Related Issues

N/A
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants