@relaymessenger/pi 0.1.4-staging.4 → 0.1.4-staging.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/dist/index.d.ts +16 -1
- package/dist/index.js +33 -8
- package/package.json +2 -2
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,21 @@ 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 rules
|
|
22
|
+
* every other runtime carries.
|
|
23
|
+
*/
|
|
24
|
+
export declare const piPrompt: (message: string) => string;
|
|
25
|
+
/**
|
|
26
|
+
* The messages an answer becomes: text in chunks the API takes, and the
|
|
27
|
+
* buttons its fenced block asked for on the last one. A block pi wrote that
|
|
28
|
+
* cannot be read stays in the words, so nothing the person was told is lost.
|
|
29
|
+
*/
|
|
30
|
+
export declare const answerMessages: (answer: string) => {
|
|
31
|
+
parts: MessagePart[];
|
|
32
|
+
error?: string;
|
|
33
|
+
}[];
|
|
19
34
|
export declare class PiChannel {
|
|
20
35
|
#private;
|
|
21
36
|
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, splitButtons, } 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(); }
|
|
@@ -12,11 +12,34 @@ const textFromEvent = (event) => {
|
|
|
12
12
|
if (event.event_type !== "message.received" || event.data.direction !== "inbound")
|
|
13
13
|
return null;
|
|
14
14
|
return event.data.parts
|
|
15
|
-
.flatMap((part) => part.type === "text" || part.type === "link"
|
|
16
|
-
? [part.value]
|
|
17
|
-
: part.type === "button_reply" ? [part.label] : [])
|
|
15
|
+
.flatMap((part) => part.type === "text" || part.type === "link" ? [part.value] : [])
|
|
18
16
|
.join("\n").trim() || null;
|
|
19
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 rules
|
|
21
|
+
* 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} ${BUTTONS_GUIDANCE}`;
|
|
24
|
+
/**
|
|
25
|
+
* The messages an answer becomes: text in chunks the API takes, and the
|
|
26
|
+
* buttons its fenced block asked for on the last one. A block pi wrote that
|
|
27
|
+
* cannot be read stays in the words, so nothing the person was told is lost.
|
|
28
|
+
*/
|
|
29
|
+
export const answerMessages = (answer) => {
|
|
30
|
+
const { text, buttons, error } = splitButtons(answer);
|
|
31
|
+
const chunks = text.match(/[\s\S]{1,10000}/gu) ?? [];
|
|
32
|
+
const messages = chunks.map((chunk) => ({ parts: [{ type: "text", value: chunk }] }));
|
|
33
|
+
if (buttons) {
|
|
34
|
+
if (messages.length === 0)
|
|
35
|
+
messages.push({ parts: [buttons] });
|
|
36
|
+
else
|
|
37
|
+
messages[messages.length - 1].parts.push(buttons);
|
|
38
|
+
}
|
|
39
|
+
if (error && messages[0])
|
|
40
|
+
messages[0].error = error;
|
|
41
|
+
return messages;
|
|
42
|
+
};
|
|
20
43
|
class ChatSession {
|
|
21
44
|
process;
|
|
22
45
|
lines;
|
|
@@ -126,7 +149,7 @@ export class PiChannel {
|
|
|
126
149
|
this.#sessions.set(data.chat.id, session);
|
|
127
150
|
}
|
|
128
151
|
const timeout = this.#options.rpcTimeoutMs ?? 60_000;
|
|
129
|
-
await session.command("prompt", { message }, timeout, signal);
|
|
152
|
+
await session.command("prompt", { message: piPrompt(message) }, timeout, signal);
|
|
130
153
|
if (!session.settled) {
|
|
131
154
|
while (!session.settled)
|
|
132
155
|
await session.read(timeout, signal);
|
|
@@ -135,9 +158,11 @@ export class PiChannel {
|
|
|
135
158
|
const answer = response.data?.text?.trim();
|
|
136
159
|
if (!answer)
|
|
137
160
|
throw new Error("Pi returned no final text answer");
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
161
|
+
const messages = answerMessages(answer);
|
|
162
|
+
if (messages[0]?.error)
|
|
163
|
+
console.error(`Relay: the buttons block in pi's answer was left as text: ${messages[0].error}.`);
|
|
164
|
+
for (const [index, message] of messages.entries())
|
|
165
|
+
await this.#relay.chats.messages.send(data.chat.id, { message: { parts: message.parts, idempotency_key: `pi-${event.event_id}-${index}` } });
|
|
141
166
|
}
|
|
142
167
|
}
|
|
143
168
|
export const runPiChannel = (options, signal) => new PiChannel(options).run(signal);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@relaymessenger/pi",
|
|
3
|
-
"version": "0.1.4-staging.
|
|
3
|
+
"version": "0.1.4-staging.5",
|
|
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.
|
|
37
|
+
"@relaymessenger/sdk": "0.3.6-staging.5"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@mariozechner/pi-coding-agent": "0.73.1",
|