@relaymessenger/pi 0.1.4-staging.1 → 0.1.4-staging.10

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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import Relay from "@relaymessenger/sdk";
1
+ import Relay, { type MessagePart } from "@relaymessenger/sdk";
2
2
  export interface PiChannelOptions {
3
3
  readonly agentToken: string;
4
4
  readonly baseURL?: string;
@@ -16,6 +16,22 @@ export interface PiProcess {
16
16
  readonly stdout: AsyncIterable<string>;
17
17
  readonly kill: () => void;
18
18
  }
19
+ /**
20
+ * The prompt pi is given for one message: the words, then how to answer.
21
+ * This process sends pi's final text for it, and the same buttons and link
22
+ * rules every other runtime carries.
23
+ */
24
+ export declare const piPrompt: (message: string) => string;
25
+ /**
26
+ * The messages an answer becomes: each link written alone on a line as its
27
+ * own message, text in chunks the API takes, and the buttons its fenced block
28
+ * asked for under the last words. A block pi wrote that cannot be read stays
29
+ * in the words, so nothing the person was told is lost.
30
+ */
31
+ export declare const answerMessages: (answer: string) => {
32
+ parts: MessagePart[];
33
+ error?: string;
34
+ }[];
19
35
  export declare class PiChannel {
20
36
  #private;
21
37
  constructor(options: PiChannelOptions);
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, {} from "@relaymessenger/sdk";
3
+ import Relay, { BUTTONS_BLOCK_INSTRUCTION, BUTTONS_GUIDANCE, 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,7 +11,38 @@ 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.filter((part) => part.type === "text" || part.type === "link").map((part) => part.value).join("\n").trim() || null;
14
+ return event.data.parts
15
+ .flatMap((part) => part.type === "text" || part.type === "link" ? [part.value] : [])
16
+ .join("\n").trim() || null;
17
+ };
18
+ /**
19
+ * The prompt pi is given for one message: the words, then how to answer.
20
+ * This process sends pi's final text for it, and the same buttons and link
21
+ * rules every other runtime carries.
22
+ */
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}`;
24
+ /**
25
+ * The messages an answer becomes: each link written alone on a line as its
26
+ * own message, text in chunks the API takes, and the buttons its fenced block
27
+ * asked for under the last words. A block pi wrote that cannot be read stays
28
+ * in the words, so nothing the person was told is lost.
29
+ */
30
+ export const answerMessages = (answer) => {
31
+ const split = splitAnswer(answer);
32
+ const messages = [];
33
+ for (const [first, ...rest] of split.messages) {
34
+ if (first?.type !== "text" || first.value.length <= 10_000) {
35
+ messages.push({ parts: first ? [first, ...rest] : rest });
36
+ continue;
37
+ }
38
+ const chunks = first.value.match(/[\s\S]{1,10000}/gu) ?? [];
39
+ for (const [index, chunk] of chunks.entries()) {
40
+ messages.push({ parts: [{ type: "text", value: chunk }, ...(index === chunks.length - 1 ? rest : [])] });
41
+ }
42
+ }
43
+ if (split.error && messages[0])
44
+ messages[0].error = split.error;
45
+ return messages;
15
46
  };
16
47
  class ChatSession {
17
48
  process;
@@ -122,7 +153,7 @@ export class PiChannel {
122
153
  this.#sessions.set(data.chat.id, session);
123
154
  }
124
155
  const timeout = this.#options.rpcTimeoutMs ?? 60_000;
125
- await session.command("prompt", { message }, timeout, signal);
156
+ await session.command("prompt", { message: piPrompt(message) }, timeout, signal);
126
157
  if (!session.settled) {
127
158
  while (!session.settled)
128
159
  await session.read(timeout, signal);
@@ -131,9 +162,11 @@ export class PiChannel {
131
162
  const answer = response.data?.text?.trim();
132
163
  if (!answer)
133
164
  throw new Error("Pi returned no final text answer");
134
- const chunks = answer.match(/[\s\S]{1,10000}/gu) ?? [];
135
- for (const [index, chunk] of chunks.entries())
136
- await this.#relay.chats.messages.send(data.chat.id, { message: { parts: [{ type: "text", value: chunk }], idempotency_key: `pi-${event.event_id}-${index}` } });
165
+ const messages = answerMessages(answer);
166
+ if (messages[0]?.error)
167
+ console.error(`Relay: the buttons block in pi's answer was left as text: ${messages[0].error}.`);
168
+ for (const [index, message] of messages.entries())
169
+ await this.#relay.chats.messages.send(data.chat.id, { message: { parts: message.parts, idempotency_key: `pi-${event.event_id}-${index}` } });
137
170
  }
138
171
  }
139
172
  export const runPiChannel = (options, signal) => new PiChannel(options).run(signal);
package/dist/native.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
  /**
3
3
  * Pi-native entry point. It deliberately exposes only lifecycle-safe commands;
4
4
  * Relay ingress remains owned by runPiChannel so the CLI and extension share
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relaymessenger/pi",
3
- "version": "0.1.4-staging.1",
3
+ "version": "0.1.4-staging.10",
4
4
  "description": "Relay Messenger channel for Pi over Relay v1 WebSocket delivery.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,11 +34,11 @@
34
34
  "test": "vitest run"
35
35
  },
36
36
  "dependencies": {
37
- "@relaymessenger/sdk": "0.3.6-staging.1"
37
+ "@relaymessenger/sdk": "0.3.6-staging.10"
38
38
  },
39
39
  "devDependencies": {
40
- "@mariozechner/pi-coding-agent": "0.73.1",
41
- "@types/node": "26.0.0",
40
+ "@earendil-works/pi-coding-agent": "0.86.0",
41
+ "@types/node": "26.6.2",
42
42
  "typescript": "7.0.2",
43
43
  "vitest": "4.1.11"
44
44
  },