@code-yeongyu/senpi-codemode 2026.8.21 → 2026.8.22

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/CHANGELOG.md CHANGED
@@ -12,6 +12,46 @@
12
12
 
13
13
  ### Removed
14
14
 
15
+ ## [2026.8.22] - 2026-08-22
16
+
17
+ ### Breaking Changes
18
+
19
+ ### Added
20
+
21
+ ### Changed
22
+
23
+ ### Fixed
24
+
25
+ - Ruby and Julia eval cells now wait for the subprocess `ready` signal before execution timeouts begin, so interpreter startup under load cannot time out a state-setting cell and silently restart the kernel before the next cell runs.
26
+
27
+ ### Removed
28
+
29
+ ## [2026.8.21-3] - 2026-08-21
30
+
31
+ ### Breaking Changes
32
+
33
+ ### Added
34
+
35
+ ### Changed
36
+
37
+ ### Fixed
38
+
39
+ ### Removed
40
+
41
+ ## [2026.8.21-2] - 2026-08-21
42
+
43
+ ### Breaking Changes
44
+
45
+ ### Added
46
+
47
+ ### Changed
48
+
49
+ ### Fixed
50
+
51
+ - `js` eval cells now accept `local://` paths in `read()` and `write()` like every other kernel. The session manager computed the session local root only after its `language === "js"` early return, so the JavaScript kernel was constructed without `localRoots` or `artifactsDir` and every `local://` helper call failed with `Protocol paths are not supported by write()`, even though the JavaScript prelude documents `local://` as the session local root. `py`/`rb`/`jl` behavior is unchanged.
52
+
53
+ ### Removed
54
+
15
55
  ## [2026.8.21] - 2026-08-21
16
56
 
17
57
  ### Breaking Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi-codemode",
3
- "version": "2026.8.21",
3
+ "version": "2026.8.22",
4
4
  "description": "Source-only senpi extension package for codemode evaluation tools",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -30,14 +30,14 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@babel/parser": "8.0.4",
33
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.8.21",
33
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.8.22",
34
34
  "typebox": "1.3.16"
35
35
  },
36
36
  "peerDependencies": {
37
- "@code-yeongyu/senpi": "2026.8.21"
37
+ "@code-yeongyu/senpi": "2026.8.22"
38
38
  },
39
39
  "devDependencies": {
40
- "@code-yeongyu/senpi": "2026.8.21"
40
+ "@code-yeongyu/senpi": "2026.8.22"
41
41
  },
42
42
  "keywords": [
43
43
  "senpi",
@@ -196,19 +196,24 @@ class DefaultCodemodeSessionManager implements CodemodeSessionManager {
196
196
  if (!bridge) throw new Error("codemode bridge server is not running");
197
197
  const configuredPoolWidth = this.#options.settings.parallelPoolWidth;
198
198
  const parallelPoolWidth = Number.isFinite(configuredPoolWidth) ? Math.max(1, Math.trunc(configuredPoolWidth)) : 1;
199
+ // localRoots must be computed BEFORE the js branch: the JS kernel resolves local://
200
+ // from its worker init connection exactly like the subprocess kernels resolve it from
201
+ // theirs. Computing it after the early return left js cells with no local root at all.
202
+ const localRoots =
203
+ this.#options.localRoots ??
204
+ (this.#options.artifactsDir ? { local: join(this.#options.artifactsDir, "local") } : undefined);
199
205
  if (language === "js") {
200
206
  return new JavaScriptKernel({
201
207
  sessionId: this.#options.sessionId,
202
208
  cwd: this.#options.cwd,
203
209
  parallelPoolWidth,
204
210
  onMessage,
211
+ ...(localRoots ? { localRoots: { ...localRoots } } : {}),
212
+ ...(this.#options.artifactsDir ? { artifactsDir: this.#options.artifactsDir } : {}),
205
213
  });
206
214
  }
207
215
  const detected = this.#options.availability[language].detected;
208
216
  if (!detected.ok) throw new Error(`No ${language} interpreter is available`);
209
- const localRoots =
210
- this.#options.localRoots ??
211
- (this.#options.artifactsDir ? { local: join(this.#options.artifactsDir, "local") } : undefined);
212
217
  const connection = {
213
218
  port: bridge.port,
214
219
  token: bridge.token,
@@ -26,6 +26,7 @@ export class SubprocessKernel {
26
26
  private readonly onMessage?: (message: KernelToHostMessage) => void;
27
27
  private readonly runs = new SubprocessRunQueue();
28
28
  private process: SubprocessProcess | null = null;
29
+ private processReady = false;
29
30
  private retirementPromise: Promise<void> | null = null;
30
31
  private retirementProcess: SubprocessProcess | null = null;
31
32
  private retirementFailure: Error | null = null;
@@ -109,7 +110,7 @@ export class SubprocessKernel {
109
110
 
110
111
  private pumpRuns(): void {
111
112
  const process = this.process;
112
- if (this.closed || this.runs.active || !process || process.isRetiring) return;
113
+ if (this.closed || this.runs.active || !process || process.isRetiring || !this.processReady) return;
113
114
  const run = this.runs.startNext(performance.now());
114
115
  if (!run) return;
115
116
  const timeoutMs = run.input.timeoutMs;
@@ -142,6 +143,7 @@ export class SubprocessKernel {
142
143
  },
143
144
  });
144
145
  this.process = process;
146
+ this.processReady = false;
145
147
  try {
146
148
  process.send(
147
149
  encodeBridgeFrame({ type: "init", sessionId: this.options.sessionId, connection: this.options.connection }),
@@ -165,6 +167,17 @@ export class SubprocessKernel {
165
167
 
166
168
  private handleMessage(process: SubprocessProcess, message: KernelToHostMessage): void {
167
169
  if (!this.accepts(process)) return;
170
+ if (message.type === "ready") {
171
+ this.processReady = true;
172
+ this.runs.handleMessage(message, this.onMessage);
173
+ this.pumpRuns();
174
+ return;
175
+ }
176
+ if (message.type === "init-failed") {
177
+ this.runs.handleMessage(message, this.onMessage);
178
+ this.failClosed(new KernelStartupError(message.error.message));
179
+ return;
180
+ }
168
181
  if (this.runs.handleMessage(message, this.onMessage)) this.pumpRuns();
169
182
  }
170
183
 
@@ -176,6 +189,7 @@ export class SubprocessKernel {
176
189
  return;
177
190
  }
178
191
  this.process = null;
192
+ this.processReady = false;
179
193
  this.failClosed(new KernelExitedError(signal ?? code ?? "unknown"));
180
194
  }
181
195