@trim21/personal-pi-extensions 0.1.660 → 0.1.662

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 CHANGED
@@ -45,8 +45,9 @@
45
45
  - **脚本接口**:`tools` / `ALL_TOOLS` / `text` / `image` / `exit` / `console.*` /
46
46
  `store(key, value)` / `load(key)`;首行可选 `// @options: {"max_output_tokens": 10000}`。
47
47
  脚本没有超时:死循环由调用方中止(Esc)结束,等嵌套调用返回(含用户审批弹窗)多久都不算超时。
48
- - **store** 写在会话的自定义 entry 上(跟随分支),输出超过 `max_output_tokens` 时头尾
49
- 截断并把全文落到 `$TMPDIR/pi-codemode-*.txt`。
48
+ - **store** 记在每次成功调用工具结果的 `details.store` 上(与 `src/lib/file-reads.ts` 的
49
+ 已读记账同一套做法),下一次调用从当前分支的 toolResult 重放;输出超过 `max_output_tokens`
50
+ 时头尾截断并把全文落到 `$TMPDIR/pi-codemode-*.txt`。
50
51
  - **构建**:worker 入口是 esbuild 产物 `src/codemode/worker.js`(随仓库提交,
51
52
  `pnpm run build:codemode-worker` 重新生成,pre-commit 会自动跑);`quickjs-wasi` 的
52
53
  wasm 在注册工具时编译一次,之后每次执行复用。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trim21/personal-pi-extensions",
3
- "version": "0.1.660",
3
+ "version": "0.1.662",
4
4
  "type": "module",
5
5
  "description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
6
6
  "keywords": [
@@ -3,10 +3,10 @@
3
3
  *
4
4
  * 沙箱就是 QuickJS VM 本身(VM 里没有 node、文件、网络、timer),脚本唯一的出口是
5
5
  * 注入的工具,而这些工具由主线程执行——所以这里只做三件事:把注册时编译好的 wasm
6
- * 与脚本交给 worker、转发脚本的嵌套调用与输出、在结束/超时/中止时 `terminate()`。
6
+ * 与脚本交给 worker、转发脚本的嵌套调用与输出、在结束或中止时 `terminate()`。
7
7
  *
8
- * worker 一次执行一个:死循环脚本由主线程 terminate 掉,连同它那份 VM 一起丢弃,
9
- * 不会污染后续执行,也不需要取消协议。
8
+ * worker 一次执行一个:脚本没有超时,死循环由调用方中止后 terminate 掉,连同它那份
9
+ * VM 一起丢弃,不会污染后续执行,也不需要取消协议。
10
10
  */
11
11
 
12
12
  import { Worker } from "node:worker_threads";
@@ -26,9 +26,6 @@ import { CODEMODE_SOURCE_GRAMMAR, DEFAULT_OUTPUT_TOKENS, parseCodemodeSource } f
26
26
 
27
27
  export const CODEMODE_TOOL_NAME = "codemode";
28
28
 
29
- /** store 落在 session 的自定义 entry 上:随分支保存与恢复。 */
30
- const STORE_ENTRY_TYPE = "codemode-store";
31
-
32
29
  /** 估计 token 用的字符数(与 pi 一致)。 */
33
30
  const CHARS_PER_TOKEN = 4;
34
31
 
@@ -89,17 +86,29 @@ function buildDescription(tools: readonly CallableTool[]): string {
89
86
  ].join("\n");
90
87
  }
91
88
 
89
+ /**
90
+ * store 的恢复:把当前分支上每次 codemode 调用记在工具结果 `details.store` 里的写入
91
+ * 按顺序重放(与 `src/lib/file-reads.ts` 从 toolResult 的 details 重放已读同一套做法)。
92
+ * 只认分支上的结果,所以 rewind / fork / resume 后 store 跟着分支走。
93
+ */
92
94
  function readStore(ctx: ExtensionContext): Record<string, unknown> {
93
95
  const store = new Map<string, unknown>();
94
96
  for (const entry of ctx.sessionManager.getBranch()) {
95
- if (entry.type !== "custom" || entry.customType !== STORE_ENTRY_TYPE) {
97
+ if (entry.type !== "message" || entry.message.role !== "toolResult") {
98
+ continue;
99
+ }
100
+ if (entry.message.toolName !== CODEMODE_TOOL_NAME) {
101
+ continue;
102
+ }
103
+ const details = entry.message.details;
104
+ if (typeof details !== "object" || details === null) {
96
105
  continue;
97
106
  }
98
- const data = (entry as { data?: unknown }).data;
99
- if (typeof data !== "object" || data === null) {
107
+ const record = (details as { store?: unknown }).store;
108
+ if (typeof record !== "object" || record === null || Array.isArray(record)) {
100
109
  continue;
101
110
  }
102
- const writes = data as Partial<StoreWrites>;
111
+ const writes = record as Partial<StoreWrites>;
103
112
  for (const key of writes.delete ?? []) {
104
113
  store.delete(key);
105
114
  }
@@ -294,9 +303,7 @@ export function createCodemodeTools(pi: ExtensionAPI): CodemodeTools {
294
303
  const images = imagesOf(outcome.output);
295
304
  if (outcome.ok) {
296
305
  const writes = outcome.writes;
297
- if (Object.keys(writes.set).length > 0 || writes.delete.length > 0) {
298
- pi.appendEntry(STORE_ENTRY_TYPE, writes);
299
- }
306
+ const hasWrites = Object.keys(writes.set).length > 0 || writes.delete.length > 0;
300
307
  const value =
301
308
  outcome.value === undefined ? "" : `\n\n${JSON.stringify(outcome.value, null, 2)}`;
302
309
  const truncated = await truncateOutput(
@@ -315,6 +322,8 @@ export function createCodemodeTools(pi: ExtensionAPI): CodemodeTools {
315
322
  ],
316
323
  details: {
317
324
  calls: outcome.calls,
325
+ // store 的写入随工具结果持久化,下一次调用从这里重放恢复
326
+ ...(hasWrites && { store: writes }),
318
327
  pendant: scriptPendant(code, `${outcome.calls.length} tool call(s)`),
319
328
  ...(truncated.fullOutputPath && { fullOutputPath: truncated.fullOutputPath }),
320
329
  },