Files
wehub-resource-sync 4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

1014 lines
33 KiB
Python

"""Tests for read-only local agent discovery."""
from __future__ import annotations
from pathlib import Path
import pytest
from tools.system.fleet_monitoring import discovery
from tools.system.fleet_monitoring.discovery import (
ProcessRow,
classify_command_provider,
discover_agents,
registered_and_discovered_agents,
)
from tools.system.fleet_monitoring.registry import AgentRecord, AgentRegistry
def _patch_codex_rollout_owners(monkeypatch: pytest.MonkeyPatch, owners: set[int]) -> None:
monkeypatch.setattr(discovery, "process_has_open_codex_rollout", lambda pid: pid in owners)
def test_discover_agent_processes_matches_known_agent_commands(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [
ProcessRow(pid=10, command="opensre"),
ProcessRow(pid=101, command="claude chat"),
ProcessRow(pid=102, command="claude code"),
ProcessRow(
pid=103,
command=(
"/Users/example/.cursor/extensions/anthropic.claude-code/resources/claude "
"--output-format stream-json --input-format stream-json"
),
),
ProcessRow(pid=104, command="aider"),
ProcessRow(pid=105, command="codex"),
ProcessRow(pid=202, command="python -m pytest"),
],
)
candidates = discovery.discover_agent_processes()
assert [(item.name, item.pid) for item in candidates] == [
("aider-104", 104),
("claude-code-102", 102),
("claude-code-103", 103),
("codex-105", 105),
]
def test_discover_agent_processes_filters_desktop_helper_noise(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [
ProcessRow(pid=201, command="/Applications/Claude.app/Contents/MacOS/Claude"),
ProcessRow(
pid=202,
command=(
"/Applications/Claude.app/Contents/Frameworks/Electron "
"Framework.framework/Helpers/chrome_crashpad_handler "
"--database=/Users/example/Library/Application Support/Claude/Crashpad"
),
),
ProcessRow(
pid=203,
command=(
"/Applications/Claude.app/Contents/Frameworks/Claude Helper "
"(Renderer).app/Contents/MacOS/Claude Helper (Renderer) --type=renderer"
),
),
ProcessRow(
pid=204,
command=(
"/Applications/Cursor.app/Contents/Frameworks/Cursor Helper "
"(Plugin).app/Contents/MacOS/Cursor Helper (Plugin) "
"/Applications/Cursor.app/Contents/Resources/app/extensions/"
"json-language-features/server/dist/node/jsonServerMain"
),
),
ProcessRow(
pid=205,
command=(
"/Applications/Cursor.app/Contents/Frameworks/Squirrel.framework/Resources/"
"ShipIt com.todesktop.230313mzl4w4u92.ShipIt"
),
),
],
)
assert discovery.discover_agent_processes() == []
def test_discover_agent_processes_all_mode_does_not_mislabel_desktop_as_claude_code(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [
ProcessRow(pid=201, command="/Applications/Claude.app/Contents/MacOS/Claude"),
ProcessRow(
pid=202,
command=(
"/Applications/Claude.app/Contents/Frameworks/Electron "
"Framework.framework/Helpers/chrome_crashpad_handler"
),
),
ProcessRow(
pid=203,
command=(
"/Applications/Claude.app/Contents/Frameworks/Claude Helper "
"(Renderer).app/Contents/MacOS/Claude Helper (Renderer) --type=renderer"
),
),
],
)
candidates = discovery.discover_agent_processes(include_all=True)
assert candidates == []
def test_discover_agent_processes_all_mode_still_keeps_non_desktop_loose_matches(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [
ProcessRow(
pid=311,
command=(
"/Applications/Cursor.app/Contents/Frameworks/"
"Cursor Helper (Plugin).app/Contents/MacOS/Cursor Helper (Plugin) "
"--cursor-agent-launch"
),
),
],
)
candidates = discovery.discover_agent_processes(include_all=True)
assert [(item.name, item.pid) for item in candidates] == [("cursor-311", 311)]
def test_discover_agent_processes_matches_bare_claude_cli(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [
ProcessRow(pid=10, command="opensre"),
ProcessRow(pid=501, command="claude"),
ProcessRow(pid=502, command="/Users/me/.npm-global/bin/claude"),
],
)
candidates = discovery.discover_agent_processes()
assert [(item.name, item.pid) for item in candidates] == [
("claude-code-501", 501),
("claude-code-502", 502),
]
def test_discover_agent_processes_matches_claude_cli_flag_variants(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [
ProcessRow(pid=10, command="opensre"),
ProcessRow(pid=601, command="claude --resume abc-123"),
ProcessRow(pid=602, command="claude --prefill 'demo prompt'"),
ProcessRow(pid=603, command="claude --print 'one shot'"),
ProcessRow(pid=604, command="claude --continue"),
ProcessRow(pid=605, command="claude -p 'short flag print'"),
ProcessRow(pid=606, command="claude -c"),
ProcessRow(pid=607, command="claude -r abc-123"),
],
)
candidates = discovery.discover_agent_processes()
assert [(item.name, item.pid) for item in candidates] == [
("claude-code-601", 601),
("claude-code-602", 602),
("claude-code-603", 603),
("claude-code-604", 604),
("claude-code-605", 605),
("claude-code-606", 606),
("claude-code-607", 607),
]
def test_discover_agent_processes_matches_claude_cli_equals_form_flags(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [
ProcessRow(pid=10, command="opensre"),
ProcessRow(pid=651, command="claude --resume=abc-123"),
ProcessRow(pid=652, command="claude --prefill=demo"),
ProcessRow(pid=653, command="claude --print=one-shot"),
ProcessRow(pid=654, command="claude -r=abc-123"),
ProcessRow(pid=655, command="claude -p=short"),
],
)
candidates = discovery.discover_agent_processes()
assert [(item.name, item.pid) for item in candidates] == [
("claude-code-651", 651),
("claude-code-652", 652),
("claude-code-653", 653),
("claude-code-654", 654),
("claude-code-655", 655),
]
def test_discover_agent_processes_rejects_claude_desktop_main_in_strict_mode(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [
ProcessRow(pid=701, command="/Applications/Claude.app/Contents/MacOS/Claude"),
],
)
assert discovery.discover_agent_processes() == []
@pytest.mark.parametrize(
"command",
[
"/snap/claude/current/usr/bin/claude",
"/usr/lib/claude-desktop/claude-desktop",
"/tmp/.mount_Claude_xyz123/usr/bin/claude",
"'C:\\Program Files\\Claude\\Claude.exe'",
"'C:\\Users\\me\\AppData\\Local\\Programs\\Claude\\Claude.exe'",
],
)
def test_discover_agent_processes_rejects_claude_desktop_cross_platform_paths(
monkeypatch: pytest.MonkeyPatch, command: str
) -> None:
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [ProcessRow(pid=801, command=command)],
)
assert discovery.discover_agent_processes() == []
assert discovery.discover_agent_processes(include_all=True) == []
def test_discover_agent_processes_does_not_drop_claude_print_with_helper_in_prompt(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [
ProcessRow(
pid=910,
command="claude --print 'look at the helper output from pty-host yesterday'",
),
],
)
candidates = discovery.discover_agent_processes()
assert [(item.name, item.pid) for item in candidates] == [("claude-code-910", 910)]
def test_discover_agent_processes_does_not_drop_claude_print_with_desktop_path_in_prompt(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [
ProcessRow(
pid=915,
command=(
"claude --print 'inspect /Applications/Claude.app/Contents/MacOS/Claude logs'"
),
),
],
)
candidates = discovery.discover_agent_processes()
assert [(item.name, item.pid) for item in candidates] == [("claude-code-915", 915)]
@pytest.mark.parametrize(
"command",
[
"/opt/Claude/claude",
"/opt/Claude/claude --resume id",
"/opt/claude-cli/bin/claude --resume sess",
],
)
def test_discover_agent_processes_allows_cli_installed_under_opt_claude_prefix(
monkeypatch: pytest.MonkeyPatch, command: str
) -> None:
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [ProcessRow(pid=920, command=command)],
)
candidates = discovery.discover_agent_processes()
assert [(item.name, item.pid) for item in candidates] == [("claude-code-920", 920)]
@pytest.mark.parametrize(
"cmdline",
[
["C:\\Program Files\\Claude\\Claude.exe"],
["C:\\Users\\me\\AppData\\Local\\Programs\\Claude\\Claude.exe"],
["/Applications/Claude.app/Contents/MacOS/Claude"],
["/snap/claude/current/usr/bin/claude"],
["/usr/lib/claude-desktop/claude-desktop"],
["/tmp/.mount_Claude_xyz123/usr/bin/claude"],
],
)
def test_is_claude_desktop_artifact_recognises_known_packaging_paths(
cmdline: list[str],
) -> None:
assert discovery._is_claude_desktop_artifact(cmdline) is True
@pytest.mark.parametrize(
"cmdline",
[
["claude"],
["claude", "--resume", "abc"],
["/Users/me/.npm-global/bin/claude"],
["/Users/me/.local/bin/claude", "--prefill", "demo"],
["/usr/local/bin/claude"],
["/opt/claude-rs/claude", "--resume", "id"],
["/opt/snap/claude-cli/bin/claude"],
["/opt/Claude/claude"],
[
"claude",
"--print",
"inspect /Applications/Claude.app/Contents/MacOS/Claude logs",
],
["/Users/me/macos/claude"],
["/opt/tools/macos/claude"],
["/home/user/my-claude-desktop-wrapper/bin/claude"],
["/usr/lib/claude-desktop-tools/cli/claude"],
],
)
def test_is_claude_desktop_artifact_passes_through_cli_invocations(
cmdline: list[str],
) -> None:
assert discovery._is_claude_desktop_artifact(cmdline) is False
def test_discover_agent_processes_rejects_codex_desktop_main_in_strict_mode(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [
ProcessRow(pid=801, command="/Applications/Codex.app/Contents/MacOS/Codex"),
],
)
assert discovery.discover_agent_processes() == []
def test_discover_agent_processes_rejects_cursor_desktop_main_in_strict_mode(
monkeypatch: pytest.MonkeyPatch,
) -> None:
# Strict-mode regression guard: no current branch in `_classify_agent`
# returns "cursor" for `executable == "cursor"`, but the explicit guard
# next to the `cursor-agent` / `cursor-agent-cli` block must keep
# Cursor.app from ever being labelled in strict mode.
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [
ProcessRow(pid=804, command="/Applications/Cursor.app/Contents/MacOS/Cursor"),
],
)
assert discovery.discover_agent_processes() == []
@pytest.mark.parametrize(
"command",
[
"/Applications/Codex.app/Contents/MacOS/Codex",
"/snap/codex/current/usr/bin/codex",
"/tmp/.mount_Codex_xyz123/usr/bin/codex",
"/var/lib/flatpak/app/com.openai.codex/current/active/files/bin/codex",
"'C:\\Program Files\\Codex\\Codex.exe'",
"'C:\\Users\\me\\AppData\\Local\\Programs\\codex\\Codex.exe'",
],
)
def test_discover_agent_processes_rejects_codex_desktop_cross_platform_paths(
monkeypatch: pytest.MonkeyPatch, command: str
) -> None:
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [ProcessRow(pid=802, command=command)],
)
assert discovery.discover_agent_processes() == []
assert discovery.discover_agent_processes(include_all=True) == []
@pytest.mark.parametrize(
"command",
[
"/Applications/Cursor.app/Contents/MacOS/Cursor",
"/snap/cursor/current/usr/bin/cursor",
"/tmp/.mount_Cursor_xyz123/usr/bin/cursor",
"/var/lib/flatpak/app/com.cursor.cursor/current/active/files/bin/cursor",
"'C:\\Program Files\\Cursor\\Cursor.exe'",
"'C:\\Users\\me\\AppData\\Local\\Programs\\cursor\\Cursor.exe'",
],
)
def test_discover_agent_processes_rejects_cursor_desktop_cross_platform_paths(
monkeypatch: pytest.MonkeyPatch, command: str
) -> None:
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [ProcessRow(pid=803, command=command)],
)
assert discovery.discover_agent_processes() == []
assert discovery.discover_agent_processes(include_all=True) == []
@pytest.mark.parametrize(
"cmdline",
[
["/Applications/Codex.app/Contents/MacOS/Codex"],
["/snap/codex/current/usr/bin/codex"],
["/tmp/.mount_Codex_xyz123/usr/bin/codex"],
["/var/lib/flatpak/app/com.openai.codex/current/active/files/bin/codex"],
["C:\\Program Files\\Codex\\Codex.exe"],
["C:\\Users\\me\\AppData\\Local\\Programs\\codex\\Codex.exe"],
],
)
def test_is_codex_desktop_artifact_recognises_known_packaging_paths(
cmdline: list[str],
) -> None:
assert discovery._is_codex_desktop_artifact(cmdline) is True
@pytest.mark.parametrize(
"cmdline",
[
["codex"],
["codex", "--resume", "id"],
["/usr/local/bin/codex"],
["/Users/me/.local/bin/codex"],
["/opt/Codex/codex"],
["/opt/codex-cli/bin/codex"],
["node", "/opt/codex.js"],
[
"codex",
"--print",
"inspect /Applications/Codex.app/Contents/MacOS/Codex logs",
],
# Electron helper paths inside the bundle stay eligible for the loose
# matcher — only the main `<bundle>/Contents/MacOS/<binary>` is blocked.
["/Applications/Codex.app/Contents/Frameworks/Codex Helper"],
],
)
def test_is_codex_desktop_artifact_passes_through_cli_invocations(
cmdline: list[str],
) -> None:
assert discovery._is_codex_desktop_artifact(cmdline) is False
@pytest.mark.parametrize(
"cmdline",
[
["/Applications/Cursor.app/Contents/MacOS/Cursor"],
["/snap/cursor/current/usr/bin/cursor"],
["/tmp/.mount_Cursor_xyz123/usr/bin/cursor"],
["/var/lib/flatpak/app/com.cursor.cursor/current/active/files/bin/cursor"],
["C:\\Program Files\\Cursor\\Cursor.exe"],
["C:\\Users\\me\\AppData\\Local\\Programs\\cursor\\Cursor.exe"],
],
)
def test_is_cursor_desktop_artifact_recognises_known_packaging_paths(
cmdline: list[str],
) -> None:
assert discovery._is_cursor_desktop_artifact(cmdline) is True
@pytest.mark.parametrize(
"cmdline",
[
["cursor-agent"],
["cursor-agent-cli"],
["/usr/local/bin/cursor-agent"],
["/opt/Cursor/cursor"],
[
"cursor-agent",
"--print",
"/Applications/Cursor.app/Contents/MacOS/Cursor",
],
# Cursor's AI agent ships as a Helper inside the .app bundle. The
# narrow `cursor.app/contents/macos/cursor/` hint must let helper
# subprocess paths through so they remain eligible for the loose
# matcher (see `test_…_all_mode_still_keeps_non_desktop_loose_matches`).
["/Applications/Cursor.app/Contents/Frameworks/Cursor Helper (Plugin)"],
],
)
def test_is_cursor_desktop_artifact_passes_through_cli_invocations(
cmdline: list[str],
) -> None:
assert discovery._is_cursor_desktop_artifact(cmdline) is False
def test_discover_agents_legacy_path_matches_bare_claude_via_cursor_terminal(
tmp_path: Path,
) -> None:
terminal = tmp_path / "project" / "terminals" / "72.txt"
terminal.parent.mkdir(parents=True)
terminal.write_text(
"---\npid: 67890\ncwd: /repo\nactive_command: claude --resume sess-1\n---\n",
encoding="utf-8",
)
records = discover_agents(process_rows=[], cursor_projects_dir=tmp_path)
assert [(record.name, record.pid, record.source) for record in records] == [
("claude-code", 67890, "discovered")
]
def test_discover_agents_legacy_path_rejects_claude_desktop_main(tmp_path: Path) -> None:
records = discover_agents(
process_rows=[
ProcessRow(pid=901, command="/Applications/Claude.app/Contents/MacOS/Claude"),
],
cursor_projects_dir=tmp_path,
)
assert records == []
def test_discover_agents_legacy_path_rejects_codex_desktop_main(tmp_path: Path) -> None:
records = discover_agents(
process_rows=[
ProcessRow(pid=902, command="/Applications/Codex.app/Contents/MacOS/Codex"),
],
cursor_projects_dir=tmp_path,
)
assert records == []
def test_display_command_truncates_long_commands() -> None:
command = "claude " + ("--very-long-option " * 20)
display = discovery.display_command(command)
assert len(display) == 120
assert display.endswith("...")
def test_parse_ps_line_with_missing_args_keeps_ppid_and_empty_command() -> None:
row = discovery._parse_ps_line("123 45")
assert row == ProcessRow(pid=123, ppid=45, command="")
def test_parse_ps_line_with_missing_args_tolerates_invalid_ppid() -> None:
row = discovery._parse_ps_line("123 not-a-ppid")
assert row == ProcessRow(pid=123, ppid=None, command="")
def test_discovers_cursor_claude_code_process() -> None:
records = discover_agents(
process_rows=[
ProcessRow(
pid=80435,
command=(
"/Users/me/.cursor/extensions/anthropic.claude-code-2.1.128-darwin-arm64/"
"resources/native-binary/claude --output-format stream-json"
),
)
],
cursor_projects_dir=Path("/does/not/exist"),
)
assert len(records) == 1
assert records[0].name == "cursor-claude-code"
assert records[0].pid == 80435
assert records[0].source == "discovered"
# cursor-claude-code wraps the real Claude Code NDJSON output, so the
# provider rolls up to the same meter (#2023).
assert records[0].provider == "claude-code"
def test_discovers_cursor_agent_exec_helper() -> None:
records = discover_agents(
process_rows=[
ProcessRow(
pid=23995,
command=(
"Cursor Helper (Plugin): extension-host (agent-exec) tracer-agent-2026 [1-4]"
),
)
],
cursor_projects_dir=Path("/does/not/exist"),
)
assert [(record.name, record.pid) for record in records] == [("cursor-agent-exec", 23995)]
# cursor-agent-exec emits Cursor-proprietary output that the Anthropic
# meter cannot parse; rolls up to the ``cursor`` provider so it routes
# to ``NullMeter`` rather than misreading as claude-code (#2023).
assert records[0].provider == "cursor"
def test_ignores_generic_desktop_cursor_processes() -> None:
records = discover_agents(
process_rows=[
ProcessRow(pid=23521, command="/Applications/Cursor.app/Contents/MacOS/Cursor"),
ProcessRow(
pid=23540,
command=(
"/Applications/Cursor.app/Contents/Frameworks/"
"Cursor Helper (Renderer).app/Contents/MacOS/Cursor Helper (Renderer)"
),
),
],
cursor_projects_dir=Path("/does/not/exist"),
)
assert records == []
def test_discovers_agent_cli_from_cursor_terminal_metadata(tmp_path: Path) -> None:
terminal = tmp_path / "project" / "terminals" / "70.txt"
terminal.parent.mkdir(parents=True)
terminal.write_text(
"---\n"
"pid: 12345\n"
"cwd: /repo\n"
"active_command: claude code\n"
"last_command: source .venv/bin/activate\n"
"---\n",
encoding="utf-8",
)
records = discover_agents(process_rows=[], cursor_projects_dir=tmp_path)
assert [(record.name, record.pid, record.source) for record in records] == [
("claude-code", 12345, "discovered")
]
# ``provider`` is set on cursor-terminal discoveries so the dashboard
# wiring (#2023) can resolve them without re-classifying.
assert records[0].provider == "claude-code"
def test_discovered_claude_code_record_carries_provider() -> None:
records = discover_agents(
process_rows=[ProcessRow(pid=42, command="claude code")],
cursor_projects_dir=Path("/does/not/exist"),
)
assert records[0].name == "claude-code"
assert records[0].provider == "claude-code"
def test_discovered_codex_record_carries_provider() -> None:
records = discover_agents(
process_rows=[ProcessRow(pid=99, command="codex exec --ephemeral")],
cursor_projects_dir=Path("/does/not/exist"),
)
assert records[0].name == "codex"
assert records[0].provider == "codex"
def test_ignores_plain_claude_commands_with_code_prefix_arguments() -> None:
records = discover_agents(
process_rows=[
ProcessRow(pid=601, command="claude codebase.py"),
ProcessRow(pid=602, command="claude codegen --project src"),
],
cursor_projects_dir=Path("/does/not/exist"),
)
assert records == []
def test_ignores_non_codex_process_from_codex_named_directory() -> None:
records = discover_agents(
process_rows=[
ProcessRow(
pid=4242,
ppid=4200,
command=(
"/workspace/project-with-codex-in-name/.venv/bin/python "
"/workspace/project-with-codex-in-name/.venv/bin/opensre"
),
)
],
cursor_projects_dir=Path("/does/not/exist"),
)
assert records == []
def test_scan_all_ignores_non_codex_process_from_codex_named_directory(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(
discovery,
"_current_process_rows",
lambda: [
ProcessRow(
pid=4242,
ppid=4200,
command=(
"/workspace/project-with-codex-in-name/.venv/bin/python "
"/workspace/project-with-codex-in-name/.venv/bin/opensre"
),
)
],
)
assert discovery.discover_agent_processes(include_all=True) == []
def test_discovers_single_codex_row_for_node_wrapper_and_native_child(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_patch_codex_rollout_owners(monkeypatch, {702})
records = discover_agents(
process_rows=[
ProcessRow(pid=701, ppid=1, command="node /Users/me/.local/bin/codex"),
ProcessRow(
pid=702,
ppid=701,
command="/Users/me/.local/share/codex/vendor/aarch64-apple-darwin/codex/codex",
),
],
cursor_projects_dir=Path("/does/not/exist"),
)
assert [(record.name, record.pid) for record in records] == [("codex", 702)]
def test_codex_dedupe_runs_after_cursor_terminal_metadata(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
terminal = tmp_path / "project" / "terminals" / "71.txt"
terminal.parent.mkdir(parents=True)
terminal.write_text(
"---\npid: 711\ncwd: /repo\nactive_command: codex\n---\n",
encoding="utf-8",
)
_patch_codex_rollout_owners(monkeypatch, {712})
records = discover_agents(
process_rows=[
ProcessRow(pid=711, ppid=1, command="node /Users/me/.local/bin/codex"),
ProcessRow(
pid=712,
ppid=711,
command="/Users/me/.local/share/codex/vendor/aarch64-apple-darwin/codex/codex",
),
],
cursor_projects_dir=tmp_path,
)
assert [(record.name, record.pid) for record in records] == [("codex", 712)]
def test_discover_agent_processes_deduplicates_codex_wrapper_child_in_all_mode(
monkeypatch: pytest.MonkeyPatch,
) -> None:
rows = [
ProcessRow(pid=10, command="opensre"),
ProcessRow(pid=801, ppid=1, command="node /Users/me/.local/bin/codex"),
ProcessRow(
pid=802,
ppid=801,
command="/Users/me/.local/share/codex/vendor/aarch64-apple-darwin/codex/codex",
),
]
_patch_codex_rollout_owners(monkeypatch, {802})
monkeypatch.setattr(discovery.os, "getpid", lambda: 10)
monkeypatch.setattr(discovery, "_current_process_rows", lambda: rows)
candidates = discovery.discover_agent_processes(include_all=True)
assert [(item.name, item.pid) for item in candidates] == [("codex-802", 802)]
def test_codex_wrapper_native_dedupe_prefers_pid_with_open_rollout(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_patch_codex_rollout_owners(monkeypatch, {901})
records = discover_agents(
process_rows=[
ProcessRow(pid=901, ppid=1, command="node /Users/me/.local/bin/codex"),
ProcessRow(
pid=902,
ppid=901,
command="/Users/me/.local/share/codex/vendor/aarch64-apple-darwin/codex/codex",
),
],
cursor_projects_dir=Path("/does/not/exist"),
)
assert [(record.name, record.pid) for record in records] == [("codex", 901)]
def test_discovers_concurrent_codex_sessions_after_deduping_each_pair(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_patch_codex_rollout_owners(monkeypatch, {1002, 1102})
records = discover_agents(
process_rows=[
ProcessRow(pid=1001, ppid=1, command="node /Users/me/.local/bin/codex"),
ProcessRow(
pid=1002,
ppid=1001,
command="/Users/me/session-a/vendor/aarch64-apple-darwin/codex/codex",
),
ProcessRow(pid=1101, ppid=1, command="node /Users/me/.local/bin/codex"),
ProcessRow(
pid=1102,
ppid=1101,
command="/Users/me/session-b/vendor/aarch64-apple-darwin/codex/codex",
),
],
cursor_projects_dir=Path("/does/not/exist"),
)
assert [(record.name, record.pid) for record in records] == [
("codex", 1002),
("codex", 1102),
]
def test_keeps_independent_codex_processes_that_are_not_wrapper_child_pairs(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_patch_codex_rollout_owners(monkeypatch, set())
records = discover_agents(
process_rows=[
ProcessRow(pid=1201, ppid=1, command="node /Users/me/.local/bin/codex"),
ProcessRow(
pid=1202,
ppid=1,
command="/Users/me/.local/share/codex/vendor/aarch64-apple-darwin/codex/codex",
),
],
cursor_projects_dir=Path("/does/not/exist"),
)
assert [(record.name, record.pid) for record in records] == [
("codex", 1201),
("codex", 1202),
]
def test_registered_records_win_over_discovered_pid(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
registry = AgentRegistry(path=tmp_path / "agents.jsonl")
registry.register(
AgentRecord(
name="manual-claude",
pid=42,
command="custom claude wrapper",
registered_at="2026-05-07T12:00:00+00:00",
)
)
monkeypatch.setattr(
"tools.system.fleet_monitoring.discovery.discover_agents",
lambda: [
AgentRecord(
name="claude-code",
pid=42,
command="claude code",
source="discovered",
)
],
)
records = registered_and_discovered_agents(registry)
assert len(records) == 1
assert records[0].name == "manual-claude"
assert records[0].source == "registered"
def test_registered_and_discovered_agents_returns_sorted_rows(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
registry = AgentRegistry(path=tmp_path / "agents.jsonl")
registry.register(AgentRecord(name="z-manual", pid=20, command="manual"))
monkeypatch.setattr(
"tools.system.fleet_monitoring.discovery.discover_agents",
lambda: [
AgentRecord(name="aider", pid=10, command="aider", source="discovered"),
],
)
records = registered_and_discovered_agents(registry)
assert [(record.name, record.pid) for record in records] == [("aider", 10), ("z-manual", 20)]
class TestClassifyCommandProvider:
def test_native_codex_binary(self) -> None:
assert classify_command_provider("/opt/bin/codex exec") == "codex"
def test_node_launched_codex_js(self) -> None:
assert (
classify_command_provider("node /usr/local/bin/codex.js exec --model gpt-5-codex")
== "codex"
)
def test_node_launched_codex_with_intermediate_flag(self) -> None:
assert classify_command_provider("node --inspect /usr/local/bin/codex.js exec") == "codex"
def test_node_launched_codex_mjs_and_cjs(self) -> None:
assert classify_command_provider("node /opt/codex/dist/codex.mjs") == "codex"
assert classify_command_provider("nodejs /opt/codex/dist/codex.cjs --help") == "codex"
def test_node_with_unrelated_script_is_not_codex(self) -> None:
assert classify_command_provider("node /opt/codex-utils/main.js") is None
assert classify_command_provider("node /opt/app/server.js") is None
def test_claude_argv0(self) -> None:
assert classify_command_provider("claude --dangerously-skip-permissions") == "claude-code"
def test_cursor_extension_substring(self) -> None:
assert (
classify_command_provider(
"/Users/me/.cursor/extensions/anthropic.claude-code/resources/"
"native-binary/claude --output-format stream-json"
)
== "claude-code"
)
def test_aider_argv0(self) -> None:
assert classify_command_provider("aider --model gpt-4o") == "aider"
def test_gemini_argv0(self) -> None:
assert classify_command_provider("gemini chat") == "gemini-cli"
def test_agy_argv0(self) -> None:
assert classify_command_provider("agy -p hello") == "antigravity-cli"
def test_unknown_command_returns_none(self) -> None:
assert classify_command_provider("python -m app.worker") is None
def test_empty_command_returns_none(self) -> None:
assert classify_command_provider("") is None
def test_loose_claude_code_argv_does_not_false_positive(self) -> None:
# Hardened against the old ``"claude" in tokens and "code" in tokens`` check.
assert classify_command_provider("run-tests --model claude --format code --verbose") is None