@trim21/personal-pi-extensions 0.1.658 → 0.1.660

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
@@ -43,8 +43,8 @@
43
43
  - **只有脚本输出进上下文**:`text(value)` / `console.log(...)` 与 `return` 值进入工具结果,
44
44
  中间的工具调用与它们的返回内容不会(也不在会话记录里留下工具调用条目)。
45
45
  - **脚本接口**:`tools` / `ALL_TOOLS` / `text` / `image` / `exit` / `console.*` /
46
- `store(key, value)` / `load(key)`;首行可选 `// @options: {"timeout_ms": 30000,
47
- "max_output_tokens": 10000}`。默认没有超时,靠调用方中止或 `timeout_ms` 结束。
46
+ `store(key, value)` / `load(key)`;首行可选 `// @options: {"max_output_tokens": 10000}`。
47
+ 脚本没有超时:死循环由调用方中止(Esc)结束,等嵌套调用返回(含用户审批弹窗)多久都不算超时。
48
48
  - **store** 写在会话的自定义 entry 上(跟随分支),输出超过 `max_output_tokens` 时头尾
49
49
  截断并把全文落到 `$TMPDIR/pi-codemode-*.txt`。
50
50
  - **构建**:worker 入口是 esbuild 产物 `src/codemode/worker.js`(随仓库提交,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trim21/personal-pi-extensions",
3
- "version": "0.1.658",
3
+ "version": "0.1.660",
4
4
  "type": "module",
5
5
  "description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
6
6
  "keywords": [
@@ -104,10 +104,15 @@ export const PRELUDE_SOURCE = String.raw`(function (bridge, toolsJson, storeJson
104
104
  Object.freeze(allTools);
105
105
 
106
106
  // key -> JSON 文本;容量按 key 与 JSON 的字符数计
107
- const stored = new Map(Object.entries(parse(storeJson)));
107
+ const stored = new Map();
108
108
  const writes = new Map();
109
109
  let storedChars = 0;
110
- for (const [key, json] of stored) storedChars += key.length + json.length;
110
+ for (const [key, value] of Object.entries(parse(storeJson))) {
111
+ const json = stringify(value);
112
+ if (json === undefined) continue;
113
+ stored.set(key, json);
114
+ storedChars += key.length + json.length;
115
+ }
111
116
 
112
117
  function checkKey(name, key) {
113
118
  if (typeof key !== "string") throw new TypeErrorCtor(name + "() key must be a string");
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * 每次执行起一个新的 worker:主线程把注册时编好的 wasm 模块连同脚本、可调用工具、
5
5
  * 初始 store 一起发过去,worker 里建 QuickJS VM 跑脚本,脚本的嵌套调用与输出回传,
6
- * 主线程执行工具后把结果发回去。超时/中止由主线程 `worker.terminate()` 负责,因此
7
- * 不需要取消消息。
6
+ * 主线程执行工具后把结果发回去。脚本没有超时,只有调用方的中止:中止时主线程直接
7
+ * `worker.terminate()`,因此不需要取消消息。
8
8
  *
9
9
  * worker 边界两侧都做 typebox 校验:worker 里跑的是模型写的代码,回传的消息同样
10
10
  * 当外部输入对待。
@@ -60,12 +60,7 @@ const outputSchema = Type.Object({
60
60
  });
61
61
 
62
62
  const scriptErrorSchema = Type.Object({
63
- kind: Type.Union([
64
- Type.Literal("script"),
65
- Type.Literal("timeout"),
66
- Type.Literal("aborted"),
67
- Type.Literal("sandbox"),
68
- ]),
63
+ kind: Type.Union([Type.Literal("script"), Type.Literal("aborted"), Type.Literal("sandbox")]),
69
64
  name: Type.Optional(Type.String()),
70
65
  message: Type.String(),
71
66
  stack: Type.Optional(Type.String()),
@@ -104,7 +99,7 @@ export interface WorkerBootstrap {
104
99
  export type CodemodeOutputItem =
105
100
  { type: "text"; text: string } | { type: "image"; data: string; mimeType: string };
106
101
 
107
- export type ScriptErrorKind = "script" | "timeout" | "aborted" | "sandbox";
102
+ export type ScriptErrorKind = "script" | "aborted" | "sandbox";
108
103
 
109
104
  export interface ScriptError {
110
105
  kind: ScriptErrorKind;
@@ -42,7 +42,7 @@ export interface SandboxRunOptions {
42
42
  code: string;
43
43
  tools: readonly ScriptTool[];
44
44
  store: Record<string, unknown>;
45
- timeoutMs: number;
45
+ /** 调用方中止本次执行;脚本没有自己的超时,死循环只能靠它结束。 */
46
46
  signal?: AbortSignal;
47
47
  /** 每次嵌套调用:由调用方执行工具并把结果回给脚本。 */
48
48
  onCall(request: { id: number; name: string; args: unknown }): Promise<SandboxCallOutcome>;
@@ -84,7 +84,6 @@ function runInWorker(wasm: object, options: SandboxRunOptions): Promise<SandboxO
84
84
  const calls: ScriptCall[] = [];
85
85
  const started = new Map<number, { name: string; at: number }>();
86
86
  let settled = false;
87
- let deadline: NodeJS.Timeout | undefined;
88
87
 
89
88
  const finish = (
90
89
  body:
@@ -95,9 +94,6 @@ function runInWorker(wasm: object, options: SandboxRunOptions): Promise<SandboxO
95
94
  return;
96
95
  }
97
96
  settled = true;
98
- if (deadline) {
99
- clearTimeout(deadline);
100
- }
101
97
  void worker.terminate();
102
98
  resolve({ ...body, output, calls });
103
99
  };
@@ -106,14 +102,6 @@ function runInWorker(wasm: object, options: SandboxRunOptions): Promise<SandboxO
106
102
  finish({ ok: false, error: { kind, message }, writes: NO_WRITES });
107
103
  };
108
104
 
109
- // 默认没有超时(Infinity):setTimeout 会把 Infinity 当成 1ms,必须显式判断
110
- if (Number.isFinite(options.timeoutMs)) {
111
- deadline = setTimeout(() => {
112
- fail(`the script exceeded its ${options.timeoutMs}ms deadline`, "timeout");
113
- }, options.timeoutMs);
114
- deadline.unref();
115
- }
116
-
117
105
  if (options.signal) {
118
106
  if (options.signal.aborted) {
119
107
  fail("codemode execution was aborted", "aborted");
@@ -1,19 +1,14 @@
1
1
  /**
2
- * codemode 脚本源码的解析:首行可选的 `// @options: {"max_output_tokens":…, "timeout_ms":…}`。
2
+ * codemode 脚本源码的解析:首行可选的 `// @options: {"max_output_tokens":…}`。
3
3
  *
4
4
  * 选项行与脚本首行共用一行,解析后原样保留为空行,脚本里报错的行号因此与用户写的
5
5
  * 一致。解析失败(空输入、JSON 非法、未知字段、只有选项行没有代码)直接报错。
6
6
  */
7
7
 
8
8
  const OPTIONS_PREFIX = "// @options:";
9
- const SUPPORTED_FIELDS = ["max_output_tokens", "timeout_ms"] as const;
9
+ const SUPPORTED_FIELDS = ["max_output_tokens"] as const;
10
10
 
11
11
  export const DEFAULT_OUTPUT_TOKENS = 10_000;
12
- /**
13
- * 脚本默认没有超时:编排一批工具调用本来就可能跑几分钟(例如批量读大文件)。
14
- * 调用方中止与 `// @options: {"timeout_ms": …}` 都能提前结束。
15
- */
16
- export const DEFAULT_TIMEOUT_MS = Infinity;
17
12
 
18
13
  export class CodemodeSourceError extends Error {
19
14
  constructor(message: string) {
@@ -24,7 +19,6 @@ export class CodemodeSourceError extends Error {
24
19
 
25
20
  export interface CodemodeSourceOptions {
26
21
  maxOutputTokens: number;
27
- timeoutMs: number;
28
22
  }
29
23
 
30
24
  export interface ParsedCodemodeSource {
@@ -54,11 +48,7 @@ function parseOptions(json: string): Partial<CodemodeSourceOptions> {
54
48
  if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
55
49
  throw new CodemodeSourceError(`${OPTIONS_PREFIX} ${key} must be a positive number`);
56
50
  }
57
- if (key === "max_output_tokens") {
58
- options.maxOutputTokens = value;
59
- } else {
60
- options.timeoutMs = value;
61
- }
51
+ options.maxOutputTokens = value;
62
52
  }
63
53
  return options;
64
54
  }
@@ -22,12 +22,7 @@ import { type ToolBus, toolResultText } from "../lib/tool-bus.js";
22
22
  import { renderDeclarations, toScriptTools } from "./declarations.js";
23
23
  import type { CodemodeOutputItem, ScriptError, StoreWrites } from "./protocol.js";
24
24
  import { type CodemodeSandbox, createCodemodeSandbox, type ScriptCall } from "./sandbox.js";
25
- import {
26
- CODEMODE_SOURCE_GRAMMAR,
27
- DEFAULT_OUTPUT_TOKENS,
28
- DEFAULT_TIMEOUT_MS,
29
- parseCodemodeSource,
30
- } from "./source.js";
25
+ import { CODEMODE_SOURCE_GRAMMAR, DEFAULT_OUTPUT_TOKENS, parseCodemodeSource } from "./source.js";
31
26
 
32
27
  export const CODEMODE_TOOL_NAME = "codemode";
33
28
 
@@ -230,7 +225,7 @@ export function createCodemodeTools(pi: ExtensionAPI): CodemodeTools {
230
225
  parameters: Type.Object({
231
226
  code: Type.String({
232
227
  description:
233
- 'Raw JavaScript source. Top-level await and return work. May start with a `// @options: {"timeout_ms": 30000}` line.',
228
+ 'Raw JavaScript source. Top-level await and return work. May start with a `// @options: {"max_output_tokens": 10000}` line.',
234
229
  }),
235
230
  }),
236
231
  constrainedSampling: {
@@ -241,12 +236,10 @@ export function createCodemodeTools(pi: ExtensionAPI): CodemodeTools {
241
236
 
242
237
  async execute(_toolCallId, params, signal, onUpdate, ctx) {
243
238
  let code: string;
244
- let timeoutMs = DEFAULT_TIMEOUT_MS;
245
239
  let maxOutputTokens = DEFAULT_OUTPUT_TOKENS;
246
240
  try {
247
241
  const parsed = parseCodemodeSource(params.code);
248
242
  code = parsed.code;
249
- timeoutMs = parsed.options.timeoutMs ?? timeoutMs;
250
243
  maxOutputTokens = parsed.options.maxOutputTokens ?? maxOutputTokens;
251
244
  } catch (error) {
252
245
  const message = error instanceof Error ? error.message : String(error);
@@ -264,7 +257,6 @@ export function createCodemodeTools(pi: ExtensionAPI): CodemodeTools {
264
257
  code,
265
258
  tools: scriptTools,
266
259
  store: readStore(ctx),
267
- timeoutMs,
268
260
  signal,
269
261
  onOutput: (items) => {
270
262
  const text = textOf(items);
@@ -89,10 +89,15 @@ var PRELUDE_SOURCE = String.raw`(function (bridge, toolsJson, storeJson) {
89
89
  Object.freeze(allTools);
90
90
 
91
91
  // key -> JSON 文本;容量按 key 与 JSON 的字符数计
92
- const stored = new Map(Object.entries(parse(storeJson)));
92
+ const stored = new Map();
93
93
  const writes = new Map();
94
94
  let storedChars = 0;
95
- for (const [key, json] of stored) storedChars += key.length + json.length;
95
+ for (const [key, value] of Object.entries(parse(storeJson))) {
96
+ const json = stringify(value);
97
+ if (json === undefined) continue;
98
+ stored.set(key, json);
99
+ storedChars += key.length + json.length;
100
+ }
96
101
 
97
102
  function checkKey(name, key) {
98
103
  if (typeof key !== "string") throw new TypeErrorCtor(name + "() key must be a string");
@@ -324,12 +329,7 @@ var outputSchema = Type.Object({
324
329
  items: Type.Array(outputItemSchema)
325
330
  });
326
331
  var scriptErrorSchema = Type.Object({
327
- kind: Type.Union([
328
- Type.Literal("script"),
329
- Type.Literal("timeout"),
330
- Type.Literal("aborted"),
331
- Type.Literal("sandbox")
332
- ]),
332
+ kind: Type.Union([Type.Literal("script"), Type.Literal("aborted"), Type.Literal("sandbox")]),
333
333
  name: Type.Optional(Type.String()),
334
334
  message: Type.String(),
335
335
  stack: Type.Optional(Type.String())