ps5-remoteplay-mcp 0.1.0__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.
- ps5_remoteplay_mcp-0.1.0/.gitignore +9 -0
- ps5_remoteplay_mcp-0.1.0/AGENTS.md +183 -0
- ps5_remoteplay_mcp-0.1.0/CLAUDE.md +183 -0
- ps5_remoteplay_mcp-0.1.0/LICENSE +674 -0
- ps5_remoteplay_mcp-0.1.0/PKG-INFO +236 -0
- ps5_remoteplay_mcp-0.1.0/README.md +212 -0
- ps5_remoteplay_mcp-0.1.0/pyproject.toml +56 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/__init__.py +1 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/__main__.py +3 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/backend/__init__.py +26 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/backend/base.py +78 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/backend/fake.py +118 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/backend/remoteplay.py +287 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/cli.py +198 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/client.py +153 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/config.py +78 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/console.py +142 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/daemon.py +489 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/engine.py +245 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/fec.py +138 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/frames.py +35 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/gamepad.py +79 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/login_window.py +103 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/mcp_server.py +317 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/pad_bridge.py +128 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/pad_reader.py +94 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/__init__.py +0 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/LICENSE +674 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/NOTICE.md +21 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/__init__.py +3 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/__version__.py +6 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/av.py +345 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/const.py +237 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/controller.py +341 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/crypt.py +519 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/ddp.py +631 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/device.py +582 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/errors.py +45 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/keys.py +1673 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/oauth.py +209 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/profile.py +249 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/protobuf.py +202 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/receiver/__init__.py +378 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/register.py +307 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/session.py +841 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/socket.py +337 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/stream.py +654 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/stream_packets.py +1114 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/takion_pb2.py +137 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/tracker.py +253 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/vendor/pyremoteplay/util.py +188 -0
- ps5_remoteplay_mcp-0.1.0/src/ps5agent/viewer.py +91 -0
- ps5_remoteplay_mcp-0.1.0/tests/__init__.py +0 -0
- ps5_remoteplay_mcp-0.1.0/tests/conftest.py +55 -0
- ps5_remoteplay_mcp-0.1.0/tests/test_console.py +90 -0
- ps5_remoteplay_mcp-0.1.0/tests/test_daemon_mcp.py +223 -0
- ps5_remoteplay_mcp-0.1.0/tests/test_engine.py +128 -0
- ps5_remoteplay_mcp-0.1.0/tests/test_fec_frames.py +58 -0
- ps5_remoteplay_mcp-0.1.0/tests/test_gamepad_priority.py +34 -0
- ps5_remoteplay_mcp-0.1.0/tests/test_hardware.py +66 -0
- ps5_remoteplay_mcp-0.1.0/tests/test_pad_bridge.py +148 -0
- ps5_remoteplay_mcp-0.1.0/tests/test_pad_expiry.py +23 -0
- ps5_remoteplay_mcp-0.1.0/tests/test_remoteplay_backend.py +79 -0
- ps5_remoteplay_mcp-0.1.0/tests/test_session_stability.py +211 -0
- ps5_remoteplay_mcp-0.1.0/tests/test_viewer.py +79 -0
- ps5_remoteplay_mcp-0.1.0/tools/gate.py +17 -0
- ps5_remoteplay_mcp-0.1.0/tools/spike.py +241 -0
- ps5_remoteplay_mcp-0.1.0/uv.lock +2117 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# PS5 Remote Play MCP
|
|
2
|
+
MCP server + daemon: an LLM agent (Claude Code, Codex) sees the screen of a stock PS5 and drives it like a gamepad over Remote Play on the LAN.
|
|
3
|
+
|
|
4
|
+
## What this project is
|
|
5
|
+
- **Nature**: not yet agreed โ until the owner says otherwise, full production discipline applies.
|
|
6
|
+
- **Graph**: `@t6ylm7b6cpmacd2lohhdpkka/ps5-agent-bridge` (`r853`) โ every session starts with `iskron_orient` here. The graph is private to the owner; external contributors skip every graph step in this file.
|
|
7
|
+
- **Focus holon**: #1 ยซ๐ฎ ะะพัั ะฐะณะตะฝัะฐ ะบ PS5ยป.
|
|
8
|
+
- **Agent role**: #3 ยซ๐ค ะะณะตะฝั-ัะฐะทัะฐะฑะพััะธะบ ะผะพััะฐยป โ adhikarin, steward of the focus holon. Your inbox: `iskron_orient(focus="3")` at session start.
|
|
9
|
+
- **Owner role**: #2 ยซ๐ค ะะปะฐะดะตะปะตั ะผะพััะฐ ะบ PS5ยป (svatantra ไธป) โ out-of-mandate questions go here as `posed_to` vimarshas.
|
|
10
|
+
- **Stack**: Python โฅ3.12, uv, MCP Python SDK 2.x (`MCPServer`, stdio), aiohttp (daemon API), httpx (client), vendored + patched pyremoteplay 0.7.6 (GPL-3.0), pywebview (sign-in window); fallback backend chiaki-ng not implemented; dev host โ Windows 11. Shipped to users as a PyPI package run by `uvx ps5-remoteplay-mcp`. License GPL-3.0-only.
|
|
11
|
+
- **Production statement**: not yet agreed โ run the interview (say iskronify) before relying on a cost of failure. Fixed by the owner's handoff: under no failure may a button stay pressed; Remote Play credentials never appear in logs or tool responses; the daemon API listens on 127.0.0.1 only.
|
|
12
|
+
|
|
13
|
+
## Persistence rules
|
|
14
|
+
State lives in the **repo** or in the **graph** โ nowhere else. The harness's built-in memory (whatever it is called โ per-project memory dir, conversation summaries, `/tmp`, machine-local files) is **forbidden entirely, not by category**: nothing goes there โ no project fact, no user preference, no working-style note. (why: local memory is invisible to every other agent and machine, so it drifts silently and breaks the reproducibility that makes a second machine or agent possible.)
|
|
15
|
+
- **Repo**: code, configs, conventions, code gotchas, branch state โ the artifact itself.
|
|
16
|
+
- **Graph**: methodology, design decisions, open questions (vimarshas), plans, handoffs, lessons, hints โ the thinking around it. Do not retell graph content in the repo; link the vimarsha or holon.
|
|
17
|
+
- **Fetch state; never reconstruct it from memory.** "We decidedโฆ" without a source? Stop and read the graph or repo before acting.
|
|
18
|
+
- **External design/spec files are drafts to intake**, not the record: the graph holds decisions; such a file is a view of them.
|
|
19
|
+
- **There are three named parking spots.** The undisciplined surface is free, the disciplined one costs, and under pressure laziness always finds a parking spot: the prose of this file; a sinn phenomenon for something that acts; a lone `context` arrow that silences the detector. Finish line for a write: **a node is not written until you name what pulls it** โ which kriya breaks if the node disappears? None โ it is parking, not a record. Read the fork BEFORE writing, not from a warning after (that only chronicles): a door enters the graph not as a thing nor as the meaning "door", but as **the place where a doer acts** โ upadhi of the kriya that passes through it.
|
|
20
|
+
- **This overrides the harness's own memory instruction**, which invites a `project` category and will keep inviting โ the pull is strongest exactly when something feels worth saving and this file is long out of context. Route instead, always โ and **before finishing, check that every durable fact from the user's context is persisted by this routing: an unpersisted fact is a task failure, not a nicety** โ asking **whose fact is this?**
|
|
21
|
+
Repo convention, code fact, this project's procedures, **its servers and deploy pipeline, its dated obligations** โ this file / docs / code, or a node in this repo's graph; work state, decision, open question โ a vimarsha in the graph.
|
|
22
|
+
A dated obligation (renewal, deadline) is a node carrying the date in `attrs`: a mention in one README loses it. Project rules never land in another project's or the user's graph.
|
|
23
|
+
A fact that is **the user's own** and serves no single project โ their machines, deadlines, people, cross-project lessons โ their personal graph `@t6ylm7b6cpmacd2lohhdpkka/mind` (**minding**), written when learned, not at session end; a fact about **another** project โ that project's graph. Standing preferences ("how to work with me") are instructions, not facts: project-scoped โ this AGENTS.md, cross-project โ the user's global instructions file; never the graph or the memory dir.
|
|
24
|
+
The memory dir keeps no residue โ "it's just a preference" is how the category comes back.
|
|
25
|
+
- The local memory dir is **evacuated and frozen**: `MEMORY.md` holds a one-line prohibition stub pointing here, and a `PreToolUse` memory-guard blocks writes there (exit 2). Whatever accumulated moves to its real home before freezing โ nothing is deleted unread.
|
|
26
|
+
|
|
27
|
+
## Session lifecycle
|
|
28
|
+
Graph = the work (structure, open questions, what's next). Git = how we got here (SHAs, branches, PRs). **Git references never enter the graph** โ no SHAs, branch names, PR numbers or "shipped/merged" in nodes (they rot on rebase).
|
|
29
|
+
- **Session start:** enter via the `entry` skill โ it ends in signs, not reading; here only the addresses: graph, focus holon, agent role and owner role from "What this project is". Address the owner by their role's seq, not the `me` sentinel.
|
|
30
|
+
- **Starting work: graph first, then project, then code.** A substantive task enters in three beats: (1) **graph reconnaissance** โ what is recorded about the place of change, which vimarshas are open, what was decided and rejected, **and what is recorded about external surfaces the work will touch** (see "External surfaces"); driven by `entry`; (2) **integration field** โ from the focus holon, steward role and the changed nodes, derive consumers, effects, neighbouring holons and their doers via `integrity`; walk relays with `lens="trace"`, design missing kriyas and phenomena, stitch gaps via `weaving`; (3) **design** the change (`design`) โ only then code. Skipping gets costlier left to right. The one exception is **explicit**: the human said "just work" or named another protocol โ then go to code and pay the reconnaissance debt at the reconcile beat at task end. Silence is not "just work".
|
|
31
|
+
- **A decision is recorded when made, not when executed.** Wherever it comes from โ chat, a socket message, two agents agreeing โ it stands in the graph **immediately**, with the modes it really has now: epistemic no higher than `anumita`, ontic `anagata`, volitive `chanda`/`adhimoksha`. Record who decided and what counts as done. A changed situation is reflected just as immediately. (why: a decision left in the conversation dies with it; a late record is what you remember as decided, which is not the same.)
|
|
32
|
+
- **Every task is described before it starts โ and recorded as what it is.** Before the first change outside the graph the work stands in the graph: a one-off deed as an anga vimarsha on the transformation it moves (a large one โ its own bianhua), with before/after in its body; **a kriya only for a repeatable transition** whose every run eats the same ahara and produces the same utpatti. Test: ask the "kriya" what it eats and produces on its *next* run โ no answer means it is a task. While work runs, the graph moves with it. On **merge** (not push) modes switch to what the merge made true. Then **reality-audit**: check the claim against the deployed artifact, not the diff.
|
|
33
|
+
- **Every merge โ update the graph.** A push that only opened or updated a PR shipped nothing. The post-merge sequence hangs on the merge **event**, never on a lull. When merged, every step below is mandatory:
|
|
34
|
+
- **Reconcile with reality.** Record what positions the change in the target system: architecture, module APIs, delivery, user experience, integration. Pure repo mechanics stay in git. **Updating the graph means weaving, not editing prose.** Zero nodes and zero arrows after a substantive wave is an unfinished step โ say so plainly if there truly was nothing, and why.
|
|
35
|
+
- **Advance the map.** Keep open work tied by `anga` to the transformation it moves. A thin `genre=hint` seed only for what the graph cannot carry.
|
|
36
|
+
- **Close by axis, not by a feeling of "done".** Record the answer as `addressed_by` to the node carrying it; release (`visarjana`) is a separate volitional act. Release yourself when three things agree: the answer is a node in the graph; the repo shows it; reality shows it as far as reachable โ where unreachable, the user's word stands in and you asked for it. Otherwise prepare the release and present it to the owner.
|
|
37
|
+
- **Sweep the shipped holon.** A push implementing designed nodes switches their modes (anagataโvartamana, kalpitaโpratyakshita) across the *whole* designed holon, and closes the design vimarshas it resolved.
|
|
38
|
+
- **Work the inbox.** `posed_to` questions the work answered close by the rule above; stale ones are parked or grouped.
|
|
39
|
+
- **Reconcile code and graph.** End of every substantive task is the `reconcile` beat: nodes vs code, code vs graph, discarded and rejected alternatives recorded and referenceable. Remaining debts as vimarshas, not a story.
|
|
40
|
+
- **Vocabulary pass.** Reread what you are about to land for borrowed project-management words (ticket, backlog, sprint, epic, story, done, blocker, committed). Do **not** substitute them yourself: name each to the user and ask what the project calls it.
|
|
41
|
+
|
|
42
|
+
`weaving` / `design` carry the *how*.
|
|
43
|
+
- **Design completeness:** a design is not *done* until its decisions, risks and lifecycle are in the graph. A design/spec file written by another suite is a draft view: intake it **in the same session**. Working autonomously: land decisions and risks now; propose a bianhua with its telos marked for owner confirmation.
|
|
44
|
+
- **Execution suites run execution.** Planning, TDD, debugging, verification, review belong to the installed execution suite; the graph carries only the memory/design plane. Decisions and risks born in execution still land in the graph **before session end**.
|
|
45
|
+
- **A claim you made is not a claim you accept.** Behavioural claims are closed by the verdict of a cold `verifier` subagent, never by your rereading. Give it the claim, the carrier and the falsifier from *Reality* and **wait for the verdict**. Where the repo has no verifier role, observe the carrier yourself; never close a behavioural claim from the source that should have produced it.
|
|
46
|
+
- **Hook merging.** Where the harness has a hooks file, entries of different suites coexist โ add alongside, never overwrite.
|
|
47
|
+
- These reminders are automated where the harness can: session-start hook, post-`git push` hook, memory-write hook. Claude Code: `.claude/settings.json` โ not wired yet (deferred by quick mode).
|
|
48
|
+
- **Keep this file honest.** It is generated by `iskronify` and stamped at the bottom with its contract. Rerun `iskronify` when the installed skill announces a newer contract, or when the sources it derives from moved after the stamp: `git log -1 --format=%cd -- pyproject.toml uv.lock .github/` against the stamp date. The installed contract number is the first word of the `iskronify` skill description. (why: a stale AGENTS.md is read with full confidence every session.)
|
|
49
|
+
- **Keep your toolchain fresh.** Updates are **on by default**: take them as the channel delivers, do not pin.
|
|
50
|
+
|
|
51
|
+
### Stage self-check
|
|
52
|
+
Gate green and a coherent stage finished โ PR opened or updated, or you are about to touch nodes beyond those you started with โ reread your branch diff against trunk for: bugs, fragile spots, weak error handling, DRY/SOLID violations, repeated patterns, missing or useless tests, files over 150 lines and god units mixing many concerns. Fix in the **same branch** and push again โ or say plainly nothing surfaced. Do not invent findings. **Per stage, not only at the end.**
|
|
53
|
+
|
|
54
|
+
### Cold stage review
|
|
55
|
+
**Self-check does not replace a cold review.** After the self-check of an opened/updated PR or a finished large stage, **open a review by a top-tier subagent** (`reviewer` role) where the harness can. If it cannot, say plainly there was no cold review. **Only the push has a watchman.**
|
|
56
|
+
|
|
57
|
+
The reviewer's field is four things, all mandatory:
|
|
58
|
+
- **branch diff against trunk** โ the whole branch, not the last commit;
|
|
59
|
+
- **the repository itself** โ so it can open neighbouring code;
|
|
60
|
+
- **the focus holon and its steward role** from "What this project is";
|
|
61
|
+
- **references to graph nodes the diff touches** โ not a retelling of them.
|
|
62
|
+
|
|
63
|
+
The reviewer runs `integrity` read-only from the given nodes and, besides code findings, returns an **integration report**: what is affected, which relays pass or break, whether neighbours are ready by available evidence, open questions, whom to wake via `collaborate`. Unknown stays `unknown`. Missing references or a broken trace โ `NEEDS_CONTEXT` naming the gap: a graph defect. Fix what comes back in the same branch; reject a finding only with a recorded "why".
|
|
64
|
+
|
|
65
|
+
### Branch discipline
|
|
66
|
+
One branch until its merge โ commit follow-ups into it. After merge, clean up locally:
|
|
67
|
+
1. `git checkout main && git pull`.
|
|
68
|
+
2. Delete the merged branch (`git branch -d <name>`); prune others already in `main`.
|
|
69
|
+
3. Update the graph: weave the shipped state into the holon, close what the merge resolved (`weaving`).
|
|
70
|
+
4. Confirm the cleanup before the next task.
|
|
71
|
+
|
|
72
|
+
## Working principles
|
|
73
|
+
1. **Think before coding.** Name assumptions; when unsure, ask โ naming *what exactly* is unclear. **Ask the human in text**; never use an interactive option-picker. Surface competing readings; push back on a simpler path or a false premise. Check repo + graph before writing; fetch, don't recall. Touch the live system before trusting a type, name or doc. Out-of-bounds questions become vimarshas `posed_to` the owner's ไธป role.
|
|
74
|
+
2. **Simplicity first.** Minimum code for the task. No speculative features, no abstractions for one-off code, no handling of impossible errors. Validate at boundaries; trust internal invariants.
|
|
75
|
+
3. **Stay inside the repo boundary.** A change belonging to another holon is recorded as a vimarsha on that holon's node in its graph.
|
|
76
|
+
4. **A second implementation is an event to report.** About to write what already exists? Derive both places via `integrity`, name them to the user, and propose reunion or a named deliberate fork.
|
|
77
|
+
5. **Surgical changes.** Touch only what the task needs. Keep existing style; the linter is authoritative. Delete only dead code your change created.
|
|
78
|
+
6. **Goal-driven execution.** Tasks โ verifiable goals. Bugs: pin with a failing test before the patch. Multi-step: plan as `step โ check` pairs. Runtimes: verify in the real environment โ *Reality* names this project's carriers. Name the falsifier before looking and observe the carrier, not its source.
|
|
79
|
+
7. **Read before answering an open question.** Discuss / think through / investigate / design / plan / analyse โ answer from recorded thinking, not training data: query the graph several ways first.
|
|
80
|
+
8. **Think in the graph, speak the project's language.** The graph's structural vocabulary is for reasoning; it does not appear in what you say to the user until they use it first. Talk *about* work in plain words: question, change, what is open, what it unblocks.
|
|
81
|
+
|
|
82
|
+
## Integration field โ from the graph only
|
|
83
|
+
The focus holon and steward role are the only permanent root of traversal. AGENTS.md keeps **no list** of shared surfaces, consumers or dependencies, and does not ask the human for one. For every change, name the graph nodes whose embodiment is in the diff and run `integrity`; trace phenomena both ways with `iskron_orient(lens="trace")`. A dependency the traversal misses is a model defect: design the missing kriyas and phenomena, stitch edges via `weaving`.
|
|
84
|
+
|
|
85
|
+
## External surfaces โ what you use and do not own
|
|
86
|
+
Someone else's API, SDK, CLI, protocol. Agents **guess** them: memory from training is indistinguishable from knowledge from the inside, and a field that does not exist looks in code exactly like one that does.
|
|
87
|
+
- **Before working, record the part of the surface you will touch** as a graph node, with the version you looked at.
|
|
88
|
+
- **Source by seniority:** your own observation (calling it, `--help` of the installed binary, types of the installed package) beats docs; docs beat memory; **memory is not a source at all**. Write epistemics honestly: `pratyakshita` only for what you observed.
|
|
89
|
+
- **Weave the link:** the surface node is `upadhi` of the kriya acting through it.
|
|
90
|
+
- **Keep in step:** found a mismatch or a version bump โ fix the node in the same move.
|
|
91
|
+
- **The reference works both ways:** source touching an external surface carries `(graph โฆ, node #N)` โ and you **read that node before working**.
|
|
92
|
+
|
|
93
|
+
## Reality โ what claims are checked against
|
|
94
|
+
Not fully settled: run the interview (say iskronify). Seeded by use:
|
|
95
|
+
|
|
96
|
+
| Claim class | Canonical carrier | How to observe | Who can |
|
|
97
|
+
|---|---|---|---|
|
|
98
|
+
| Session / input / frames work | a live PS5 on the host's LAN, through the daemon | `PS5_HOST=<IP> uv run pytest -m hardware`; `uv run ps5agent snapshot` and look at the image | agent (console on the host LAN) |
|
|
99
|
+
| An agent drives the console over MCP | the MCP server over stdio, launched as Claude Code launches it | stdio client `mcp.Client(StdioServerParameters(uv run ps5-remoteplay-mcp))` โ `call_tool` | agent |
|
|
100
|
+
| Users can install it | the built wheel in a clean `uvx --isolated` env (py3.12โ3.14) | `uv build`; `uvx --isolated --python 3.13 --from dist/<wheel> ps5-remoteplay-mcp --help` | agent |
|
|
101
|
+
| Sign-in window links a console | pywebview window + a real PSN account + PIN on the TV | `uv run ps5agent setup --force` | user (PSN password, TV) |
|
|
102
|
+
| Latency | a live PS5 on the home screen | `uv run ps5agent latency` | agent |
|
|
103
|
+
|
|
104
|
+
**Ceiling**: behaviour inside a running game (analog L2/R2, sticks) needs a game running and the owner's go-ahead; Wi-Fi loss was verified only by artificial packet drops.
|
|
105
|
+
|
|
106
|
+
## Graph โ repo: where things live
|
|
107
|
+
| Concern | Repo | Graph |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| Code, configs, lockfiles | โ | |
|
|
110
|
+
| Commands, conventions, gotchas, stack | โ (AGENTS.md) | |
|
|
111
|
+
| Branch state, what is in flight | git + PR body | โ (`genre=hint` โ work without a PR) |
|
|
112
|
+
| Methodology, ontology | | โ |
|
|
113
|
+
| Design decisions, open questions | | โ (vimarshas) |
|
|
114
|
+
| Plans, task lists, session handoffs | | โ |
|
|
115
|
+
| Lessons | | โ (graph first; thin `genre=hint` for the off-map remainder) |
|
|
116
|
+
| Commit history, PRs, SHAs | git | (never in the graph) |
|
|
117
|
+
|
|
118
|
+
**No `HANDOVER.md` โ a decision, not an omission.** Branch and in-flight state: `git branch` / `log` and the open PR; how claims are checked: *Reality*; why and what is open: the graph; work without a PR yet: a `genre=hint` seed. Branch state is told **in the PR body**.
|
|
119
|
+
|
|
120
|
+
## Commands
|
|
121
|
+
| What | Command |
|
|
122
|
+
|---|---|
|
|
123
|
+
| Gate (ruff check + format --check + pytest) | `uv run python tools/gate.py` |
|
|
124
|
+
| Hardware test (needs the console) | `PS5_HOST=<IP> uv run pytest -m hardware` |
|
|
125
|
+
| Daemon in the foreground | `PS5_HOST=<IP> uv run ps5agent daemon` |
|
|
126
|
+
| Status / snapshot / press / latency | `uv run ps5agent status \| snapshot -o f.jpg \| press cross \| latency` |
|
|
127
|
+
| Picture window for the human (or `PS5_VIEWER=1` at daemon start) | `uv run ps5agent view` |
|
|
128
|
+
| Stop the daemon | `uv run ps5agent stop` |
|
|
129
|
+
| Link to the console (sign-in window + PIN) | `uv run ps5agent setup` (`--no-gui` for terminal) |
|
|
130
|
+
| MCP server (agents launch it; no args = MCP, args = CLI) | `uv run ps5-remoteplay-mcp` |
|
|
131
|
+
| Build / try as users get it | `uv build` โ `uvx --isolated --from dist/<wheel> ps5-remoteplay-mcp --help` |
|
|
132
|
+
| Publish (owner only, needs a PyPI token) | `uv publish` |
|
|
133
|
+
| Format | `uv run ruff format src tests tools/gate.py` |
|
|
134
|
+
|
|
135
|
+
## Project structure
|
|
136
|
+
- `src/ps5agent/vendor/pyremoteplay/` โ vendored protocol library, patched in place; every change listed in its `NOTICE.md`; excluded from ruff.
|
|
137
|
+
- `src/ps5agent/backend/` โ `ConsoleBackend` (base), `remoteplay.py` (session over the vendored library), `fake.py` (tests, `PS5_BACKEND=fake`).
|
|
138
|
+
- `src/ps5agent/console.py` โ discovery, saved console (`console.json`), linking (PSN redirect โ user, PIN โ registration).
|
|
139
|
+
- `src/ps5agent/login_window.py` โ pywebview window: Sony sign-in, catches the redirect, PIN form.
|
|
140
|
+
- `src/ps5agent/engine.py` โ input: serialization, watchdog, release guarantees.
|
|
141
|
+
- `src/ps5agent/daemon.py` โ session, reconnect, loopback HTTP API.
|
|
142
|
+
- `src/ps5agent/viewer.py` โ picture window for the human (separate process reading `/frame`).
|
|
143
|
+
- `src/ps5agent/client.py` โ daemon client + autostart; `mcp_server.py` โ tools; `cli.py` โ `ps5agent`.
|
|
144
|
+
- `src/ps5agent/fec.py` โ `pyjerasure` replacement (Cauchy Reed-Solomon, GF(2^8)).
|
|
145
|
+
- `tests/` โ unit + MCP contract tests on fake; `test_hardware.py` โ only with `PS5_HOST`.
|
|
146
|
+
- `tools/gate.py` โ the gate; `tools/spike.py` โ one-off phase-0 spike (excluded from lint).
|
|
147
|
+
|
|
148
|
+
## Code conventions
|
|
149
|
+
- **Meaning lives in the graph, code references it.** A comment carrying the rationale of a decision, discarded alternatives or integration design is a graph node living away from home: move the meaning to the graph and leave a reference in code โ "(graph `ps5-agent-bridge`, node #N)". Boundary: **the mechanics of a step โ in words in the comment; meaning, rationale, integration โ in the graph.** The reference works both ways: after referencing, check the node really says it. This repo is public: external contributors have no graph access, so a graph reference never replaces the one-line "why" a reader needs to change the code safely.
|
|
150
|
+
- **External APIs (pyremoteplay, chiaki-ng, MCP SDK)**: class and method names come from the installed package and its source, never from memory.
|
|
151
|
+
- **Test discipline**: unit tests on `FakeBackend`, an MCP contract test per tool, hardware tests `@pytest.mark.hardware` (only with `PS5_HOST`). Check the "nothing held" invariant (`FakeBackend.idle`) in every test of a new input path.
|
|
152
|
+
- **Gotchas**:
|
|
153
|
+
- pyremoteplay is vendored, not a dependency: the PyPI release pulls `netifaces` (no Windows wheels โ breaks `uvx`) and pins old `pyee`/`av`. Fix protocol bugs in `src/ps5agent/vendor/pyremoteplay` directly and add a line to its `NOTICE.md` (GPL requires marking changes). Run the hardware test after any change there.
|
|
154
|
+
- The vendored library's DEBUG logs print the handshake key and ECDH secret โ `console.prepare` keeps logger `ps5agent.vendor.pyremoteplay` at INFO or above. Call `console.prepare(cfg)` before using the library (it also points profiles at `~/.config/ps5-agent/profiles.json`).
|
|
155
|
+
- Audio is disabled in the receiver (decoder and resampler are skipped).
|
|
156
|
+
- Sony redirects only to `remoteplay.dl.playstation.net` after sign-in: a normal browser cannot hand the code back. The pywebview window catches it from its own navigation. pywebview needs the main thread of its own process: the MCP `setup` tool spawns `ps5agent setup --gui-only` with stdin=DEVNULL; exit code 3 = no GUI โ the tool returns the chat fallback (link โ `setup(redirect_url)` โ `setup(pin)`).
|
|
157
|
+
- `[WinError 10048]` on DDP port 9303 in the log is harmless: two processes discovering at once; the library falls back to another port.
|
|
158
|
+
- Windows: after a pyremoteplay session, closing the asyncio loop hangs on a UDP operation โ processes holding a session exit via `os._exit` after cleanup (`daemon.run`).
|
|
159
|
+
- The vendored library sends UDP from other threads (controller worker, AV executor); asyncio transports are loop-thread only, and a cross-thread send kills the stream on Windows (`Fatal write error on datagram transport`). `RPStream.Protocol.sendto` marshals to the loop โ any new send path must go through it.
|
|
160
|
+
- Two `python.exe` per daemon or MCP server on Windows is one process: the venv `python.exe` is a launcher that starts the real interpreter as a child. The daemon port (9395) already makes a second daemon fail to bind.
|
|
161
|
+
- The daemon is spawned with `CREATE_BREAKAWAY_FROM_JOB`: MCP clients on Windows kill the server's process tree on exit. Without the flag the daemon dies with every MCP server.
|
|
162
|
+
- aiohttp cancels a handler when the client leaves only with `handler_cancellation=True` (`daemon.make_runner`) โ otherwise a hold runs to its end.
|
|
163
|
+
- A killed daemon leaves a zombie session on the console for ~30โ40 s: "Another Remote Play session is connected to host". The daemon reconnects by itself on backoff.
|
|
164
|
+
- mcp 2.x: `MCPServer` instead of `FastMCP`; result fields are snake_case (`is_error`, `mime_type`); errors meant for the model are `ToolError` (otherwise the text is masked); enter and exit an in-process `Client` in the test body, not in an async fixture (anyio cancel scope).
|
|
165
|
+
- The Create button is `SHARE` in pyremoteplay.
|
|
166
|
+
- The console swallows presses during screen-transition animations โ not an input bug.
|
|
167
|
+
|
|
168
|
+
## What to update when
|
|
169
|
+
- `AGENTS.md` โ inverted default: **if it can be learned by reading a graph node, it is not here.** This file holds only what is needed BEFORE the agent reaches the graph: commands, the entry into orientation, code invariants the linter cannot express, forks that must stop you before acting โ and is updated when THAT changes. Cleaning up written prose is a reconcile beat with transfer into carrying nodes, never deletion.
|
|
170
|
+
- `CLAUDE.md` โ regenerate the copy on every change to `AGENTS.md`; it is a duplicate, never edited directly.
|
|
171
|
+
- The project graph โ every push (see "Session lifecycle").
|
|
172
|
+
|
|
173
|
+
## Git workflow
|
|
174
|
+
- **Conventional commits** (`feat:`/`fix:`/`chore:`/`refactor:`/`docs:`/`test:`). Branches `feat/โฆ`, `fix/โฆ`, `chore/โฆ`.
|
|
175
|
+
- **Merge without a PR โ always** (owner's standing instruction): when the owner says to ship, merge the branch into `main` locally (`git merge --no-ff`) and push `main`; do not open a PR. The cold stage review and the post-merge graph steps still apply.
|
|
176
|
+
- **No AI attribution**: no `Co-Authored-By` trailers for Claude or other agents in commits, no "Generated with" lines in PR bodies โ the repository lists the owner as its only contributor (owner's decision).
|
|
177
|
+
- **Forge**: GitHub, `khodossss/ps5-remoteplay-mcp` (public); CLI `gh` โ observe with `gh pr list`, `gh pr checks <n>`.
|
|
178
|
+
- **Local gate โ one call**: `uv run python tools/gate.py`; call it by name, never assemble the steps by hand. No CI yet.
|
|
179
|
+
- **Definition of done**: not yet agreed.
|
|
180
|
+
- **Never** `--no-verify`, `--force`, `--no-gpg-sign`, `git reset --hard` without an explicit instruction from the user.
|
|
181
|
+
|
|
182
|
+
*(iskronify: contract `6`, stamp `2026-10-12` โ rerun when the installed iskronify description names a
|
|
183
|
+
higher contract or when the sources this file is derived from moved after this date.)*
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# PS5 Remote Play MCP
|
|
2
|
+
MCP server + daemon: an LLM agent (Claude Code, Codex) sees the screen of a stock PS5 and drives it like a gamepad over Remote Play on the LAN.
|
|
3
|
+
|
|
4
|
+
## What this project is
|
|
5
|
+
- **Nature**: not yet agreed โ until the owner says otherwise, full production discipline applies.
|
|
6
|
+
- **Graph**: `@t6ylm7b6cpmacd2lohhdpkka/ps5-agent-bridge` (`r853`) โ every session starts with `iskron_orient` here. The graph is private to the owner; external contributors skip every graph step in this file.
|
|
7
|
+
- **Focus holon**: #1 ยซ๐ฎ ะะพัั ะฐะณะตะฝัะฐ ะบ PS5ยป.
|
|
8
|
+
- **Agent role**: #3 ยซ๐ค ะะณะตะฝั-ัะฐะทัะฐะฑะพััะธะบ ะผะพััะฐยป โ adhikarin, steward of the focus holon. Your inbox: `iskron_orient(focus="3")` at session start.
|
|
9
|
+
- **Owner role**: #2 ยซ๐ค ะะปะฐะดะตะปะตั ะผะพััะฐ ะบ PS5ยป (svatantra ไธป) โ out-of-mandate questions go here as `posed_to` vimarshas.
|
|
10
|
+
- **Stack**: Python โฅ3.12, uv, MCP Python SDK 2.x (`MCPServer`, stdio), aiohttp (daemon API), httpx (client), vendored + patched pyremoteplay 0.7.6 (GPL-3.0), pywebview (sign-in window); fallback backend chiaki-ng not implemented; dev host โ Windows 11. Shipped to users as a PyPI package run by `uvx ps5-remoteplay-mcp`. License GPL-3.0-only.
|
|
11
|
+
- **Production statement**: not yet agreed โ run the interview (say iskronify) before relying on a cost of failure. Fixed by the owner's handoff: under no failure may a button stay pressed; Remote Play credentials never appear in logs or tool responses; the daemon API listens on 127.0.0.1 only.
|
|
12
|
+
|
|
13
|
+
## Persistence rules
|
|
14
|
+
State lives in the **repo** or in the **graph** โ nowhere else. The harness's built-in memory (whatever it is called โ per-project memory dir, conversation summaries, `/tmp`, machine-local files) is **forbidden entirely, not by category**: nothing goes there โ no project fact, no user preference, no working-style note. (why: local memory is invisible to every other agent and machine, so it drifts silently and breaks the reproducibility that makes a second machine or agent possible.)
|
|
15
|
+
- **Repo**: code, configs, conventions, code gotchas, branch state โ the artifact itself.
|
|
16
|
+
- **Graph**: methodology, design decisions, open questions (vimarshas), plans, handoffs, lessons, hints โ the thinking around it. Do not retell graph content in the repo; link the vimarsha or holon.
|
|
17
|
+
- **Fetch state; never reconstruct it from memory.** "We decidedโฆ" without a source? Stop and read the graph or repo before acting.
|
|
18
|
+
- **External design/spec files are drafts to intake**, not the record: the graph holds decisions; such a file is a view of them.
|
|
19
|
+
- **There are three named parking spots.** The undisciplined surface is free, the disciplined one costs, and under pressure laziness always finds a parking spot: the prose of this file; a sinn phenomenon for something that acts; a lone `context` arrow that silences the detector. Finish line for a write: **a node is not written until you name what pulls it** โ which kriya breaks if the node disappears? None โ it is parking, not a record. Read the fork BEFORE writing, not from a warning after (that only chronicles): a door enters the graph not as a thing nor as the meaning "door", but as **the place where a doer acts** โ upadhi of the kriya that passes through it.
|
|
20
|
+
- **This overrides the harness's own memory instruction**, which invites a `project` category and will keep inviting โ the pull is strongest exactly when something feels worth saving and this file is long out of context. Route instead, always โ and **before finishing, check that every durable fact from the user's context is persisted by this routing: an unpersisted fact is a task failure, not a nicety** โ asking **whose fact is this?**
|
|
21
|
+
Repo convention, code fact, this project's procedures, **its servers and deploy pipeline, its dated obligations** โ this file / docs / code, or a node in this repo's graph; work state, decision, open question โ a vimarsha in the graph.
|
|
22
|
+
A dated obligation (renewal, deadline) is a node carrying the date in `attrs`: a mention in one README loses it. Project rules never land in another project's or the user's graph.
|
|
23
|
+
A fact that is **the user's own** and serves no single project โ their machines, deadlines, people, cross-project lessons โ their personal graph `@t6ylm7b6cpmacd2lohhdpkka/mind` (**minding**), written when learned, not at session end; a fact about **another** project โ that project's graph. Standing preferences ("how to work with me") are instructions, not facts: project-scoped โ this AGENTS.md, cross-project โ the user's global instructions file; never the graph or the memory dir.
|
|
24
|
+
The memory dir keeps no residue โ "it's just a preference" is how the category comes back.
|
|
25
|
+
- The local memory dir is **evacuated and frozen**: `MEMORY.md` holds a one-line prohibition stub pointing here, and a `PreToolUse` memory-guard blocks writes there (exit 2). Whatever accumulated moves to its real home before freezing โ nothing is deleted unread.
|
|
26
|
+
|
|
27
|
+
## Session lifecycle
|
|
28
|
+
Graph = the work (structure, open questions, what's next). Git = how we got here (SHAs, branches, PRs). **Git references never enter the graph** โ no SHAs, branch names, PR numbers or "shipped/merged" in nodes (they rot on rebase).
|
|
29
|
+
- **Session start:** enter via the `entry` skill โ it ends in signs, not reading; here only the addresses: graph, focus holon, agent role and owner role from "What this project is". Address the owner by their role's seq, not the `me` sentinel.
|
|
30
|
+
- **Starting work: graph first, then project, then code.** A substantive task enters in three beats: (1) **graph reconnaissance** โ what is recorded about the place of change, which vimarshas are open, what was decided and rejected, **and what is recorded about external surfaces the work will touch** (see "External surfaces"); driven by `entry`; (2) **integration field** โ from the focus holon, steward role and the changed nodes, derive consumers, effects, neighbouring holons and their doers via `integrity`; walk relays with `lens="trace"`, design missing kriyas and phenomena, stitch gaps via `weaving`; (3) **design** the change (`design`) โ only then code. Skipping gets costlier left to right. The one exception is **explicit**: the human said "just work" or named another protocol โ then go to code and pay the reconnaissance debt at the reconcile beat at task end. Silence is not "just work".
|
|
31
|
+
- **A decision is recorded when made, not when executed.** Wherever it comes from โ chat, a socket message, two agents agreeing โ it stands in the graph **immediately**, with the modes it really has now: epistemic no higher than `anumita`, ontic `anagata`, volitive `chanda`/`adhimoksha`. Record who decided and what counts as done. A changed situation is reflected just as immediately. (why: a decision left in the conversation dies with it; a late record is what you remember as decided, which is not the same.)
|
|
32
|
+
- **Every task is described before it starts โ and recorded as what it is.** Before the first change outside the graph the work stands in the graph: a one-off deed as an anga vimarsha on the transformation it moves (a large one โ its own bianhua), with before/after in its body; **a kriya only for a repeatable transition** whose every run eats the same ahara and produces the same utpatti. Test: ask the "kriya" what it eats and produces on its *next* run โ no answer means it is a task. While work runs, the graph moves with it. On **merge** (not push) modes switch to what the merge made true. Then **reality-audit**: check the claim against the deployed artifact, not the diff.
|
|
33
|
+
- **Every merge โ update the graph.** A push that only opened or updated a PR shipped nothing. The post-merge sequence hangs on the merge **event**, never on a lull. When merged, every step below is mandatory:
|
|
34
|
+
- **Reconcile with reality.** Record what positions the change in the target system: architecture, module APIs, delivery, user experience, integration. Pure repo mechanics stay in git. **Updating the graph means weaving, not editing prose.** Zero nodes and zero arrows after a substantive wave is an unfinished step โ say so plainly if there truly was nothing, and why.
|
|
35
|
+
- **Advance the map.** Keep open work tied by `anga` to the transformation it moves. A thin `genre=hint` seed only for what the graph cannot carry.
|
|
36
|
+
- **Close by axis, not by a feeling of "done".** Record the answer as `addressed_by` to the node carrying it; release (`visarjana`) is a separate volitional act. Release yourself when three things agree: the answer is a node in the graph; the repo shows it; reality shows it as far as reachable โ where unreachable, the user's word stands in and you asked for it. Otherwise prepare the release and present it to the owner.
|
|
37
|
+
- **Sweep the shipped holon.** A push implementing designed nodes switches their modes (anagataโvartamana, kalpitaโpratyakshita) across the *whole* designed holon, and closes the design vimarshas it resolved.
|
|
38
|
+
- **Work the inbox.** `posed_to` questions the work answered close by the rule above; stale ones are parked or grouped.
|
|
39
|
+
- **Reconcile code and graph.** End of every substantive task is the `reconcile` beat: nodes vs code, code vs graph, discarded and rejected alternatives recorded and referenceable. Remaining debts as vimarshas, not a story.
|
|
40
|
+
- **Vocabulary pass.** Reread what you are about to land for borrowed project-management words (ticket, backlog, sprint, epic, story, done, blocker, committed). Do **not** substitute them yourself: name each to the user and ask what the project calls it.
|
|
41
|
+
|
|
42
|
+
`weaving` / `design` carry the *how*.
|
|
43
|
+
- **Design completeness:** a design is not *done* until its decisions, risks and lifecycle are in the graph. A design/spec file written by another suite is a draft view: intake it **in the same session**. Working autonomously: land decisions and risks now; propose a bianhua with its telos marked for owner confirmation.
|
|
44
|
+
- **Execution suites run execution.** Planning, TDD, debugging, verification, review belong to the installed execution suite; the graph carries only the memory/design plane. Decisions and risks born in execution still land in the graph **before session end**.
|
|
45
|
+
- **A claim you made is not a claim you accept.** Behavioural claims are closed by the verdict of a cold `verifier` subagent, never by your rereading. Give it the claim, the carrier and the falsifier from *Reality* and **wait for the verdict**. Where the repo has no verifier role, observe the carrier yourself; never close a behavioural claim from the source that should have produced it.
|
|
46
|
+
- **Hook merging.** Where the harness has a hooks file, entries of different suites coexist โ add alongside, never overwrite.
|
|
47
|
+
- These reminders are automated where the harness can: session-start hook, post-`git push` hook, memory-write hook. Claude Code: `.claude/settings.json` โ not wired yet (deferred by quick mode).
|
|
48
|
+
- **Keep this file honest.** It is generated by `iskronify` and stamped at the bottom with its contract. Rerun `iskronify` when the installed skill announces a newer contract, or when the sources it derives from moved after the stamp: `git log -1 --format=%cd -- pyproject.toml uv.lock .github/` against the stamp date. The installed contract number is the first word of the `iskronify` skill description. (why: a stale AGENTS.md is read with full confidence every session.)
|
|
49
|
+
- **Keep your toolchain fresh.** Updates are **on by default**: take them as the channel delivers, do not pin.
|
|
50
|
+
|
|
51
|
+
### Stage self-check
|
|
52
|
+
Gate green and a coherent stage finished โ PR opened or updated, or you are about to touch nodes beyond those you started with โ reread your branch diff against trunk for: bugs, fragile spots, weak error handling, DRY/SOLID violations, repeated patterns, missing or useless tests, files over 150 lines and god units mixing many concerns. Fix in the **same branch** and push again โ or say plainly nothing surfaced. Do not invent findings. **Per stage, not only at the end.**
|
|
53
|
+
|
|
54
|
+
### Cold stage review
|
|
55
|
+
**Self-check does not replace a cold review.** After the self-check of an opened/updated PR or a finished large stage, **open a review by a top-tier subagent** (`reviewer` role) where the harness can. If it cannot, say plainly there was no cold review. **Only the push has a watchman.**
|
|
56
|
+
|
|
57
|
+
The reviewer's field is four things, all mandatory:
|
|
58
|
+
- **branch diff against trunk** โ the whole branch, not the last commit;
|
|
59
|
+
- **the repository itself** โ so it can open neighbouring code;
|
|
60
|
+
- **the focus holon and its steward role** from "What this project is";
|
|
61
|
+
- **references to graph nodes the diff touches** โ not a retelling of them.
|
|
62
|
+
|
|
63
|
+
The reviewer runs `integrity` read-only from the given nodes and, besides code findings, returns an **integration report**: what is affected, which relays pass or break, whether neighbours are ready by available evidence, open questions, whom to wake via `collaborate`. Unknown stays `unknown`. Missing references or a broken trace โ `NEEDS_CONTEXT` naming the gap: a graph defect. Fix what comes back in the same branch; reject a finding only with a recorded "why".
|
|
64
|
+
|
|
65
|
+
### Branch discipline
|
|
66
|
+
One branch until its merge โ commit follow-ups into it. After merge, clean up locally:
|
|
67
|
+
1. `git checkout main && git pull`.
|
|
68
|
+
2. Delete the merged branch (`git branch -d <name>`); prune others already in `main`.
|
|
69
|
+
3. Update the graph: weave the shipped state into the holon, close what the merge resolved (`weaving`).
|
|
70
|
+
4. Confirm the cleanup before the next task.
|
|
71
|
+
|
|
72
|
+
## Working principles
|
|
73
|
+
1. **Think before coding.** Name assumptions; when unsure, ask โ naming *what exactly* is unclear. **Ask the human in text**; never use an interactive option-picker. Surface competing readings; push back on a simpler path or a false premise. Check repo + graph before writing; fetch, don't recall. Touch the live system before trusting a type, name or doc. Out-of-bounds questions become vimarshas `posed_to` the owner's ไธป role.
|
|
74
|
+
2. **Simplicity first.** Minimum code for the task. No speculative features, no abstractions for one-off code, no handling of impossible errors. Validate at boundaries; trust internal invariants.
|
|
75
|
+
3. **Stay inside the repo boundary.** A change belonging to another holon is recorded as a vimarsha on that holon's node in its graph.
|
|
76
|
+
4. **A second implementation is an event to report.** About to write what already exists? Derive both places via `integrity`, name them to the user, and propose reunion or a named deliberate fork.
|
|
77
|
+
5. **Surgical changes.** Touch only what the task needs. Keep existing style; the linter is authoritative. Delete only dead code your change created.
|
|
78
|
+
6. **Goal-driven execution.** Tasks โ verifiable goals. Bugs: pin with a failing test before the patch. Multi-step: plan as `step โ check` pairs. Runtimes: verify in the real environment โ *Reality* names this project's carriers. Name the falsifier before looking and observe the carrier, not its source.
|
|
79
|
+
7. **Read before answering an open question.** Discuss / think through / investigate / design / plan / analyse โ answer from recorded thinking, not training data: query the graph several ways first.
|
|
80
|
+
8. **Think in the graph, speak the project's language.** The graph's structural vocabulary is for reasoning; it does not appear in what you say to the user until they use it first. Talk *about* work in plain words: question, change, what is open, what it unblocks.
|
|
81
|
+
|
|
82
|
+
## Integration field โ from the graph only
|
|
83
|
+
The focus holon and steward role are the only permanent root of traversal. AGENTS.md keeps **no list** of shared surfaces, consumers or dependencies, and does not ask the human for one. For every change, name the graph nodes whose embodiment is in the diff and run `integrity`; trace phenomena both ways with `iskron_orient(lens="trace")`. A dependency the traversal misses is a model defect: design the missing kriyas and phenomena, stitch edges via `weaving`.
|
|
84
|
+
|
|
85
|
+
## External surfaces โ what you use and do not own
|
|
86
|
+
Someone else's API, SDK, CLI, protocol. Agents **guess** them: memory from training is indistinguishable from knowledge from the inside, and a field that does not exist looks in code exactly like one that does.
|
|
87
|
+
- **Before working, record the part of the surface you will touch** as a graph node, with the version you looked at.
|
|
88
|
+
- **Source by seniority:** your own observation (calling it, `--help` of the installed binary, types of the installed package) beats docs; docs beat memory; **memory is not a source at all**. Write epistemics honestly: `pratyakshita` only for what you observed.
|
|
89
|
+
- **Weave the link:** the surface node is `upadhi` of the kriya acting through it.
|
|
90
|
+
- **Keep in step:** found a mismatch or a version bump โ fix the node in the same move.
|
|
91
|
+
- **The reference works both ways:** source touching an external surface carries `(graph โฆ, node #N)` โ and you **read that node before working**.
|
|
92
|
+
|
|
93
|
+
## Reality โ what claims are checked against
|
|
94
|
+
Not fully settled: run the interview (say iskronify). Seeded by use:
|
|
95
|
+
|
|
96
|
+
| Claim class | Canonical carrier | How to observe | Who can |
|
|
97
|
+
|---|---|---|---|
|
|
98
|
+
| Session / input / frames work | a live PS5 on the host's LAN, through the daemon | `PS5_HOST=<IP> uv run pytest -m hardware`; `uv run ps5agent snapshot` and look at the image | agent (console on the host LAN) |
|
|
99
|
+
| An agent drives the console over MCP | the MCP server over stdio, launched as Claude Code launches it | stdio client `mcp.Client(StdioServerParameters(uv run ps5-remoteplay-mcp))` โ `call_tool` | agent |
|
|
100
|
+
| Users can install it | the built wheel in a clean `uvx --isolated` env (py3.12โ3.14) | `uv build`; `uvx --isolated --python 3.13 --from dist/<wheel> ps5-remoteplay-mcp --help` | agent |
|
|
101
|
+
| Sign-in window links a console | pywebview window + a real PSN account + PIN on the TV | `uv run ps5agent setup --force` | user (PSN password, TV) |
|
|
102
|
+
| Latency | a live PS5 on the home screen | `uv run ps5agent latency` | agent |
|
|
103
|
+
|
|
104
|
+
**Ceiling**: behaviour inside a running game (analog L2/R2, sticks) needs a game running and the owner's go-ahead; Wi-Fi loss was verified only by artificial packet drops.
|
|
105
|
+
|
|
106
|
+
## Graph โ repo: where things live
|
|
107
|
+
| Concern | Repo | Graph |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| Code, configs, lockfiles | โ | |
|
|
110
|
+
| Commands, conventions, gotchas, stack | โ (AGENTS.md) | |
|
|
111
|
+
| Branch state, what is in flight | git + PR body | โ (`genre=hint` โ work without a PR) |
|
|
112
|
+
| Methodology, ontology | | โ |
|
|
113
|
+
| Design decisions, open questions | | โ (vimarshas) |
|
|
114
|
+
| Plans, task lists, session handoffs | | โ |
|
|
115
|
+
| Lessons | | โ (graph first; thin `genre=hint` for the off-map remainder) |
|
|
116
|
+
| Commit history, PRs, SHAs | git | (never in the graph) |
|
|
117
|
+
|
|
118
|
+
**No `HANDOVER.md` โ a decision, not an omission.** Branch and in-flight state: `git branch` / `log` and the open PR; how claims are checked: *Reality*; why and what is open: the graph; work without a PR yet: a `genre=hint` seed. Branch state is told **in the PR body**.
|
|
119
|
+
|
|
120
|
+
## Commands
|
|
121
|
+
| What | Command |
|
|
122
|
+
|---|---|
|
|
123
|
+
| Gate (ruff check + format --check + pytest) | `uv run python tools/gate.py` |
|
|
124
|
+
| Hardware test (needs the console) | `PS5_HOST=<IP> uv run pytest -m hardware` |
|
|
125
|
+
| Daemon in the foreground | `PS5_HOST=<IP> uv run ps5agent daemon` |
|
|
126
|
+
| Status / snapshot / press / latency | `uv run ps5agent status \| snapshot -o f.jpg \| press cross \| latency` |
|
|
127
|
+
| Picture window for the human (or `PS5_VIEWER=1` at daemon start) | `uv run ps5agent view` |
|
|
128
|
+
| Stop the daemon | `uv run ps5agent stop` |
|
|
129
|
+
| Link to the console (sign-in window + PIN) | `uv run ps5agent setup` (`--no-gui` for terminal) |
|
|
130
|
+
| MCP server (agents launch it; no args = MCP, args = CLI) | `uv run ps5-remoteplay-mcp` |
|
|
131
|
+
| Build / try as users get it | `uv build` โ `uvx --isolated --from dist/<wheel> ps5-remoteplay-mcp --help` |
|
|
132
|
+
| Publish (owner only, needs a PyPI token) | `uv publish` |
|
|
133
|
+
| Format | `uv run ruff format src tests tools/gate.py` |
|
|
134
|
+
|
|
135
|
+
## Project structure
|
|
136
|
+
- `src/ps5agent/vendor/pyremoteplay/` โ vendored protocol library, patched in place; every change listed in its `NOTICE.md`; excluded from ruff.
|
|
137
|
+
- `src/ps5agent/backend/` โ `ConsoleBackend` (base), `remoteplay.py` (session over the vendored library), `fake.py` (tests, `PS5_BACKEND=fake`).
|
|
138
|
+
- `src/ps5agent/console.py` โ discovery, saved console (`console.json`), linking (PSN redirect โ user, PIN โ registration).
|
|
139
|
+
- `src/ps5agent/login_window.py` โ pywebview window: Sony sign-in, catches the redirect, PIN form.
|
|
140
|
+
- `src/ps5agent/engine.py` โ input: serialization, watchdog, release guarantees.
|
|
141
|
+
- `src/ps5agent/daemon.py` โ session, reconnect, loopback HTTP API.
|
|
142
|
+
- `src/ps5agent/viewer.py` โ picture window for the human (separate process reading `/frame`).
|
|
143
|
+
- `src/ps5agent/client.py` โ daemon client + autostart; `mcp_server.py` โ tools; `cli.py` โ `ps5agent`.
|
|
144
|
+
- `src/ps5agent/fec.py` โ `pyjerasure` replacement (Cauchy Reed-Solomon, GF(2^8)).
|
|
145
|
+
- `tests/` โ unit + MCP contract tests on fake; `test_hardware.py` โ only with `PS5_HOST`.
|
|
146
|
+
- `tools/gate.py` โ the gate; `tools/spike.py` โ one-off phase-0 spike (excluded from lint).
|
|
147
|
+
|
|
148
|
+
## Code conventions
|
|
149
|
+
- **Meaning lives in the graph, code references it.** A comment carrying the rationale of a decision, discarded alternatives or integration design is a graph node living away from home: move the meaning to the graph and leave a reference in code โ "(graph `ps5-agent-bridge`, node #N)". Boundary: **the mechanics of a step โ in words in the comment; meaning, rationale, integration โ in the graph.** The reference works both ways: after referencing, check the node really says it. This repo is public: external contributors have no graph access, so a graph reference never replaces the one-line "why" a reader needs to change the code safely.
|
|
150
|
+
- **External APIs (pyremoteplay, chiaki-ng, MCP SDK)**: class and method names come from the installed package and its source, never from memory.
|
|
151
|
+
- **Test discipline**: unit tests on `FakeBackend`, an MCP contract test per tool, hardware tests `@pytest.mark.hardware` (only with `PS5_HOST`). Check the "nothing held" invariant (`FakeBackend.idle`) in every test of a new input path.
|
|
152
|
+
- **Gotchas**:
|
|
153
|
+
- pyremoteplay is vendored, not a dependency: the PyPI release pulls `netifaces` (no Windows wheels โ breaks `uvx`) and pins old `pyee`/`av`. Fix protocol bugs in `src/ps5agent/vendor/pyremoteplay` directly and add a line to its `NOTICE.md` (GPL requires marking changes). Run the hardware test after any change there.
|
|
154
|
+
- The vendored library's DEBUG logs print the handshake key and ECDH secret โ `console.prepare` keeps logger `ps5agent.vendor.pyremoteplay` at INFO or above. Call `console.prepare(cfg)` before using the library (it also points profiles at `~/.config/ps5-agent/profiles.json`).
|
|
155
|
+
- Audio is disabled in the receiver (decoder and resampler are skipped).
|
|
156
|
+
- Sony redirects only to `remoteplay.dl.playstation.net` after sign-in: a normal browser cannot hand the code back. The pywebview window catches it from its own navigation. pywebview needs the main thread of its own process: the MCP `setup` tool spawns `ps5agent setup --gui-only` with stdin=DEVNULL; exit code 3 = no GUI โ the tool returns the chat fallback (link โ `setup(redirect_url)` โ `setup(pin)`).
|
|
157
|
+
- `[WinError 10048]` on DDP port 9303 in the log is harmless: two processes discovering at once; the library falls back to another port.
|
|
158
|
+
- Windows: after a pyremoteplay session, closing the asyncio loop hangs on a UDP operation โ processes holding a session exit via `os._exit` after cleanup (`daemon.run`).
|
|
159
|
+
- The vendored library sends UDP from other threads (controller worker, AV executor); asyncio transports are loop-thread only, and a cross-thread send kills the stream on Windows (`Fatal write error on datagram transport`). `RPStream.Protocol.sendto` marshals to the loop โ any new send path must go through it.
|
|
160
|
+
- Two `python.exe` per daemon or MCP server on Windows is one process: the venv `python.exe` is a launcher that starts the real interpreter as a child. The daemon port (9395) already makes a second daemon fail to bind.
|
|
161
|
+
- The daemon is spawned with `CREATE_BREAKAWAY_FROM_JOB`: MCP clients on Windows kill the server's process tree on exit. Without the flag the daemon dies with every MCP server.
|
|
162
|
+
- aiohttp cancels a handler when the client leaves only with `handler_cancellation=True` (`daemon.make_runner`) โ otherwise a hold runs to its end.
|
|
163
|
+
- A killed daemon leaves a zombie session on the console for ~30โ40 s: "Another Remote Play session is connected to host". The daemon reconnects by itself on backoff.
|
|
164
|
+
- mcp 2.x: `MCPServer` instead of `FastMCP`; result fields are snake_case (`is_error`, `mime_type`); errors meant for the model are `ToolError` (otherwise the text is masked); enter and exit an in-process `Client` in the test body, not in an async fixture (anyio cancel scope).
|
|
165
|
+
- The Create button is `SHARE` in pyremoteplay.
|
|
166
|
+
- The console swallows presses during screen-transition animations โ not an input bug.
|
|
167
|
+
|
|
168
|
+
## What to update when
|
|
169
|
+
- `AGENTS.md` โ inverted default: **if it can be learned by reading a graph node, it is not here.** This file holds only what is needed BEFORE the agent reaches the graph: commands, the entry into orientation, code invariants the linter cannot express, forks that must stop you before acting โ and is updated when THAT changes. Cleaning up written prose is a reconcile beat with transfer into carrying nodes, never deletion.
|
|
170
|
+
- `CLAUDE.md` โ regenerate the copy on every change to `AGENTS.md`; it is a duplicate, never edited directly.
|
|
171
|
+
- The project graph โ every push (see "Session lifecycle").
|
|
172
|
+
|
|
173
|
+
## Git workflow
|
|
174
|
+
- **Conventional commits** (`feat:`/`fix:`/`chore:`/`refactor:`/`docs:`/`test:`). Branches `feat/โฆ`, `fix/โฆ`, `chore/โฆ`.
|
|
175
|
+
- **Merge without a PR โ always** (owner's standing instruction): when the owner says to ship, merge the branch into `main` locally (`git merge --no-ff`) and push `main`; do not open a PR. The cold stage review and the post-merge graph steps still apply.
|
|
176
|
+
- **No AI attribution**: no `Co-Authored-By` trailers for Claude or other agents in commits, no "Generated with" lines in PR bodies โ the repository lists the owner as its only contributor (owner's decision).
|
|
177
|
+
- **Forge**: GitHub, `khodossss/ps5-remoteplay-mcp` (public); CLI `gh` โ observe with `gh pr list`, `gh pr checks <n>`.
|
|
178
|
+
- **Local gate โ one call**: `uv run python tools/gate.py`; call it by name, never assemble the steps by hand. No CI yet.
|
|
179
|
+
- **Definition of done**: not yet agreed.
|
|
180
|
+
- **Never** `--no-verify`, `--force`, `--no-gpg-sign`, `git reset --hard` without an explicit instruction from the user.
|
|
181
|
+
|
|
182
|
+
*(iskronify: contract `6`, stamp `2026-10-12` โ rerun when the installed iskronify description names a
|
|
183
|
+
higher contract or when the sources this file is derived from moved after this date.)*
|