@khanglvm/relay 0.13.4 → 0.13.5

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.
package/README.md CHANGED
@@ -61,10 +61,11 @@ rly mcp config
61
61
 
62
62
  When the agent calls a relay tool, the host renders relay's `ui://relay/board`
63
63
  resource in a sandboxed iframe, hands it the board spec, and the user's answers
64
- flow back to the agent over the bridge (`ui/update-model-context`) markdown,
65
- code, diffs, tables, charts, mermaid/graphviz diagrams, images, and forms, all
66
- in-chat. The classic browser board (`rly ask` / `rly show`) is unchanged; pick
67
- whichever surface fits.
64
+ flow back to the agent as a `ui/message` user turn, with
65
+ `ui/update-model-context` used as a best-effort structured context sync
66
+ markdown, code, diffs, tables, charts, mermaid/graphviz diagrams, images, and
67
+ forms, all in-chat. The classic browser board (`rly ask` / `rly show`) is
68
+ unchanged; pick whichever surface fits.
68
69
 
69
70
  ## What it improves
70
71
 
@@ -114,10 +115,11 @@ npm test # zero-dep smoke tests (spawns real servers, fake-submits)
114
115
  - **Same board, postMessage transport.** The inline board reuses relay's block
115
116
  renderer (markdown, code, diff, table, chart, mermaid, graphviz, image, html)
116
117
  over the MCP Apps JSON-RPC bridge: the spec arrives as the tool result, the
117
- user's answers go back via `ui/update-model-context`, the iframe auto-sizes
118
- via `ui/notifications/size-changed`, and vendored Chart.js / Mermaid / Viz.js
119
- load on demand through the host's `resources/read` (no `/vendor` route, no
120
- server in the sandbox).
118
+ user's submit goes back via `ui/message` so the agent resumes, the structured
119
+ payload is also offered through `ui/update-model-context`, the iframe
120
+ auto-sizes via `ui/notifications/size-changed`, and vendored Chart.js /
121
+ Mermaid / Viz.js load on demand through the host's `resources/read` (no
122
+ `/vendor` route, no server in the sandbox).
121
123
  - **One-command setup** — `rly mcp install --target claude|codex` writes the
122
124
  host config; `rly mcp config` prints the snippet for any MCP host. The classic
123
125
  browser board is untouched.
package/docs/AGENT.md CHANGED
@@ -111,7 +111,8 @@ finalizes when your call completes — so the user sees it build, not a blank wa
111
111
  Both take **the exact same board spec** documented below (the tool `inputSchema`
112
112
  *is* this spec). Call the tool with your spec; the host shows the board, the user
113
113
  fills it in, and their answers come back to you (answers, per-question notes,
114
- comment) read them just as you would the CLI's result JSON. There is **no
114
+ comment) as a user-message turn, with structured context synced when the host
115
+ supports it — read them just as you would the CLI's result JSON. There is **no
115
116
  `--detach`/`rly wait` dance, no stdout parsing, and no timeout** in this mode; the
116
117
  board stays live until the user submits and the host delivers the result.
117
118
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanglvm/relay",
3
- "version": "0.13.4",
3
+ "version": "0.13.5",
4
4
  "description": "Question boards with rich blocks (markdown, charts, mermaid, tables, code, diffs, video, sandboxed HTML), clickable local file-links, and element-level annotations for AI coding agents (Claude Code, Codex, …): ask users structured questions, present interactive visuals, collect inline comments, read answers as JSON — in a local browser board OR rendered INLINE inside the Claude & Codex apps as an MCP App (SEP-1865).",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -4,8 +4,9 @@
4
4
  // host app (Claude desktop/mobile, Codex, …) as a sandboxed inline iframe.
5
5
  // There is no local HTTP server here: every exchange with the host travels over
6
6
  // JSON-RPC on window.postMessage — the spec arrives as the tool result, the
7
- // user's answers go back via `ui/update-model-context`, and vendored libraries
8
- // (Chart.js / Mermaid / Viz.js) are pulled through the host's `resources/read`.
7
+ // user's final submission goes back as a `ui/message` user turn, and vendored
8
+ // libraries (Chart.js / Mermaid / Viz.js) are pulled through the host's
9
+ // `resources/read`.
9
10
  //
10
11
  // Rich blocks are rendered by the SAME window.RelayBlocks as the browser board
11
12
  // (markdown, code, diff, table, chart, mermaid, graphviz, image, html), so the
@@ -957,19 +958,30 @@
957
958
  annotations: data.annotations,
958
959
  };
959
960
  const text = summarize(data);
960
- let delivered = false;
961
+ let messageDelivered = false;
962
+ let contextDelivered = false;
961
963
  try {
964
+ // A completed relay form is a user reply, not passive context. Some hosts
965
+ // ACK `ui/update-model-context` without starting a new model turn, so send
966
+ // the transcript as a user message first to wake the agent reliably.
967
+ await request('ui/message', { role: 'user', content: { type: 'text', text } });
968
+ messageDelivered = true;
969
+ } catch {
970
+ // Older/leaner hosts may not expose app-initiated messages.
971
+ }
972
+ try {
973
+ // Keep the structured payload available to hosts that attach app context.
974
+ // This is best-effort because context updates are intentionally silent.
962
975
  await request('ui/update-model-context', { content: [{ type: 'text', text }], structuredContent: structured });
963
- delivered = true;
976
+ contextDelivered = true;
964
977
  } catch {
965
- // Fallback for hosts without update-model-context: post a chat message.
966
- try { await request('ui/message', { role: 'user', content: { type: 'text', text } }); delivered = true; } catch { /* give up gracefully */ }
978
+ // If ui/message worked, the agent still receives the submission transcript.
967
979
  }
968
980
  submitted = true;
969
- showDone(delivered);
981
+ showDone(messageDelivered, contextDelivered);
970
982
  }
971
983
 
972
- function showDone(delivered) {
984
+ function showDone(messageDelivered, contextDelivered) {
973
985
  // Collapse: leave fullscreen, drop the whole form for a one-line confirmation
974
986
  // so the host shrinks the iframe to a small footprint in the transcript.
975
987
  if (Annotate) { try { Annotate.teardown(); } catch { /* nothing to tear down */ } }
@@ -977,8 +989,10 @@
977
989
  app.replaceChildren(el('div', { class: 'mcp-done' },
978
990
  el('span', { class: 'mark' }, '✓'),
979
991
  el('span', { class: 'lead' }, QS.length ? 'Submitted' : 'Acknowledged'),
980
- el('span', { class: 'sub' }, delivered
992
+ el('span', { class: 'sub' }, messageDelivered
981
993
  ? '· sent back to the agent'
994
+ : contextDelivered
995
+ ? '· saved; send the agent a message to continue'
982
996
  : '· tell the agent you’ve responded')));
983
997
  // Report the small height immediately, then again next frame / after layout
984
998
  // settles — beats hosts that only grow on debounced size events.
package/src/mcp.js CHANGED
@@ -8,8 +8,8 @@
8
8
  // • the tools `relay_ask` / `relay_show` link to it via _meta.ui.resourceUri
9
9
  // • calling a tool returns the (normalized) board spec as structuredContent;
10
10
  // the host renders the resource in a sandboxed iframe and forwards the spec
11
- // • the iframe collects the user's answers and sends them back to the model
12
- // via `ui/update-model-context`
11
+ // • the iframe collects the user's answers and sends them back as a user
12
+ // message; it also syncs model context when the host supports it
13
13
  //
14
14
  // Framing is the MCP stdio transport: newline-delimited JSON-RPC, one message
15
15
  // per line, never embedded newlines. stdout carries ONLY protocol messages;
@@ -156,7 +156,7 @@ function buildResult(method, params, clientProtocol) {
156
156
  },
157
157
  serverInfo: { name: 'relay', version: PKG.version },
158
158
  instructions:
159
- 'relay renders interactive boards inline. Call relay_ask to collect decisions/feedback with real form controls, or relay_show to present plans/diagrams/data — instead of asking in plain text. Read the user\'s answers from the structuredContent that returns after they submit.',
159
+ 'relay renders interactive boards inline. Call relay_ask to collect decisions/feedback with real form controls, or relay_show to present plans/diagrams/data — instead of asking in plain text. When the user submits, relay sends their answers back as a user message so the agent continues.',
160
160
  };
161
161
  case 'ping':
162
162
  return {};
@@ -198,9 +198,10 @@ function readResource(params) {
198
198
 
199
199
  // A tool call: normalize the spec and hand it to the host as structuredContent.
200
200
  // The host renders ui://relay/board and forwards this spec to the iframe, which
201
- // collects the answers and returns them via ui/update-model-context. Spec errors
202
- // come back as an isError tool result (not a protocol error) so the model can
203
- // see and fix them.
201
+ // collects the answers and returns them via a UI-originated user message (with
202
+ // ui/update-model-context as a best-effort context sync). Spec errors come back
203
+ // as an isError tool result (not a protocol error) so the model can see and fix
204
+ // them.
204
205
  function callTool(params) {
205
206
  const name = params && params.name;
206
207
  if (name !== 'relay_ask' && name !== 'relay_show') {