@relaymessenger/pi 0.1.4-staging.12 → 0.1.4-staging.14

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.
Files changed (3) hide show
  1. package/README.md +16 -0
  2. package/dist/index.js +15 -5
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -15,3 +15,19 @@ pi install npm:@relaymessenger/pi
15
15
 
16
16
  Set `RELAY_AGENT_TOKEN` before using `/relay-connect`. Set
17
17
  `RELAY_BASE_URL` when the Agent belongs to a non-default API environment.
18
+
19
+ ## Selection, coming soon
20
+
21
+ These are local candidate sources, not a claim that the published package or
22
+ hosted API supports selection yet.
23
+
24
+ End the final Pi answer with a `selection` JSON fence after the question.
25
+ The RPC prompt preserves ordered rich parts, `selected_values`, and `reply_to`
26
+ as data. FULL sync still fails closed rather than discarding skipped context.
27
+
28
+ New human reply text is literal `• ` + each selected source label joined with
29
+ `\n`, followed by `selection_response` metadata in source-option order. Dispatch
30
+ with `selected_values` and the explicit source target, never label parsing.
31
+ Exact legacy comma-joined text remains a server compatibility input. Tapping a
32
+ selected option deselects it locally; the sole submit action is a centered
33
+ compact light-blue Send button. iOS checked circles are presentation only.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { createInterface } from "node:readline";
3
- import Relay, { BUTTONS_BLOCK_INSTRUCTION, BUTTONS_GUIDANCE, LINK_LINE_INSTRUCTION, answerMessages as splitAnswer, } from "@relaymessenger/sdk";
3
+ import Relay, { BUTTONS_BLOCK_INSTRUCTION, BUTTONS_GUIDANCE, selectionReply, selectionReplyContext, SELECTION_GUIDANCE, SELECTION_BLOCK_INSTRUCTION, LINK_LINE_INSTRUCTION, answerMessages as splitAnswer, } from "@relaymessenger/sdk";
4
4
  class ChildPiProcess {
5
5
  #child;
6
6
  constructor(command, args) { this.#child = spawn(command, [...args], { stdio: ["pipe", "pipe", "pipe"] }); this.#child.stderr.resume(); }
@@ -11,16 +11,22 @@ class ChildPiProcess {
11
11
  const textFromEvent = (event) => {
12
12
  if (event.event_type !== "message.received" || event.data.direction !== "inbound")
13
13
  return null;
14
- return event.data.parts
14
+ const text = event.data.parts
15
15
  .flatMap((part) => part.type === "text" || part.type === "link" ? [part.value] : [])
16
- .join("\n").trim() || null;
16
+ .join("\n").trim();
17
+ const context = selectionReplyContext(selectionReply(event.data.parts, event.data.reply_to), {
18
+ parts: event.data.parts, ...(event.data.reply_to ? { reply_to: event.data.reply_to } : {}),
19
+ });
20
+ if (!text && !context)
21
+ return null;
22
+ return [text, context].filter(Boolean).join("\n\n");
17
23
  };
18
24
  /**
19
25
  * The prompt pi is given for one message: the words, then how to answer.
20
26
  * This process sends pi's final text for it, and the same buttons and link
21
27
  * rules every other runtime carries.
22
28
  */
23
- export const piPrompt = (message) => `${message}\n\nWrite your answer as your final message. Relay sends that answer to the chat for you, so do not send it yourself. Write chat text. Inline Markdown draws: bold, italic, strikethrough, code, links. Headings, lists and code fences show as written.\n\n${BUTTONS_BLOCK_INSTRUCTION} ${LINK_LINE_INSTRUCTION} ${BUTTONS_GUIDANCE}`;
29
+ export const piPrompt = (message) => `${message}\n\nWrite your answer as your final message. Relay sends that answer to the chat for you, so do not send it yourself. Write chat text. Inline Markdown draws: bold, italic, strikethrough, code, links. Headings, lists and code fences show as written.\n\n${BUTTONS_BLOCK_INSTRUCTION} ${LINK_LINE_INSTRUCTION} ${BUTTONS_GUIDANCE} ${SELECTION_BLOCK_INSTRUCTION} ${SELECTION_GUIDANCE}`;
24
30
  /**
25
31
  * The messages an answer becomes: each link written alone on a line as its
26
32
  * own message, text in chunks the API takes, and the buttons its fenced block
@@ -123,6 +129,10 @@ export class PiChannel {
123
129
  stop() { for (const session of this.#sessions.values())
124
130
  session.stop(); this.#sessions.clear(); }
125
131
  async #handle(event, signal) {
132
+ // Concurrent redelivery must await the original handoff, not ACK early.
133
+ const inflight = this.#inflight.get(event.event_id);
134
+ if (inflight)
135
+ return inflight;
126
136
  if (this.#seen.has(event.event_id))
127
137
  return;
128
138
  const message = textFromEvent(event);
@@ -164,7 +174,7 @@ export class PiChannel {
164
174
  throw new Error("Pi returned no final text answer");
165
175
  const messages = answerMessages(answer);
166
176
  if (messages[0]?.error)
167
- console.error(`Relay: the buttons block in pi's answer was left as text: ${messages[0].error}.`);
177
+ console.error(`Relay: the component block in pi's answer was left as text: ${messages[0].error}.`);
168
178
  for (const [index, message] of messages.entries())
169
179
  await this.#relay.chats.messages.send(data.chat.id, { message: { parts: message.parts, idempotency_key: `pi-${event.event_id}-${index}` } });
170
180
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relaymessenger/pi",
3
- "version": "0.1.4-staging.12",
3
+ "version": "0.1.4-staging.14",
4
4
  "description": "Relay Messenger channel for Pi over Relay v1 WebSocket delivery.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,7 +34,7 @@
34
34
  "test": "vitest run"
35
35
  },
36
36
  "dependencies": {
37
- "@relaymessenger/sdk": "0.3.6-staging.12"
37
+ "@relaymessenger/sdk": "0.3.6-staging.14"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@earendil-works/pi-coding-agent": "0.86.0",