@hicaru/pi-rlm 0.3.3 → 0.3.4

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/package.json CHANGED
@@ -1,57 +1,58 @@
1
1
  {
2
- "name": "@hicaru/pi-rlm",
3
- "version": "0.3.3",
4
- "author": "hicaru",
5
- "repository": {
6
- "type": "git",
7
- "url": "git+https://github.com/openzebra/rlm.pi.git"
8
- },
9
- "devDependencies": {
10
- "typescript": "^5.0.0"
11
- },
12
- "peerDependencies": {
13
- "@earendil-works/pi-ai": "*",
14
- "@earendil-works/pi-coding-agent": "*",
15
- "@earendil-works/pi-tui": "*",
16
- "typebox": "*"
17
- },
18
- "bugs": {
19
- "url": "https://github.com/openzebra/rlm.pi/issues"
20
- },
21
- "description": "Save 99% tokens, Recursive Language Model (RLM) for the Pi",
22
- "files": [
23
- "src/",
24
- "README.md",
25
- "LICENSE"
26
- ],
27
- "homepage": "https://github.com/openzebra/rlm.pi",
28
- "keywords": [
29
- "pi-package",
30
- "pi-extension",
31
- "rlm",
32
- "recursive",
33
- "ai-agent"
34
- ],
35
- "license": "MIT",
36
- "pi": {
37
- "extensions": [
38
- "./src/index.ts"
39
- ],
40
- "image": "https://raw.githubusercontent.com/openzebra/rlm.pi/main/assets/plugin-cover.png"
41
- },
42
- "publishConfig": {
43
- "access": "public"
44
- },
45
- "scripts": {
46
- "check": "tsc --noEmit",
47
- "test": "bun run test/smoke.ts",
48
- "prepublishOnly": "npm run check"
49
- },
50
- "type": "module",
51
- "engines": {
52
- "node": ">=20"
53
- },
54
- "dependencies": {
55
- "@firecrawl/anydoc": "^0.1.7"
56
- }
2
+ "name": "@hicaru/pi-rlm",
3
+ "version": "0.3.4",
4
+ "author": "hicaru",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/openzebra/rlm.pi.git"
8
+ },
9
+ "devDependencies": {
10
+ "typescript": "^5.0.0"
11
+ },
12
+ "peerDependencies": {
13
+ "@earendil-works/pi-ai": "^0.84.1",
14
+ "@earendil-works/pi-coding-agent": "^0.84.1",
15
+ "@earendil-works/pi-tui": "^0.84.1",
16
+ "typebox": "*"
17
+ },
18
+ "bugs": {
19
+ "url": "https://github.com/openzebra/rlm.pi/issues"
20
+ },
21
+ "description": "Save 99% tokens, Recursive Language Model (RLM) for the Pi",
22
+ "files": [
23
+ "src/",
24
+ "README.md",
25
+ "LICENSE"
26
+ ],
27
+ "homepage": "https://github.com/openzebra/rlm.pi",
28
+ "keywords": [
29
+ "pi-package",
30
+ "pi-extension",
31
+ "rlm",
32
+ "recursive",
33
+ "ai-agent"
34
+ ],
35
+ "license": "MIT",
36
+ "pi": {
37
+ "extensions": [
38
+ "./src/index.ts"
39
+ ],
40
+ "image": "https://raw.githubusercontent.com/openzebra/rlm.pi/main/assets/plugin-cover.png"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "scripts": {
46
+ "check": "tsc --noEmit",
47
+ "test": "bun run test/smoke.ts",
48
+ "prepublishOnly": "npm run check"
49
+ },
50
+ "type": "module",
51
+ "engines": {
52
+ "node": ">=20"
53
+ },
54
+ "dependencies": {
55
+ "@earendil-works/pi-agent-core": "^0.84.1",
56
+ "@firecrawl/anydoc": "^0.1.7"
57
+ }
57
58
  }
@@ -6,7 +6,7 @@
6
6
  * Used both for `llm_query` (one user prompt) and for the headless RLM root (full history).
7
7
  */
8
8
 
9
- import { type Api, completeSimple, type Message, type Model, type ThinkingLevel, type Usage } from "@earendil-works/pi-ai";
9
+ import { type Api, completeSimple, type Message, type Model, type ThinkingLevel, type Usage } from "@earendil-works/pi-ai/compat";
10
10
  import type { ModelRegistry } from "@earendil-works/pi-coding-agent";
11
11
 
12
12
  export type Role = "system" | "user" | "assistant";
@@ -34,11 +34,11 @@ function validateString(v: unknown): string | undefined {
34
34
 
35
35
  /**
36
36
  * Every value pi-ai accepts for `reasoning`. Keyed by the union so a new level added upstream
37
- * is a compile error here rather than a silently-rejected setting. Note `off` and `max` are
38
- * NOT ThinkingLevels — a hand-edited rlm.json carrying one is dropped, not forwarded.
37
+ * is a compile error here rather than a silently-rejected setting. Note `off` is NOT a
38
+ * ThinkingLevel — a hand-edited rlm.json carrying one is dropped, not forwarded.
39
39
  */
40
40
  const THINKING_LEVELS: Readonly<Record<ThinkingLevel, true>> = Object.freeze({
41
- minimal: true, low: true, medium: true, high: true, xhigh: true,
41
+ minimal: true, low: true, medium: true, high: true, xhigh: true, max: true,
42
42
  });
43
43
 
44
44
  function validateThinkingLevel(v: unknown): ThinkingLevel | undefined {
@@ -106,10 +106,23 @@ export class SandboxManager {
106
106
  awaitTimeoutS: this.config.awaitTimeoutS,
107
107
  handlers,
108
108
  }).then(async (s) => {
109
+ // ②A: a dispose racing an in-flight spawn must not leak the worker — kill it the
110
+ // instant it resolves. dispose() awaits initPromise so this always runs before it returns.
111
+ if (this.disposed) {
112
+ await s.dispose().catch(() => {});
113
+ throw new Error("SandboxManager disposed during spawn");
114
+ }
109
115
  // Load context on first creation if available (empty list is a valid starting value).
110
- if (this.contextPayload !== undefined) {
111
- await s.loadContext(this.contextPayload);
112
- this.contextLoaded = true;
116
+ try {
117
+ if (this.contextPayload !== undefined) {
118
+ await s.loadContext(this.contextPayload);
119
+ this.contextLoaded = true;
120
+ }
121
+ } catch (err) {
122
+ // ②B: never leak a spawned worker whose context load failed.
123
+ await s.dispose().catch(() => {});
124
+ this.initPromise = null;
125
+ throw err;
113
126
  }
114
127
  this.sandbox = s;
115
128
  this.initPromise = null;
@@ -196,6 +209,11 @@ export class SandboxManager {
196
209
  async dispose(): Promise<void> {
197
210
  if (this.disposed) return;
198
211
  this.disposed = true;
212
+ // ②A: a spawn in flight finishes after this.disposed was set — await it so the guard
213
+ // inside getOrCreate disposes its worker before dispose() returns (no leaked process).
214
+ if (this.initPromise) {
215
+ try { await this.initPromise; } catch { /* spawn failed or was disposed */ }
216
+ }
199
217
  await this.sandbox?.dispose();
200
218
  if (this.sandbox !== null) {
201
219
  this.sandbox = null;
@@ -12,7 +12,7 @@ export interface ModelSelection {
12
12
  readonly thinkingLevel?: ThinkingLevel;
13
13
  }
14
14
 
15
- const LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"] as const;
15
+ const LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const;
16
16
  type SelectableThinkingLevel = (typeof LEVELS)[number];
17
17
 
18
18
  /** Sentinel SelectList value for "always use cheapest available". */