Commit graph

4 commits

Author SHA1 Message Date
Drexuxux
27b31bb7ff fix(relay): normalize a 0/negative max_message_length at the descriptor boundary
Follow-up to the truncate_message split-loop floor. Two review points:

- CapabilityDescriptor.from_json trusted the wire max_message_length
  verbatim, so a connector advertising 0 ('no limit') — or a buggy one
  sending 0/negative — produced a descriptor whose bound flowed straight
  into the adapter's MAX_MESSAGE_LENGTH and truncate_message. Normalize
  it to the documented 4096 default (mirrors from_platform_entry's
  'or 4096' and docs/relay-connector-contract.md), fixing the degenerate
  budget at its source rather than only surviving it downstream.

- Document the truncate_message length contract for a budget too small
  for one codepoint (max_length=1 with a 2-unit surrogate pair under
  utf16_len): the chunk intentionally exceeds max_length by that one
  indivisible codepoint, because emitting it whole preserves content
  where the alternatives are data loss or an infinite loop.

Tests: from_json normalizes 0 and negative bounds to 4096 and passes a
real positive bound through unchanged; the sub-codepoint budget emits
whole codepoints with no data loss (all emojis preserved) and a chunk
that necessarily exceeds the 1-unit budget.
2026-07-16 07:21:23 -07:00
Ben Barclay
bb5fc723b6
feat(relay): consume channel context from the connector (#64649)
Phase 3 of relay-channel-context (gateway/agent side, single PR). The
connector (gateway-gateway #122/#123/#124) now attaches read-only
surrounding channel/group context to an addressed relay turn; this wires
the gateway to consume it.

- descriptor.py: additive optional supports_context (default False) on
  CapabilityDescriptor. from_json already filters unknown keys, so this is
  back-compat both directions within contract_version 1.
- ws_transport.py: _event_from_wire maps the connector's read-only
  context[] array into the EXISTING MessageEvent.channel_context field via
  a new _render_relay_context() helper — reusing the same read-only
  injection path history-backfill uses (run.py prepends channel_context
  ahead of the trigger message). Never raises; absent/empty/malformed ->
  channel_context unset (byte-identical to today).
- docs/relay-connector-contract.md: document supports_context in the §2
  descriptor table (fixes the contract-doc conformance test) + the
  context/context_error inbound fields in §3.
- tests: descriptor default/round-trip/forward-compat; _render_relay_context
  rendering + malformed-safe; _event_from_wire context->channel_context
  mapping + the read-only invariant (trigger text untouched).

RELAY-ONLY: only gateway/relay/* + the shared MessageEvent consumption via
its existing channel_context field. No native adapter touched.
2026-07-15 09:31:34 +10:00
Ben
3db49381d6 feat(relay): derive descriptor from PlatformEntry
CapabilityDescriptor.from_platform_entry() projects an existing PlatformEntry
(label, max_message_length, emoji, platform_hint, pii_safe, name) into a
descriptor, proving the descriptor is a projection of existing config rather
than a parallel concept. Runtime-only capabilities (len_unit, draft/edit/
thread/markdown) are caller-supplied. max_message_length==0 ('no limit') maps
to the stream_consumer 4096 default.

Phase 0 complete. Task 0.3 of the gateway-relay plan.
2026-06-17 16:37:45 -07:00
Ben
53d9b98305 feat(relay): experimental CapabilityDescriptor schema
Frozen, JSON-serializable handshake payload the connector hands the future
RelayAdapter: char limit, draft-streaming/edit/threading flags, markdown
dialect, len_unit. Mostly a wire projection of PlatformEntry + the adapter
capability methods. contract_version gates additive-only evolution; declared
EXPERIMENTAL until >=2 Class-1 platforms validate it. from_json ignores
unknown keys (forward-compat) and fills optional defaults.

Phase 0, Task 0.2 of the gateway-relay plan.
2026-06-17 16:37:45 -07:00