Przeglądaj źródła

Vendor chattolib 0.5.0b15 and restate what it means for the realtime channel

The vendored tree was still b13, and with it two claims the docs had stopped
tracking. The first is the protocol version: the README's "realtime protocol
v2" was written when chattolib 0.5.0a3 sent 2 in its subscribe handshake, and
the library has advertised 4 since the 0.5 import — the text was never moved,
and a refresh that only restates the server floor keeps missing it. The second
is the promise that a server-side presence projection arrives on subscribe.
There is no RealtimeProjectionEvent: the channel offers
RealtimeSubscribe.initial_state = SNAPSHOT, whose single RealtimeSnapshot frame
carries the rooms and the user directory but no presence at all, and chattolib
asks for LIVE_ONLY by default while the adapter drops the frame — so the
discard-and-refetch stays the fail-fresh fallback it was meant to be until
something actually consumes it.

Refreshed via vendor_chattolib.sh (0.5.0b13 -> 0.5.0b15, three platforms,
import verified). The refresh also restores the vendored client.py to upstream:
the branch patched its start_dm() onto the real stub name, but b15 still ships
the broken call, so the patch was a vendor edit the rulebook forbids and no
fix at all. That path is removed from the adapter in the next commit instead.
Paul Klumpp 2 tygodni temu
rodzic
commit
d0238204a0

+ 14 - 9
AGENTS.md

@@ -34,9 +34,10 @@ consumption — because the bot is a Chatto client just like the browser. Read
 how the platform itself models a problem before inventing our own shape (its
 presence-overlay pattern is what shaped the roster projection below).
 
-The vendored copy is upstream `chattolib[realtime]` 0.5.0b13 (realtime
-protocol v2). chattolib versions track the Chatto server release they target,
-so this copy pairs with Chatto v0.5.0-beta.13, which is also the server floor
+The vendored copy is upstream `chattolib[realtime]` 0.5.0b15 (realtime
+protocol 4 — the revision chattolib sends in `RealtimeSubscribe.protocol_version`).
+chattolib versions track the Chatto server release they target,
+so this copy pairs with Chatto v0.5.0-beta.15, which is also the server floor
 this build claims to support. Refreshing `vendor/` via `vendor_chattolib.sh`
 can move that floor (see VENDORING.md) — after each refresh, restate the new
 version here and in README.md.
@@ -184,12 +185,16 @@ changes must keep them, or consciously renegotiate the docs.**
   fetched from the directory once. `presence_changed` patches the cached user
   in place; `user_joined_room`/`user_left_room`, `/leave`, stale-room pruning
   and reconnects discard a room's projection so its next turn refetches once —
-  this mirrors realtime protocol v1, which sent no presence snapshot on
-  subscribe, so discarding was what forced fresh data after downtime; protocol
-  v2 now delivers a server projection on subscribe (and `RealtimeProjectionEvent`
-  in chattolib ≥0.5), which the adapter does not consume yet — until it does,
-  the discard behaviour stays the fail-fresh fallback; quiet rooms stay free
-  of lookups. The bot's own presence events are filtered out. A failed lookup
+  a discarded cache is what forces one honest refetch after downtime. The
+  realtime channel *can* deliver a server-side projection on subscribe, but not
+  a presence one: `RealtimeSubscribe.initial_state = SNAPSHOT` makes the server
+  send a single `RealtimeSnapshot` frame (rooms with their `member_user_ids`,
+  the user directory, room groups), and it carries no presence for anyone —
+  presence only ever arrives as `PresenceChangedEvent`s. chattolib requests
+  `LIVE_ONLY` by default and the adapter neither requests the snapshot nor
+  consumes the frame (`isinstance(event, RealtimeEvent)` drops it), so the
+  discard-and-refetch behaviour stays the fail-fresh fallback; quiet rooms stay
+  free of lookups. The bot's own presence events are filtered out. A failed lookup
   leaves the room unprojected and lets the next turn retry.
 - **Edits are corrections, not new traffic.** An inbound `message_edited`
   re-runs the admission gates against the new body: a message currently being

+ 6 - 6
README.md

@@ -4,7 +4,7 @@ A [Chatto](https://github.com/chattocorp/chatto) platform adapter plugin for [He
 
 All API interaction goes through [chattolib](https://github.com/TheCodemancerLtd/chattolib): a persistent WebSocket realtime connection delivers inbound events, ConnectRPC over HTTP handles outbound messages, reactions, uploads and more. Messages are processed through the full Hermes Agent pipeline (tool use, memory, reasoning) and answered in real time.
 
-Tested against Chatto v0.5.0-beta.13 with the vendored `chattolib[realtime]` 0.5.0b13 (realtime protocol v2). chattolib versions track the Chatto server release they target, so this build pairs with Chatto v0.5.0-beta.13; Chatto servers older than v0.5.0-beta.13 are not supported by this build.
+Tested against Chatto v0.5.0-beta.15 with the vendored `chattolib[realtime]` 0.5.0b15 (realtime protocol 4). chattolib versions track the Chatto server release they target, so this build pairs with Chatto v0.5.0-beta.15; Chatto servers older than v0.5.0-beta.15 are not supported by this build.
 
 Before setup, here's the part most people want to know: how Hermes behaves once it's in your Chatto instance.
 
@@ -58,7 +58,7 @@ Capabilities natively implemented by the Chatto plugin adapter:
 3. **Room membership** — the bot account must be a member of any room where you want it to respond; the adapter picks up every room the account has joined. Membership can be granted natively in Chatto (invite the bot account), or by DMing the bot `/join <room-id or #name>` (see [Managing Rooms over DM](#managing-rooms-over-dm)). For DMs, simply start a direct message with the bot.
 4. **Network access** — the Hermes host must reach the Chatto server URL over HTTPS (or HTTP) and establish a WebSocket connection to `/api/realtime`.
 
-> **Info:** The adapter uses WebSocket protocol v2 (compatible with Chatto v0.5.0-beta.13+). Ensure your Chatto server is up to date.
+> **Info:** The adapter speaks realtime protocol 4 (the revision chattolib sends in the subscribe handshake) and needs a Chatto server that accepts it. Ensure your Chatto server is up to date.
 
 ## Install
 
@@ -216,7 +216,7 @@ Seeding happens once per session, not per message and not per restart: later tur
 
 Hermes cannot see who else is in a room: unaddressed channel messages are dropped by the mention gates long before they could teach him a name. So every turn in a channel carries the member roster alongside the message text — one entry per member as `@login (Display Name, presence)`, e.g. `@alice (Alice, online), @bob (Bob, offline)`. The bot's own account and deleted users are left out; rooms larger than 20 members end with `… and N more`. Presence is the Chatto status (`online`, `away`, `do not disturb`, `offline`).
 
-The roster rides on every channel turn — threads hold isolated sessions, so each turn needs its own copy. That repetition is free of API calls: the adapter keeps a small projection per room (member IDs plus their cached users), fetched once from the directory, and `presence_changed` events patch those users in place, so someone going `away` or `offline` shows up on the next turn without any extra lookup. The bot's own presence refresh never enters the roster. Membership changes (`user_joined_room`/`user_left_room`), leaving a room and reconnects discard the projection, and the room's next turn refetches once. Realtime protocol v2 now delivers a server-side presence projection on subscribe; the adapter does not consume it yet, so the discard-and-refetch behaviour stays as the fail-fresh fallback until it does. If the initial lookup fails, the turn proceeds without the roster and the next turn tries again.
+The roster rides on every channel turn — threads hold isolated sessions, so each turn needs its own copy. That repetition is free of API calls: the adapter keeps a small projection per room (member IDs plus their cached users), fetched once from the directory, and `presence_changed` events patch those users in place, so someone going `away` or `offline` shows up on the next turn without any extra lookup. The bot's own presence refresh never enters the roster. Membership changes (`user_joined_room`/`user_left_room`), leaving a room and reconnects discard the projection, and the room's next turn refetches once. The realtime channel can deliver a server-side projection on subscribe, but not a presence one: it carries the rooms and the user directory, while presence only ever arrives as its own events. The adapter neither requests that snapshot nor consumes it, so the discard-and-refetch behaviour stays as the fail-fresh fallback until it does. If the initial lookup fails, the turn proceeds without the roster and the next turn tries again.
 
 ### Reactions
 
@@ -400,9 +400,9 @@ location /api/realtime {
 
 ### Protocol version mismatch
 
-**Cause**: The Chatto server is running an older version that doesn't support realtime protocol v2.
+**Cause**: The Chatto server is running an older version that rejects the realtime protocol revision the adapter's client sends (4, set in the subscribe handshake).
 
-**Fix**: The adapter uses protocol v2, supported by Chatto v0.5.0-beta.13 and later. Upgrade your Chatto server:
+**Fix**: Upgrade your Chatto server to a release that accepts realtime protocol 4:
 
 ```bash
 curl -s https://chat.example.com/api/version | jq .
@@ -441,7 +441,7 @@ grep -i "chatto\|websocket\|realtime" ~/.hermes/logs/gateway.log | tail -30
 ## Notes
 
 - **Self-hosted friendly**: Works with any self-hosted Chatto instance. No cloud account or subscription required.
-- **Minimal dependencies**: Only requires `chattolib[realtime]==0.5.0b13`, which is vendored — nothing to install.
+- **Minimal dependencies**: Only requires `chattolib[realtime]==0.5.0b15`, which is vendored — nothing to install.
 - **No message backfill**: Events missed while the gateway is down are gone — Chatto keeps no replay for the bot. Redelivered events after a reconnect are recognised and processed only once; anything genuinely new starts from the moment of reconnection.
 
 ## Acknowledgements

+ 2 - 2
VENDORING.md

@@ -66,7 +66,7 @@ load-bearing where it does not.
 
 chattolib requires `httpx>=0.27`, `protobuf>=5.28` and (via the `[realtime]`
 extra) `websockets>=13.0`. Two of those floors are worth knowing about:
-`protobuf>=5.28` is wrong in practice — chattolib b13 ships gencode 7.35.1, and
+`protobuf>=5.28` is wrong in practice — chattolib b15 ships gencode 7.35.1, and
 protobuf ≥ 5.27 refuses at import time to load gencode newer than its own
 runtime, so a host protobuf older than 7.35.1 raises `VersionError` rather
 than degrading. And if a future Hermes release pins `httpx` below 0.27, the
@@ -91,7 +91,7 @@ PLATFORMS="linux-x86_64 linux-aarch64 macos-arm64 windows-amd64" ./vendor_chatto
 
 # Pin a specific chattolib version (keep the [realtime] extra — without it
 # websockets is not vendored and the event stream cannot start)
-CHATTOLIB_SPEC="chattolib[realtime]==0.5.0b13" ./vendor_chattolib.sh
+CHATTOLIB_SPEC="chattolib[realtime]==0.5.0b15" ./vendor_chattolib.sh
 ```
 
 The script passes `--only-binary :all: --no-binary chattolib`. Everything must

+ 8 - 6
adapter.py

@@ -1423,8 +1423,9 @@ class ChattoAdapter(BasePlatformAdapter):
         """Drop all roster state for a room.
 
         Called on membership changes (user_joined/left_room), when we leave a
-        room ourselves, and on reconnect — protocol v1 has no presence snapshot
-        on subscribe, so a discarded cache is what forces one honest refetch.
+        room ourselves, and on reconnect. The subscribe snapshot carries the
+        room and user directory but no presence, and the adapter does not
+        request it — so a discarded cache is what forces one honest refetch.
         """
         try:
             self._roster_announced.remove(room_id)
@@ -2134,10 +2135,11 @@ class ChattoAdapter(BasePlatformAdapter):
         """Refresh room list via ConnectRPC, join and seed any newly discovered rooms.
 
         Runs once per (re)connect, so this is also the bootstrap point for the
-        roster projection: protocol v1 sends no presence snapshot on subscribe,
-        meaning cached presence could only heal on the next random change.
-        Discarding it makes each announced room's first turn refetch fresh
-        data — lazily, so quiet rooms stay free of API calls.
+        roster projection: nothing on the wire reports presence at connect
+        time (the subscribe snapshot has no presence field; presence arrives
+        as its own events), so cached presence could only heal on the next
+        random change. Discarding it makes each announced room's first turn
+        refetch fresh data — lazily, so quiet rooms stay free of API calls.
         """
         client = await self._get_chatto_client()
         if client is None:

+ 1 - 1
vendor/common/bin/httpx

@@ -1,4 +1,4 @@
-#!/home/paul/.paseo/worktrees/02u7s3t4/loyal-elephant/.venv/bin/python3
+#!/tmp/opencode/plugin-tools-review/.venv/bin/python3
 # -*- coding: utf-8 -*-
 import sys
 from httpx import main

+ 1 - 1
vendor/common/bin/idna

@@ -1,4 +1,4 @@
-#!/home/paul/.paseo/worktrees/02u7s3t4/loyal-elephant/.venv/bin/python3
+#!/tmp/opencode/plugin-tools-review/.venv/bin/python3
 # -*- coding: utf-8 -*-
 import sys
 from idna.cli import main

+ 0 - 0
vendor/common/chattolib-0.5.0b13.dist-info/INSTALLER → vendor/common/chattolib-0.5.0b15.dist-info/INSTALLER


+ 1 - 1
vendor/common/chattolib-0.5.0b13.dist-info/METADATA → vendor/common/chattolib-0.5.0b15.dist-info/METADATA

@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: chattolib
-Version: 0.5.0b13
+Version: 0.5.0b15
 Summary: Async Python client library for the Chatto webchat Connect API
 Author-email: Felix <chatto@f3l1x.it>
 License-Expression: MPL-2.0 AND Apache-2.0

+ 11 - 11
vendor/common/chattolib-0.5.0b13.dist-info/RECORD → vendor/common/chattolib-0.5.0b15.dist-info/RECORD

@@ -1,13 +1,13 @@
-chattolib-0.5.0b13.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
-chattolib-0.5.0b13.dist-info/METADATA,sha256=JWyRwb74kUfG0o4OgCqKppS-qygoFyi8RZ0_qKE6XmY,4936
-chattolib-0.5.0b13.dist-info/RECORD,,
-chattolib-0.5.0b13.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-chattolib-0.5.0b13.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
-chattolib-0.5.0b13.dist-info/licenses/LICENSE,sha256=Pz2eACSxkhsGfW9_iN60pgy-enjnbGTj8df8O3ebnQQ,16726
-chattolib-0.5.0b13.dist-info/licenses/LICENSES/Apache-2.0.txt,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
-chattolib-0.5.0b13.dist-info/licenses/LICENSES/MPL-2.0.txt,sha256=Pz2eACSxkhsGfW9_iN60pgy-enjnbGTj8df8O3ebnQQ,16726
-chattolib-0.5.0b13.dist-info/top_level.txt,sha256=vgsGOu_BQfCunqwDpfv3epJ86-g6s0va3aAYlwUW1Cg,10
-chattolib-0.5.0b13.dist-info/uv_build.json,sha256=RBNvo1WzZ4oRRq0W9-hknpT7T8If536DEMBg9hyq_4o,2
+chattolib-0.5.0b15.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+chattolib-0.5.0b15.dist-info/METADATA,sha256=W7A-kenV31dfGiQ8ESVBqQ4iVJNfHtcbgZqbidg3px4,4936
+chattolib-0.5.0b15.dist-info/RECORD,,
+chattolib-0.5.0b15.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+chattolib-0.5.0b15.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
+chattolib-0.5.0b15.dist-info/licenses/LICENSE,sha256=Pz2eACSxkhsGfW9_iN60pgy-enjnbGTj8df8O3ebnQQ,16726
+chattolib-0.5.0b15.dist-info/licenses/LICENSES/Apache-2.0.txt,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
+chattolib-0.5.0b15.dist-info/licenses/LICENSES/MPL-2.0.txt,sha256=Pz2eACSxkhsGfW9_iN60pgy-enjnbGTj8df8O3ebnQQ,16726
+chattolib-0.5.0b15.dist-info/top_level.txt,sha256=vgsGOu_BQfCunqwDpfv3epJ86-g6s0va3aAYlwUW1Cg,10
+chattolib-0.5.0b15.dist-info/uv_build.json,sha256=RBNvo1WzZ4oRRq0W9-hknpT7T8If536DEMBg9hyq_4o,2
 chattolib/__init__.py,sha256=baJ3jHj7jkcOU5QBk9wu72Q1pqbStc6IjNSifTP63kI,3581
 chattolib/_codegen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
 chattolib/_codegen/protoc_gen_request_pb2.py,sha256=ctc_R6vciqBWr8Ki2eYWCwQnxXYg9t9OsPMVrjobK5M,1636
@@ -21,7 +21,7 @@ chattolib/_pb/chatto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
 chattolib/_pb/chatto/admin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
 chattolib/_pb/chatto/admin/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
 chattolib/_pb/chatto/admin/v1/diagnostics_connect.py,sha256=4CqQlFyoL1Sc2294dq-OWPaVyuy_yVYF7e60ZRyCuZY,1613
-chattolib/_pb/chatto/admin/v1/diagnostics_pb2.py,sha256=-OPd-Lf_Lv9bLI6Gpn3YUSawiihW_BCd9oOHGgFmy4Y,10919
+chattolib/_pb/chatto/admin/v1/diagnostics_pb2.py,sha256=PREI5OVgq8lSp2e2VNSkZQaEISTvoxkP1IPG0mcuG9A,12280
 chattolib/_pb/chatto/admin/v1/event_log_connect.py,sha256=oYl-yCR3f6lyocszTWUmwYRTH3jIlQJEXLYrC8LaEkQ,3899
 chattolib/_pb/chatto/admin/v1/event_log_pb2.py,sha256=zmLBF2nULUHZxL7eBIS5pfUPEN1jPzrstHhTuc5yLRk,4851
 chattolib/_pb/chatto/admin/v1/members_connect.py,sha256=g6Cdn0oexiwiEURRDwdOrEuOjtDNi2xIMT9lY2u0zbQ,9791

+ 0 - 0
vendor/common/chattolib-0.5.0b13.dist-info/REQUESTED → vendor/common/chattolib-0.5.0b15.dist-info/REQUESTED


+ 0 - 0
vendor/common/chattolib-0.5.0b13.dist-info/WHEEL → vendor/common/chattolib-0.5.0b15.dist-info/WHEEL


+ 0 - 0
vendor/common/chattolib-0.5.0b13.dist-info/licenses/LICENSE → vendor/common/chattolib-0.5.0b15.dist-info/licenses/LICENSE


+ 0 - 0
vendor/common/chattolib-0.5.0b13.dist-info/licenses/LICENSES/Apache-2.0.txt → vendor/common/chattolib-0.5.0b15.dist-info/licenses/LICENSES/Apache-2.0.txt


+ 0 - 0
vendor/common/chattolib-0.5.0b13.dist-info/licenses/LICENSES/MPL-2.0.txt → vendor/common/chattolib-0.5.0b15.dist-info/licenses/LICENSES/MPL-2.0.txt


+ 0 - 0
vendor/common/chattolib-0.5.0b13.dist-info/top_level.txt → vendor/common/chattolib-0.5.0b15.dist-info/top_level.txt


+ 0 - 0
vendor/common/chattolib-0.5.0b13.dist-info/uv_build.json → vendor/common/chattolib-0.5.0b15.dist-info/uv_build.json


Plik diff jest za duży
+ 0 - 0
vendor/common/chattolib/_pb/chatto/admin/v1/diagnostics_pb2.py


+ 1 - 1
vendor/common/chattolib/client.py

@@ -698,7 +698,7 @@ class ChattoClient:
 
     async def start_dm(self, participant_ids: list[str]) -> Room:
         resp = await self._rpc(
-            self._svc.rooms.start_d_m(
+            self._svc.rooms.start_dm(
                 rooms_pb2.StartDMRequest(participant_ids=participant_ids),
                 headers=self._headers(),
             )

+ 1 - 1
vendor/common/httpx-0.28.1.dist-info/RECORD

@@ -1,4 +1,4 @@
-bin/httpx,sha256=wIhQ-mAEZxQf6Z7ljfogCl3qPaX97JNiQh59wSjCPR8,340
+bin/httpx,sha256=zPigzwnYXsLcj5251CDcXu32UO8YrhEJvPN3cE79v08,322
 httpx-0.28.1.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
 httpx-0.28.1.dist-info/METADATA,sha256=_rubD48-gNV8gZnDBPNcQzboWB0dGNeYPJJ2a4J5OyU,7052
 httpx-0.28.1.dist-info/RECORD,,

+ 1 - 1
vendor/common/idna-3.20.dist-info/RECORD

@@ -1,4 +1,4 @@
-bin/idna,sha256=daeKOf8nadde-jDFkSL7wXTrRywU_IY5z2BGWONRGOU,343
+bin/idna,sha256=W8SrhFisvAOxGFDsU7YI5UcQUY_uGJNwasa-UZG9WbM,325
 idna-3.20.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
 idna-3.20.dist-info/METADATA,sha256=29jBTB5MoeDJgkptvHy794iE8461LZEUjNMCX2ceS4U,7207
 idna-3.20.dist-info/RECORD,,

+ 1 - 1
vendor/platform/linux-aarch64/bin/websockets

@@ -1,4 +1,4 @@
-#!/home/paul/.paseo/worktrees/02u7s3t4/loyal-elephant/.venv/bin/python3
+#!/tmp/opencode/plugin-tools-review/.venv/bin/python3
 # -*- coding: utf-8 -*-
 import sys
 from websockets.cli import main

+ 1 - 1
vendor/platform/linux-aarch64/websockets-17.2.dist-info/RECORD

@@ -1,4 +1,4 @@
-bin/websockets,sha256=0L0GTu_SOE9DVfpVT8zOWXglVyU07BhvW1PN8nfhW9I,349
+bin/websockets,sha256=6lZ8JjO5SEmNpLr66BLIJlF6UgHBn3ZSc07QAZneCA0,331
 websockets-17.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
 websockets-17.2.dist-info/METADATA,sha256=DV8SedCV57qE7yFhSvCCLcUAzLLlG3w6Ge2nFZstreQ,6336
 websockets-17.2.dist-info/RECORD,,

+ 1 - 1
vendor/platform/linux-x86_64/bin/websockets

@@ -1,4 +1,4 @@
-#!/home/paul/.paseo/worktrees/02u7s3t4/loyal-elephant/.venv/bin/python3
+#!/tmp/opencode/plugin-tools-review/.venv/bin/python3
 # -*- coding: utf-8 -*-
 import sys
 from websockets.cli import main

+ 1 - 1
vendor/platform/linux-x86_64/websockets-17.2.dist-info/RECORD

@@ -1,4 +1,4 @@
-bin/websockets,sha256=0L0GTu_SOE9DVfpVT8zOWXglVyU07BhvW1PN8nfhW9I,349
+bin/websockets,sha256=6lZ8JjO5SEmNpLr66BLIJlF6UgHBn3ZSc07QAZneCA0,331
 websockets-17.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
 websockets-17.2.dist-info/METADATA,sha256=DV8SedCV57qE7yFhSvCCLcUAzLLlG3w6Ge2nFZstreQ,6336
 websockets-17.2.dist-info/RECORD,,

+ 1 - 1
vendor/platform/macos-arm64/bin/websockets

@@ -1,4 +1,4 @@
-#!/home/paul/.paseo/worktrees/02u7s3t4/loyal-elephant/.venv/bin/python3
+#!/tmp/opencode/plugin-tools-review/.venv/bin/python3
 # -*- coding: utf-8 -*-
 import sys
 from websockets.cli import main

+ 1 - 1
vendor/platform/macos-arm64/websockets-17.2.dist-info/RECORD

@@ -1,4 +1,4 @@
-bin/websockets,sha256=0L0GTu_SOE9DVfpVT8zOWXglVyU07BhvW1PN8nfhW9I,349
+bin/websockets,sha256=6lZ8JjO5SEmNpLr66BLIJlF6UgHBn3ZSc07QAZneCA0,331
 websockets-17.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
 websockets-17.2.dist-info/METADATA,sha256=DV8SedCV57qE7yFhSvCCLcUAzLLlG3w6Ge2nFZstreQ,6336
 websockets-17.2.dist-info/RECORD,,

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików