@trim21/personal-pi-extensions 0.0.193 → 0.0.195
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 +65 -0
- package/package.json +3 -2
- package/src/bwrap/index.ts +3 -10
- package/src/lib/path.ts +28 -0
- package/src/talk/core.ts +630 -0
- package/src/talk/format.ts +54 -0
- package/src/talk/index.ts +354 -0
- package/src/talk/mailbox.ts +306 -0
- package/src/talk/policy.ts +84 -0
- package/src/talk/registry.ts +148 -0
- package/src/talk/storage.ts +142 -0
- package/src/workspace-guard.ts +4 -15
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
| [vision-agent](#vision-agent) | 视觉代理:主模型不支持视觉时,spawn 子 agent 识别图片 |
|
|
14
14
|
| [todowrite](#todowrite) | opencode 风格的任务列表工具,完整列表替换语义 |
|
|
15
15
|
| [question](#question) | opencode 风格的提问工具,阻塞式询问用户选择 |
|
|
16
|
+
| [talk](#talk) | session 间消息传递,SQLite 邮箱 + 双向 ask 时间戳仲裁 |
|
|
16
17
|
|
|
17
18
|
---
|
|
18
19
|
|
|
@@ -213,6 +214,70 @@ pi -e ./src/question.ts
|
|
|
213
214
|
|
|
214
215
|
---
|
|
215
216
|
|
|
217
|
+
## talk
|
|
218
|
+
|
|
219
|
+
session 间消息传递:不同 pi session(同一台机器)通过一个共享的 SQLite 邮箱互相发送消息、提问并等待回复。
|
|
220
|
+
|
|
221
|
+
### 架构(三层)
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
storage.ts —— 存储层:TalkStorage 接口 + SqliteTalkStorage 实现(node:sqlite,零 npm 依赖)
|
|
225
|
+
core.ts —— talk 核心:registry/mailbox/policy/format + TalkCore 协调器,只依赖存储层,通过回调 yield 投递/通知
|
|
226
|
+
index.ts —— pi adapter:把 core 接到 pi 的 sendMessage / 生命周期事件 / 工具注册
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
存储层抽象成接口是为了后续可换成 HTTP / remote 后端,talk 核心无需改动。
|
|
230
|
+
|
|
231
|
+
### 工具(LLM 可见)
|
|
232
|
+
|
|
233
|
+
| 工具 | 作用 |
|
|
234
|
+
| -------------------- | ----------------------------------------------------------------- |
|
|
235
|
+
| `talk-list-sessions` | 列出其他 session(presence:idle/working/not responding/offline) |
|
|
236
|
+
| `talk-read-messages` | 主动读收件箱(读即消费) |
|
|
237
|
+
| `talk-ask` | 向某个 session 提问并阻塞等待回复(默认 120s 超时) |
|
|
238
|
+
| `talk-wait` | 阻塞等待新消息到达 |
|
|
239
|
+
| `talk-send` | 发送纯文本消息(`to: "*"` 广播所有,`to: "cwd"` 广播同 cwd) |
|
|
240
|
+
| `talk-reply` | 回复一个 ask(`replyTo` 为 ask id,显式关联、不推断) |
|
|
241
|
+
|
|
242
|
+
对端消息在模型工作过程中以 steer 方式注入上下文;模型也可主动 `talk-read-messages` 拉取。
|
|
243
|
+
|
|
244
|
+
### 关键设计
|
|
245
|
+
|
|
246
|
+
- **投递成功才消费**:信件只在成功交给 `sendMessage` 后才从 inbox 删除,投递失败留在 inbox 下次重试——不会因 `sendMessage` 吞异常而静默丢信。
|
|
247
|
+
- **双向 ask 仲裁**:`talk-ask` 发起前先检查收件箱(有对方消息就先读/先回);阻塞等待期间若收到对方的 ask(而非 reply),按两个 ask 的 `ts` 字段仲裁——先 ask 者主导继续等,后 ask 者让位并先回复对方。`ts` 是信件内固定字段,双方读到同一对值,结论天然对称;同毫秒碰撞用 `session dir + session id` 字符串比较兜底。
|
|
248
|
+
- **typebox runtime 验证**:所有从存储读出的值经 TypeBox schema 校验,损坏/伪造数据被拒绝,不做 `as T` 强转。
|
|
249
|
+
- **安全**:纯文本 ≤32KB;10s 去重 / 30s 限速 8 条 / 50 积压上限(防环);每条投递带「来自其他 session、无权威」声明。
|
|
250
|
+
|
|
251
|
+
### 配置
|
|
252
|
+
|
|
253
|
+
sqlite 文件路径按优先级取第一个可用值:
|
|
254
|
+
|
|
255
|
+
1. 环境变量 `PI_TALK_DB`
|
|
256
|
+
2. global `~/.pi/agent/settings.json` 里的 `talk.db_path`
|
|
257
|
+
3. 默认 `~/.pi/agent/talk.db`
|
|
258
|
+
|
|
259
|
+
`db_path` 支持 `~` 展开(`~/…` → 用户主目录),相对路径相对 `~/.pi/agent` 解析;绝对路径原样使用。
|
|
260
|
+
|
|
261
|
+
```jsonc
|
|
262
|
+
// ~/.pi/agent/settings.json
|
|
263
|
+
{
|
|
264
|
+
"talk": { "db_path": "~/data/talk.db" },
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
| 变量 | 默认 | 含义 |
|
|
269
|
+
| ----------------- | --------------------------------- | ----------------------------- |
|
|
270
|
+
| `PI_TALK_DB` | settings 或 `~/.pi/agent/talk.db` | SQLite 邮箱数据库路径 |
|
|
271
|
+
| `PI_TALK_INBOUND` | `accept` | `refuse` 时丢弃所有 peer 消息 |
|
|
272
|
+
|
|
273
|
+
### 使用
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
pi -e ./src/talk/index.ts
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
216
281
|
## 安装
|
|
217
282
|
|
|
218
283
|
### 通过 npm/git 包
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trim21/personal-pi-extensions",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.195",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
|
|
6
6
|
"keywords": [
|
|
@@ -65,7 +65,8 @@
|
|
|
65
65
|
"src/gh-readonly.ts",
|
|
66
66
|
"src/spawn-agent.ts",
|
|
67
67
|
"src/opencode-todo.ts",
|
|
68
|
-
"src/question.ts"
|
|
68
|
+
"src/question.ts",
|
|
69
|
+
"src/talk/index.ts"
|
|
69
70
|
]
|
|
70
71
|
},
|
|
71
72
|
"lint-staged": {
|
package/src/bwrap/index.ts
CHANGED
|
@@ -67,6 +67,8 @@ import { type BashOperations, createBashTool, getAgentDir } from "@earendil-work
|
|
|
67
67
|
import { Type } from "typebox";
|
|
68
68
|
import { Value } from "typebox/value";
|
|
69
69
|
|
|
70
|
+
import { expandHome } from "../lib/path.js";
|
|
71
|
+
|
|
70
72
|
const SANDBOX_PROMPT = `
|
|
71
73
|
## Command Execution
|
|
72
74
|
You are running inside a sandbox.
|
|
@@ -232,17 +234,8 @@ function deepMerge(base: BwrapConfig, overrides: Partial<BwrapConfig>): BwrapCon
|
|
|
232
234
|
};
|
|
233
235
|
}
|
|
234
236
|
|
|
235
|
-
function expandPath(p: string): string {
|
|
236
|
-
if (p.startsWith("~/")) {
|
|
237
|
-
const home = process.env.HOME;
|
|
238
|
-
if (home === undefined) return p;
|
|
239
|
-
return join(home, p.slice(2));
|
|
240
|
-
}
|
|
241
|
-
return p;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
237
|
function resolvePath(p: string, cwd: string): string {
|
|
245
|
-
const expanded =
|
|
238
|
+
const expanded = expandHome(p);
|
|
246
239
|
if (expanded === ".") return cwd;
|
|
247
240
|
return expanded;
|
|
248
241
|
}
|
package/src/lib/path.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared path helpers used by multiple extensions.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Expand a leading `~` to the user's home directory.
|
|
10
|
+
* - `~` → home
|
|
11
|
+
* - `~/rest` → `home/rest`
|
|
12
|
+
* - anything else (including `~user`) is left untouched.
|
|
13
|
+
*/
|
|
14
|
+
export function expandHome(p: string): string {
|
|
15
|
+
if (p === "~") return homedir();
|
|
16
|
+
if (p.startsWith("~/")) return join(homedir(), p.slice(2));
|
|
17
|
+
return p;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolve a path that may start with `~` (expanded to home) and may be
|
|
22
|
+
* relative (resolved against `baseDir`). Absolute paths are returned
|
|
23
|
+
* normalized.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveHomePath(p: string, baseDir: string): string {
|
|
26
|
+
const expanded = expandHome(p);
|
|
27
|
+
return isAbsolute(expanded) ? resolve(expanded) : resolve(baseDir, expanded);
|
|
28
|
+
}
|