@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 +2 -2
- package/package.json +1 -1
- package/src/codemode/prelude.ts +7 -2
- package/src/codemode/protocol.ts +4 -9
- package/src/codemode/sandbox.ts +1 -13
- package/src/codemode/source.ts +3 -13
- package/src/codemode/tool.ts +2 -10
- package/src/codemode/worker.js +8 -8
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: {"
|
|
47
|
-
|
|
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
package/src/codemode/prelude.ts
CHANGED
|
@@ -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(
|
|
107
|
+
const stored = new Map();
|
|
108
108
|
const writes = new Map();
|
|
109
109
|
let storedChars = 0;
|
|
110
|
-
for (const [key,
|
|
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");
|
package/src/codemode/protocol.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 每次执行起一个新的 worker:主线程把注册时编好的 wasm 模块连同脚本、可调用工具、
|
|
5
5
|
* 初始 store 一起发过去,worker 里建 QuickJS VM 跑脚本,脚本的嵌套调用与输出回传,
|
|
6
|
-
*
|
|
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" | "
|
|
102
|
+
export type ScriptErrorKind = "script" | "aborted" | "sandbox";
|
|
108
103
|
|
|
109
104
|
export interface ScriptError {
|
|
110
105
|
kind: ScriptErrorKind;
|
package/src/codemode/sandbox.ts
CHANGED
|
@@ -42,7 +42,7 @@ export interface SandboxRunOptions {
|
|
|
42
42
|
code: string;
|
|
43
43
|
tools: readonly ScriptTool[];
|
|
44
44
|
store: Record<string, unknown>;
|
|
45
|
-
|
|
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");
|
package/src/codemode/source.ts
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* codemode 脚本源码的解析:首行可选的 `// @options: {"max_output_tokens"
|
|
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"
|
|
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
|
-
|
|
58
|
-
options.maxOutputTokens = value;
|
|
59
|
-
} else {
|
|
60
|
-
options.timeoutMs = value;
|
|
61
|
-
}
|
|
51
|
+
options.maxOutputTokens = value;
|
|
62
52
|
}
|
|
63
53
|
return options;
|
|
64
54
|
}
|
package/src/codemode/tool.ts
CHANGED
|
@@ -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: {"
|
|
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);
|
package/src/codemode/worker.js
CHANGED
|
@@ -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(
|
|
92
|
+
const stored = new Map();
|
|
93
93
|
const writes = new Map();
|
|
94
94
|
let storedChars = 0;
|
|
95
|
-
for (const [key,
|
|
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())
|