Repository navigation
feat: Walkie Talkie V1.1 — groups, broadcasts, structured requests, multi-agent orchestration - #1
Merged
Merged
Conversation
P0 evidence: PUB-01..11 verified from GitHub API, successor worktrees verified at exact bases, V1 baseline 403 passed/4 skipped archived, ADR-0004 (V2 identity/groups/workflows), ADR-0005 (Windows backend, native proof pending), ADR-0006 (Desktop boundary), Windows transport spike (POSIX reference PASS, native PENDING), Desktop plugin spike (contract shape proven). No production code yet.
P1 backend-neutral seam (ADR-0005): TransportEndpoint, OwnerEvidence, ListenerAuthority, ListenerHandle, LocalTransportBackend, PathBackend protocols; PosixTransportBackend/PosixPathBackend reference implementations wrapping accepted V1 AF_UNIX + SO_PEERCRED + chmod/st_uid behaviour; WindowsTransportBackend/WindowsPathBackend fail-closed skeletons (native proof pending, no mock success); runtime/discovery inject backends via platform auto-selection. No change to V1 receipts, delivery order, resource ceiling or listener fencing; teardown fence preserved via close_fd (FD-only) vs close (FD+unlink). Security gates extended to the backend seam. 27 new tests; full suite 430 passed/4 skipped; ruff clean; ty clean; coverage gate PASS 91.6%.
P2 (ADR-0005 Candidate 2): explicit SID/DACL named-pipe backend via the optional pywin32 extra; WindowsPathBackend under %LOCALAPPDATA%/agent-peer; same backend-neutral contract as POSIX; owner proof = DACL at open + client process SID match; teardown fence preserves replacement pipes. Native-gated tests (unit/security/integration/e2e) skip on non-Windows with explicit reasons — never green evidence. Fail-closed on Linux; no mocked Windows success. NATIVE PROOF PENDING: no Windows runner on this rig.
P3 (ADR-0004): agent_id long-lived adapter identity persisted owner-only in HERMES_HOME (ephemeral when no real home — tests never mutate ~/.hermes); PeerRecord advertises protocols + capabilities; ReceiptState gains incompatible/ambiguous; Surface gains desktop; capabilities.py negotiates highest mutual version fail-closed; protocol_v2.py strict typed payloads (message/receipt/discover/alive/request/request_status/request_cancel); DiscoveryService.resolve_agent deterministic routing (pinned peer -> primary -> single live -> ambiguous, never broadcast-all); sessions.py V2 advertisement; store v2 migration idempotent with old rows readable as V1 and readonly-safe open. V1 stale tests honestly updated (agent-peer/2 now known; unknown = agent-peer/3+). 49 new tests; full suite 487 passed; ruff/ty clean; coverage gate PASS 91.3%.
P4 (ADR-0004): schema v3 adds groups/group_members/broadcasts/ broadcast_children with idempotent incremental migration (old rows readable as V1). GroupStore: CRUD with optimistic revision, unique normalised names, owner fence, member caps (default 32, hard 128). BroadcastEngine: persist-parent-first; deterministic child ids from (broadcast_id, agent_id, peer_id); atomic created->in_flight single-writer gate so concurrent duplicate broadcasters converge on one child per recipient (no duplicate injection); bounded concurrency; explicit partial results; sender self-exclusion; unknown/empty/sender-mismatch fail closed. Real-runtime integration proves 1 parent -> N receipts and retry idempotency; property tests prove no-duplicate across retries. 26 new tests; full suite 513 passed; ruff/ty clean; coverage gate PASS 91.5%.
P5 (ADR-0004): pure transition decision table (workflows.py) enforcing created->queued->accepted->in_progress->completed|failed|refused|cancelled| expired with impossible/stale transitions rejected and terminal states frozen; Request aggregate + schema v4 (requests/request_events, idempotent migration, NULL idempotency-key semantics); RequestStore with (sender,key) idempotency returning the original request (never duplicate work); session manager create/status/respond/cancel/expire with recipient-only respond; inert <peer_request> conversational marker (cannot approve, invoke commands or bypass policy); request tools create/status/respond/cancel; REAL two-Hermes E2E drives create->delivered->accept->progress->complete through the model tool pipeline. Fix: integration fixtures pin AGENT_PEER_STATE_DIR so tests never mutate real ~/.local/state. 38 new tests; full suite 551 passed; ruff/ty clean; coverage gate PASS 90.1%.
P6 (G1.1..G1.8): content-free MetricsRegistry (counts, latency p50/p95, failure reasons, held depth, stale events — recorders accept no content params, structural gate); bounded EventBroker (slow consumers dropped safely, client cap, never blocks delivery); health_snapshot with actionable remedies (backend, registry, peers, store, groups, requests, stale state); stale alerts with debounce + exact-instance fence (cleanup never deletes a replaced live instance); session manager instruments _on_inbound and doctor() now exposes the full health snapshot + metrics; metrics/events exposed for Desktop acceleration; PeerConfig ceilings validated within hard bounds (group_cap<=128, fanout<=64, TTL bounds, event_clients<=256). 34 new tests; full suite 582 passed; ruff/ty clean; coverage gate PASS 90.4%.
P7: group tools (peer_group_list/manage, peer_broadcast); request tools (create/status/respond/cancel); V1 tools preserved with **kwargs dispatcher metadata; slash commands peer-groups/peer-group/peer-broadcast/peer-request; CLI subcommands groups/group/broadcast/request/desktop; hermes peer desktop install|status|remove with bundled plugin.js asset (explicit install only, G6.9); tool-schema budget gate (10 tools hermes-peer-scoped, <4KiB aggregate); multi-session mutating ops fail closed without session_id (P7.6); membership stores stable agent_id — alias rename does not change membership. Manager group/broadcast ops proven over real transport (3 real managers). 35 new tests; full suite 617 passed; ruff/ty clean; coverage gate PASS 90.2%.
P8: first-class Hermes Desktop plugin. Dashboard backend (dashboard/manifest.json + FastAPI plugin_api) exposes /api/plugins/hermes-peer health, metrics, peers, groups + members, broadcast outcomes, inbox, requests + respond, and an /events WebSocket (delegated _ws_auth_ok gate, always-frame heartbeat so clients never hang). React+TS desktop source (api.ts typed wrappers, store.ts profile-scoped refresher + bounded content-free activity ring, PeerPanel tabs Peers/Groups/Inbox/Requests/Health) builds via vite into compiled plugin.js + style.css shipped in hermes_peer/assets/desktop (wheel package-data includes dashboard + assets, ACC-17). @hermes/ plugin-sdk is externalized and loader-injected at runtime; local sdk-stub.d.ts mirrors the consumed PluginContext surface for standalone typecheck. hermes peer desktop install now copies the full compiled bundle. G6.9 no-auto-install structural gate: plugin load never writes to HERMES_HOME. 15 new Python tests + 7 vitest (store/api); full suite 629 passed; ruff/ty clean; coverage gate PASS 90.2%.
P9: REAL two-Hermes broadcast E2E (group create/add/broadcast through real deferred dispatch, callable fake-model script threads runtime group_id); real desktop-surface session observed by a second process; busy-target queue-only delivery (host mode=queue, HELD on refusal, at-most-once dedup); trust-path validation branch coverage. Drift fixes locked by tests: desktop surface no longer collapses to tui; broadcast summary surfaces held. 20 new tests; 649 passed; ruff/ty clean; coverage gate PASS 90.5%.
P10: expand CI to ubuntu/macos/windows x py3.11-3.13 with coverage gate, wheel-asset verify, install-from-wheel smoke and a native-windows job for the gated suites (prepared locally; remote CI requires approval). New test_windows_native.py (P9.2 named-pipe exchange, P9.9 DACL denial) and scripts/verify_wheel_assets.py (ACC-17). Wheel built and smoke-installed into a disposable venv. Docs: architecture/security/compatibility extended; new windows, groups-and-broadcasts, request-workflows, desktop, operations, upgrade-v1-to-v1-1 guides; README/CHANGELOG updated. P10.8 limits documented; P10.9 no tag/publish/activate. Full suite 649 passed; ruff/ty clean; coverage gate PASS 90.6%.
…etrics coverage P11 adversarial probe found a fail-closed gap: a peer that accepts then immediately closes made sendall/recv raise bare ConnectionResetError, violating the transport contract that wire failures surface as UnreachableError. Both PeerClient.request and PosixTransportBackend now wrap OSError on send and recv. RED->GREEN tests: test_transport_send_reset, test_posix_backend_errors (non-unix endpoint, closed socket, wrong-owner, bound fence). New coverage for protocol_v2 payload validation (10) and metrics/health edge branches (5). Adds Octacon's read-only V1.1 vs A2A reconciliation audit.
… tests P11.1/P11.2: the trusted-branch denominator now includes every new protocol/backend/group/workflow/metrics/API module (28 total) and --cov=dashboard. Added fail-closed branch coverage for: posix backend rejection paths (credential fail, EOF, foreign-UID), protocol_v2 payload validation, groups/requests edge branches, v2 tool error handlers, desktop installer failure modes, dashboard API 503/404 branches, CLI dispatch table, sessions lifecycle edges. Gate now PASSES at 92.6% line / 85%+ branches on the expanded trust set. Windows backend branch (native import guard) stays native-gated and BLOCKED per the Windows evidence policy.
P11.8/P11.9: scripts/verify_v1_1_plus_completion.py checks real evidence only (git SHA/clean worktrees, plan checkboxes with an explicit allowed-blocked set, package assets, full suite, coverage gate, Windows evidence status) and returns 0=COMPLETE / 2=PARTIAL (windows-only) / 3=FAIL — no Markdown/parser placebo verdicts (P11.12). Review packet: HANDOFF (V1.1 final candidates), VERIFICATION, DEVIATIONS (Windows BLOCKED, CI gated, plan checkboxes ticked post-hoc with rationale), SECURITY, WINDOWS_EVIDENCE (BLOCKED), DESKTOP_EVIDENCE, completed-plan. Removed an 'or True' placebo assertion in test_desktop_no_auto_install.py.
SAFE-1: commit .hermes/environment.json (bootstrap uv sync --group dev, test .venv/bin/python -m pytest -q, start:null) so hermes verify stops auto-detecting a FastAPI/uvicorn app that does not exist; verified source=manifest, readiness=null, ok=true. SAFE-2: tools.py docstring now describes the real 10-tool V1.1 surface instead of the stale 'Exactly three tools in v1'. SAFE-3: events.py docstring states subscribers are retained and oldest buffered events are dropped, matching the deque(maxlen=...) behaviour.
GroupStore.add_member now honours INSERT OR IGNORE rowcount: an idempotent re-add returns False (as documented) instead of True. PeerSessionManager.group_add_member distinguishes an unknown group (ValueError) from an existing member (added:false), so tools and the Desktop API surface the same contract. RED tests cover store, manager, tool and dashboard paths; corrected the stale assertion in test_membership_crud that encoded the buggy True.
Added accepted->expired to the workflow transition table and included 'accepted' in RequestStore.expire_overdue. RED tests lock: accepted past deadline expires (count 1), future-deadline stays accepted, terminal states are never expired.
Column introspection (SELECT * FROM requests LIMIT 0) is now cached per RequestStore instead of re-running for every converted row. Test uses a delegating connection spy to prove exactly one introspection across multiple conversions; ordering and public Request objects preserved.
…CAREFUL-4) Replaced the per-member daemon-thread + Semaphore pattern with a ThreadPoolExecutor(max_workers=concurrency): identical bounded execution, less lifecycle code, deterministic per-member result ordering preserved. Full broadcast matrix green: unit (9), limits (security), runtime (integration), group-broadcast manager, idempotency property, and the real two-Hermes E2E.
_ws_upgrade_authorized now rejects the upgrade when hermes_cli.web_server import fails or the delegate raises. A broken production import can no longer become an authentication bypass. RED tests lock: missing module -> False, delegate error -> False, real delegate decides True/False, tests stay injectable via monkeypatch (never via import-absence). Updated the dashboard WS happy-path test to inject auth explicitly.
…KY-2/3) Manager now exposes resolve_session() (exact/single/unambiguous, raises when multiple active without an id), session_inbox(), session_requests(), group_members_list(), broadcast_outcomes(). Dashboard routes accept an optional session_id query parameter, reject with 400 when the selection is ambiguous or absent, and never read _peers/_group_store()/ _request_store()/_store._conn directly. RED tests lock the seam contract, the 400-on-ambiguous behaviour, and a source scan proving the Dashboard stays a thin adapter. Desktop state/API tests updated to the new contract; desktop E2E green.
Fresh-install smoke against the built wheel exposed that the transport backends subpackage (added P1) was never in the setuptools packages list — PeerSessionManager import failed from a clean wheel. verify_wheel_assets only globbed dist/ (stale P10 wheel) and checked 8 shallow paths, so it could not catch the omission. Now: packages includes agent_peer.backends; verify_wheel_assets accepts --wheel, enforces backends modules (12 paths) and fails on the stale-dist default; dashboard extra declares fastapi for the shipped plugin API router.
The dashboard optional extra (fastapi==0.115.6) added in 772b6a8 was not committed to the lockfile; uv sync regenerated it. Validate + commit so the tree is reproducible under --locked.
Reviewer remediation: 1. uv.lock committed (dashboard extra lockfile, previous commit). 2. .hermes/environment.json bootstrap now uv sync --locked --group dev so canonical verification cannot silently regenerate uv.lock. 3. verify_v1_1_plus_completion.py gains a post-gate-clean check after the full suite + coverage gate, listing any mutation the gates introduced. 4. tests/security/test_verification_no_mutation.py locks the property: verifier contains the post-gate check ordered after coverage-gate; suite collection leaves the tree untouched; manifest enforces --locked.
added 28 commits
August 13, 2026 02:03
The P2 fail-closed stubs raised NotImplementedError, so every test that closed a listener leaked the pipe handle. On the next bind of the same logical name, CreateNamedPipe returned ERROR_PIPE_BUSY (231, all instances busy) because the leaked instance was never released. Implement close()/close_fd() as win32file.CloseHandle; named pipes have no filesystem node so there is nothing to unlink. Verified on native-windows CI (cannot run locally on Linux — the ADR-0005 gate fails closed by design).
ty's unresolved-import rule treats the 'if sys.platform != win32: raise' guard as evidence the pywin32 imports are platform-gated, so _native() passes type check. close() lacked the gate and its lazy win32file import failed CI type check on all POSIX lanes. Mirror the _native() gate. Local: ty check all-checks-passed; unit/security/governance/integration suite green.
verify_remote_owner resolves the peer via GetNamedPipeClientProcessId, which requires a CONNECTED client. The tests called it on a freshly bound listener handle, so resolution failed and authenticated=False. Add the same ConnectNamedPipe/CreateFile cycle test_request_roundtrip already uses. test_teardown_never_touches_replacement: nMaxInstances=1 means binding a second instance while the first is open returns ERROR_PIPE_BUSY. Release the old instance (close_fd) first, then bind the replacement — matching the docstring's intended semantics. Windows-only tests; verified by the native-windows CI job (cannot run locally on Linux — ADR-0005 fail-closed).
verify_remote_owner passed the client PID straight to OpenProcessToken, which treats its first argument as a handle -> 'The handle is invalid' (6) -> OwnerEvidence(authenticated=False) on every connected client. Open the process with OpenProcess(PROCESS_QUERY_INFORMATION) first, mirroring _current_user_sid's use of GetCurrentProcess. Verified on native-windows CI (cannot run locally — ADR-0005 gate).
The constant is exported by win32con, not win32security; referencing it on the wrong module raised AttributeError inside the caught block, so every owner verification returned authenticated=False. Add win32con to the native namespace and use the named constant.
dir=/tmp broke the two-process E2E on Windows: FileNotFoundError 'C:/tmp/aps-...' because Windows has no /tmp directory. The AF_UNIX length fix only applies to POSIX (named pipes are not filesystem paths). Use /tmp on POSIX, the default temp dir on Windows. Local POSIX verification: transport/identity/paths suites pass.
The two-process E2E failed with JSONDecodeError on the child's ready line (empty stdout), but swallowed the child's stderr so the real cause was invisible. Include stderr in the assertion message for diagnosis.
The two-process named-pipe E2E crashed on Windows because several modules used POSIX-only owner primitives (os.geteuid / st_uid) that do not exist on Windows: - paths.py: same_owner() helper — True on non-POSIX (owner enforced by the OS via SID-bound DACLs / %LOCALAPPDATA%); _owner_suffix() uses the current user SID on Windows instead of geteuid - agent_identity.py / registry.py / discovery.py: use same_owner() instead of st_uid != os.geteuid() - transport.py: peer credentials carry uid=None on Windows; the SID DACL is the owner boundary (ADR-0005), verify_peer_credentials passes on non-POSIX instead of comparing a nonexistent uid - runtime.py: socket UID/inode capture only on POSIX (named pipes have no filesystem node); teardown stat fence still applies on POSIX Local: ty all-checks-passed; unit/security/governance/integration 728 pass. Windows behavior verified by the native-windows CI job.
same_owner() replaced the st_uid/os.geteuid call sites, leaving os unused in discovery.py (and the second file ruff flagged). ruff --fix removed both; full suite still 728 pass.
Named pipes have no pollable fd (by design), so the POSIX selector supervisor cannot serve them. Implement the documented P2 native-gate path: each listener gets a bounded daemon thread that blocks on ConnectNamedPipe, verifies the client SID via GetNamedPipeClientProcessId and services request/reply frames through the existing dispatch machinery. - backends/windows.py: _WindowsPipeConnection wrapper (recv/send from win32file.ReadFile/WriteFile); accept() implements ConnectNamedPipe; connection.close() is a no-op because the listener owns the single-instance handle (avoids double-CloseHandle) - runtime.py: _is_windows branch — register_peer starts the wait thread instead of the selector; _service_connection/_flush/_drop_connection/ _service_writable skip selector ops on Windows; unregister/shutdown join wait threads Local: ty all-checks-passed; 728 pass. Windows evidence from the native-windows CI job.
Two root causes for the Windows E2E 2h+ hang: 1. test_windows_native.py line 72: proc_b.stdout.readline() had no timeout. If Process B never printed its ready line, the test hung forever. Fix: use a daemon reader thread with a 60s join timeout; on timeout, kill B and surface its stderr for diagnostics. 2. WindowsTransportBackend.bound() used CreateFile to check pipe liveness. CreateFile opens a client-side pipe connection, which consumes the ConnectNamedPipe slot. If bound() is called before the listener thread reaches ConnectNamedPipe, this creates a deadlock. Fix: replace CreateFile with WaitNamedPipe, which only checks pipe availability without creating a client handle. Additional fix: runtime._backend_endpoint() hardcoded kind='unix' on all platforms. On Windows this meant bound() always returned False (wrong kind), bypassing the liveness check. Now returns kind='named-pipe' on Windows.
…test Same unbounded-readline hang as test_windows_native.py. If Process B/A never prints its ready line (e.g. due to the named-pipe deadlock), the test hangs forever. Apply the same daemon-thread-with-timeout pattern so the test fails fast with diagnostics.
- Replace os.pipe() with socket.socketpair() for wakeup mechanism. os.pipe() FDs are not selectable on Windows where SelectSelector only supports sockets. - Update _wakeup(), _run_loop drain, and shutdown() to use socket API (send/recv/close) instead of os.write/os.read/os.close. - Add stderr trace markers and traceback handler to E2E worker code in both test_windows_native.py and test_cross_platform_two_processes.py so CI failures show exactly where the hang occurs instead of empty stderr.
…r AV scan Diagnostic trace pinpointed the hang: register_peer reaches 'registering in registry' but never completes. os.fsync() maps to FlushFileBuffers on Windows, which blocks indefinitely when Windows Defender (active on CI runners) holds a scan lock on the temp file. fsync is belt-and-braces for crash durability; os.replace still guarantees atomicity without it. POSIX path unchanged.
Root cause: subprocess stderr pipe has a 4KB buffer on Windows. The E2E tests only read stderr AFTER the 60s timeout, so any stderr output from the worker (trace prints, warnings, logging) fills the buffer and blocks the subprocess on its next write to stderr — which looks exactly like a hang in register_peer. Fix: add a daemon thread that continuously drains stderr for both process B and process A in both E2E tests. Remove all diagnostic trace markers from runtime.py, registry.py, and worker code (they served their purpose — pinpointed the symptom to the registry write step, which led to the pipe buffer diagnosis). Also keeps the earlier fixes: - socket.socketpair() instead of os.pipe() (selectable on Windows) - WaitNamedPipe instead of CreateFile in bound() (no deadlock) - kind='named-pipe' fix in _backend_endpoint() - fsync skipped on Windows (FlushFileBuffers can block under AV)
stderr pipe is unreliable for diagnosis on Windows — even with concurrent drain, the subprocess may block before writing anything visible. Switch to file-based trace written to %TEMP%/agent_peer_trace.log which is independent of the subprocess pipe. Test reads the trace file on timeout failure and includes it in the AssertionError message.
…hread Root cause found via file-based trace: 1. os.fchmod(fd, 0o600) throws OSError on Windows (0o600 mode is meaningless — Windows only supports read-only bit) 2. The exception triggers register_peer's except-block cleanup 3. Cleanup calls _stop_windows_listener (3s join timeout) BEFORE listener.close() — but the thread is blocked on ConnectNamedPipe which only unblocks when CloseHandle is called, so the join always times out and subsequent cleanup deadlocks Fixes: - Skip os.fchmod on Windows (POSIX-only, like fsync) - On Windows, close the pipe handle FIRST to unblock ConnectNamedPipe, then join the thread - Remove all diagnostic trace code (root cause confirmed)
…ter_peer Same deadlock pattern as register_peer's except-block: on Windows, CloseHandle on a handle with pending synchronous ConnectNamedPipe can block or raise. Wrap in suppress(Exception) and close-before-join. Also bump subprocess wait timeout from 10s to 30s to give shutdown cleanup room.
…able shutdown The core deadlock: synchronous ConnectNamedPipe (NULL overlapped) blocks forever. CloseHandle from another thread on a handle with a pending synchronous pipe operation deadlocks — which is why shutdown() hung. Fix: switch to overlapped I/O: - CreateNamedPipe with FILE_FLAG_OVERLAPPED (already set in bind_listener) - ConnectNamedPipe with an OVERLAPPED structure - WaitForSingleObject(overlapped.hEvent, 500) — 500ms timeout - Return None on timeout so the wait loop can check _stop_event - The wait loop retries accept() until stop event is set This makes shutdown responsive: set _stop_event -> wait loop checks it within 500ms -> exits -> thread join completes immediately.
…d ReadFile/WriteFile The overlapped ConnectNamedPipe wasn't actually working because the pipe was created without FILE_FLAG_OVERLAPPED. Without this flag, overlapped structures are ignored and ConnectNamedPipe still blocks synchronously. Also update _WindowsPipeConnection.recv/send to use overlapped I/O — required because all I/O on a handle created with FILE_FLAG_OVERLAPPED must use overlapped structures (otherwise ERROR_INVALID_PARAMETER). Client-side CreateFile handle stays synchronous (no flag) — only the server-side pipe handle needs overlapped.
The E2E controller was a third, unrelated PeerRuntimeManager. send() correctly rejects its claimed alpha sender with ReceiptState.INVALID because alpha is registered only in the alpha process. Route the request over alpha's stdin and have alpha call its own registered runtime.send(). This exercises the intended A-to-B named-pipe path while preserving SEC-R1 sender authentication.
Windows does not enforce POSIX mode bits. _read_file previously rejected all registry JSON files where NTFS reports group/other mode bits, even though same-user ownership is already enforced by the SID-bound DACL. Apply the mode-bit check only on POSIX. Adds a RED/GREEN regression test simulating Windows mode semantics.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Walkie Talkie V1.1
Extends V1 with local groups/broadcasts, structured request/reply workflows, multi-agent orchestration, and a Dashboard API — a thin complement to existing Hermes agent A2A features.
What changed
Security
Direct security review by KENSEI found and fixed two issues:
PeerRuntimeManager.send()now authenticates sender against registered peers; unregistered senders rejected, registered senders identity-stamped from bound recordpeer_message_marker/peer_request_markersanitise sender names (strip newlines + boundary tags) and neutralise boundary tags in content/summaryVerification
hermes verifyok:true with locked bootstrapWindows
NATIVE WINDOWS GATE remains BLOCKED. 6 owner-boundary tests skipped. Status: IMPLEMENTED — WINDOWS RELEASE EVIDENCE BLOCKED.
A2A boundary
Walkie Talkie retains unique local session messaging value. Hermes A2A remains authoritative for remote/cross-machine agents, Agent Cards, HTTP JSON-RPC. No modules removed solely due to overlap.
Commits
V1.1 build (ffe3687) → remediation (f96f8b5) → round-2 remediation (f6081ef) → security fixes (0d5d02c)