@trim21/personal-pi-extensions 0.0.199 → 0.0.201
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 +1 -2
- package/package.json +1 -1
- package/src/talk/core.ts +2 -48
- package/src/talk/index.ts +0 -18
package/README.md
CHANGED
|
@@ -234,7 +234,6 @@ index.ts —— pi adapter:把 core 接到 pi 的 sendMessage / 生命周
|
|
|
234
234
|
| -------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
235
235
|
| `talk-list-sessions` | 列出其他 session,返回 JSON 数组(`status` / `work_dir` / `id` / `name`);默认只列有心跳的,`includeOffline: true` 列全部 |
|
|
236
236
|
| `talk-ask` | 向某个 session 提问并阻塞等待回复(默认 30 分钟超时) |
|
|
237
|
-
| `talk-wait` | 阻塞等待新消息到达 |
|
|
238
237
|
| `talk-send` | 发送纯文本消息(`to: "*"` 广播所有,`to: "cwd"` 广播同 cwd) |
|
|
239
238
|
| `talk-reply` | 回复一个 ask(`replyTo` 为 ask id,显式关联、不推断) |
|
|
240
239
|
|
|
@@ -246,7 +245,7 @@ index.ts —— pi adapter:把 core 接到 pi 的 sendMessage / 生命周
|
|
|
246
245
|
|
|
247
246
|
### 关键设计
|
|
248
247
|
|
|
249
|
-
- **心跳即活跃**:每个 session 每 15s 写一次 `lastSeenAt`;`talk-list-sessions` 默认只显示最近 15 分钟内有心跳的 session,已结束/挂起的 session 自动从默认列表消失(`includeOffline: true` 可见全部)。`status` 用 45s 心跳阈值区分 live / not responding / offline,并显示 `working` / `waiting-talk-message`(`talk-
|
|
248
|
+
- **心跳即活跃**:每个 session 每 15s 写一次 `lastSeenAt`;`talk-list-sessions` 默认只显示最近 15 分钟内有心跳的 session,已结束/挂起的 session 自动从默认列表消失(`includeOffline: true` 可见全部)。`status` 用 45s 心跳阈值区分 live / not responding / offline,并显示 `working` / `waiting-talk-message`(`talk-ask` 阻塞等待回复中)/ `idle`。
|
|
250
249
|
- **定期清理**:心跳停止超过 24h 且无未投递 mail 的记录会被定期 sweep(30 分钟一次)回收;有 mail 的保留 30 天。resume 后 session 会自动重新注册,无 mail 即无损失。
|
|
251
250
|
- **投递成功才消费**:信件只在成功交给 `sendMessage` 后才从 inbox 删除,投递失败留在 inbox 下次重试——不会因 `sendMessage` 吞异常而静默丢信。
|
|
252
251
|
- **双向 ask 仲裁**:`talk-ask` 发起前先检查收件箱(有对方消息就先读/先回);阻塞等待期间若收到对方的 ask(而非 reply),按两个 ask 的 `ts` 字段仲裁——先 ask 者主导继续等,后 ask 者让位并先回复对方。`ts` 是信件内固定字段,双方读到同一对值,结论天然对称;同毫秒碰撞用 `session dir + session id` 字符串比较兜底。
|
package/package.json
CHANGED
package/src/talk/core.ts
CHANGED
|
@@ -12,13 +12,12 @@
|
|
|
12
12
|
import { isAbsolute, resolve } from "node:path";
|
|
13
13
|
|
|
14
14
|
import { expandHome } from "../lib/path.js";
|
|
15
|
-
import {
|
|
15
|
+
import { formatListing, refusalUnknown, shortAddr } from "./format.js";
|
|
16
16
|
import {
|
|
17
17
|
appendAudit,
|
|
18
18
|
awaitReceipt,
|
|
19
19
|
clearAsk,
|
|
20
20
|
deposit,
|
|
21
|
-
type InboxItem,
|
|
22
21
|
type Letter,
|
|
23
22
|
type LetterKind,
|
|
24
23
|
listInbox,
|
|
@@ -69,11 +68,8 @@ const HEARTBEAT_MS = 15_000;
|
|
|
69
68
|
const WATCH_POLL_MS = 5000;
|
|
70
69
|
const DELIVERY_BACKOFF_MS = 5000;
|
|
71
70
|
const INITIAL_DRAIN_DELAY_MS = 1200;
|
|
72
|
-
const WAIT_POLL_MS = 500;
|
|
73
71
|
const SWEEP_INTERVAL_MS = 30 * 60 * 1000;
|
|
74
72
|
|
|
75
|
-
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
76
|
-
|
|
77
73
|
/** Normalize an allowed path: expand ~, resolve relative against baseCwd, strip trailing slashes. */
|
|
78
74
|
function normalizeAllowedPath(p: string, baseCwd: string): string {
|
|
79
75
|
const expanded = expandHome(p);
|
|
@@ -205,7 +201,7 @@ export class TalkCore {
|
|
|
205
201
|
void this.writeSelf({ status: "working" });
|
|
206
202
|
}
|
|
207
203
|
|
|
208
|
-
/** Blocked on talk-
|
|
204
|
+
/** Blocked on talk-ask — visible as "waiting-talk-message". */
|
|
209
205
|
setWaiting(): void {
|
|
210
206
|
void this.writeSelf({ status: "waiting-talk-message" });
|
|
211
207
|
}
|
|
@@ -325,28 +321,6 @@ export class TalkCore {
|
|
|
325
321
|
}
|
|
326
322
|
}
|
|
327
323
|
|
|
328
|
-
/** Read and consume fresh inbox letters, returning those to present to the model. */
|
|
329
|
-
private async consumeFresh(items: InboxItem[]): Promise<Letter[]> {
|
|
330
|
-
const self = this.requireSelf();
|
|
331
|
-
const fresh: Letter[] = [];
|
|
332
|
-
for (const item of items) {
|
|
333
|
-
if (this.deliveredIds.has(item.letter.id)) {
|
|
334
|
-
// Already handed out in a previous pass but the remove failed; only remove.
|
|
335
|
-
if (await removeLetter(this.storage, self.addr, item.fileName)) {
|
|
336
|
-
this.deliveredIds.delete(item.letter.id);
|
|
337
|
-
}
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
if (await this.processIncoming(item.letter)) fresh.push(item.letter);
|
|
341
|
-
if (await removeLetter(this.storage, self.addr, item.fileName)) {
|
|
342
|
-
// consumed
|
|
343
|
-
} else {
|
|
344
|
-
this.deliveredIds.add(item.letter.id);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
return fresh;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
324
|
// ── Outbound ───────────────────────────────────────────────────────────
|
|
351
325
|
|
|
352
326
|
/**
|
|
@@ -474,26 +448,6 @@ export class TalkCore {
|
|
|
474
448
|
|
|
475
449
|
// ── Tool actions ───────────────────────────────────────────────────────
|
|
476
450
|
|
|
477
|
-
/** Block until a message arrives (or timeout/abort). */
|
|
478
|
-
async wait(timeoutMs: number, signal?: AbortSignal): Promise<string> {
|
|
479
|
-
const self = this.requireSelf();
|
|
480
|
-
const deadline = this.now() + timeoutMs;
|
|
481
|
-
this.setWaiting();
|
|
482
|
-
try {
|
|
483
|
-
for (;;) {
|
|
484
|
-
const inbox = await listInbox(this.storage, self.addr);
|
|
485
|
-
const fresh = await this.consumeFresh(inbox);
|
|
486
|
-
if (fresh.length > 0) return fresh.map((l) => formatDelivery(l)).join("\n\n");
|
|
487
|
-
if (signal?.aborted) return "aborted";
|
|
488
|
-
if (this.now() >= deadline) return `No message within ${Math.round(timeoutMs / 1000)}s.`;
|
|
489
|
-
await sleep(WAIT_POLL_MS);
|
|
490
|
-
}
|
|
491
|
-
} finally {
|
|
492
|
-
// The tool call is still part of a running agent turn.
|
|
493
|
-
this.setWorking();
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
451
|
/**
|
|
498
452
|
* JSON listing of visible peer sessions. Defaults to sessions whose
|
|
499
453
|
* heartbeat is fresh (within LIST_ACTIVE_MS); pass includeOffline to show
|
package/src/talk/index.ts
CHANGED
|
@@ -260,24 +260,6 @@ export default function talk(pi: ExtensionAPI) {
|
|
|
260
260
|
},
|
|
261
261
|
});
|
|
262
262
|
|
|
263
|
-
pi.registerTool({
|
|
264
|
-
name: "talk-wait",
|
|
265
|
-
label: "Wait for Talk Message",
|
|
266
|
-
description:
|
|
267
|
-
"Block until a new talk message arrives in your inbox (or until the timeout). Returns the message(s) that arrived.",
|
|
268
|
-
promptSnippet: "Wait for an incoming talk message",
|
|
269
|
-
parameters: Type.Object({
|
|
270
|
-
timeoutMs: Type.Optional(
|
|
271
|
-
Type.Number({ description: `How long to block in ms; default ${ASK_TIMEOUT_MS}` }),
|
|
272
|
-
),
|
|
273
|
-
}),
|
|
274
|
-
async execute(_toolCallId, params, signal) {
|
|
275
|
-
const initError = requireInit();
|
|
276
|
-
if (initError) return toolResult(initError);
|
|
277
|
-
return toolResult(await core.wait(params.timeoutMs ?? ASK_TIMEOUT_MS, signal ?? undefined));
|
|
278
|
-
},
|
|
279
|
-
});
|
|
280
|
-
|
|
281
263
|
pi.registerTool({
|
|
282
264
|
name: "talk-send",
|
|
283
265
|
label: "Send Talk Message",
|