@code-yeongyu/senpi-codemode 2026.9.20 → 2026.9.21-2
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 +28 -0
- package/package.json +5 -5
- package/src/kernels/py/prelude.py +24 -3
- package/src/kernels/py/process.ts +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,34 @@
|
|
|
12
12
|
|
|
13
13
|
### Removed
|
|
14
14
|
|
|
15
|
+
## [2026.9.21-2] - 2026-09-21
|
|
16
|
+
|
|
17
|
+
### Breaking Changes
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
### Removed
|
|
26
|
+
|
|
27
|
+
## [2026.9.21] - 2026-09-21
|
|
28
|
+
|
|
29
|
+
### Breaking Changes
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- Updated the bundled dependencies: typebox 1.3.27 -> 1.3.34. ([#1895](https://github.com/code-yeongyu/senpi/issues/1895))
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- Python eval kernel: a kernel whose host process died mid-cell now exits instead of being orphaned forever. ([#1659](https://github.com/code-yeongyu/senpi/issues/1659))
|
|
40
|
+
|
|
41
|
+
### Removed
|
|
42
|
+
|
|
15
43
|
## [2026.9.20] - 2026-09-20
|
|
16
44
|
|
|
17
45
|
### Breaking Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi-codemode",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.21-2",
|
|
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.9.
|
|
34
|
-
"typebox": "1.3.
|
|
33
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.21-2",
|
|
34
|
+
"typebox": "1.3.34"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@code-yeongyu/senpi": "2026.9.
|
|
37
|
+
"@code-yeongyu/senpi": "2026.9.21-2"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@code-yeongyu/senpi": "2026.9.
|
|
40
|
+
"@code-yeongyu/senpi": "2026.9.21-2"
|
|
41
41
|
},
|
|
42
42
|
"keywords": [
|
|
43
43
|
"senpi",
|
|
@@ -27,7 +27,6 @@ from threading import Lock, Thread
|
|
|
27
27
|
from typing import Any, Callable, Union
|
|
28
28
|
from urllib.parse import unquote
|
|
29
29
|
|
|
30
|
-
SESSION_ID = ""
|
|
31
30
|
CONNECTION: dict[str, Any] = {}
|
|
32
31
|
USER_NS: dict[str, Any] = {"__name__": "__main__", "__doc__": None, "__builtins__": __builtins__}
|
|
33
32
|
LOOP = asyncio.new_event_loop()
|
|
@@ -1056,10 +1055,9 @@ def elapsed(start: float) -> int:
|
|
|
1056
1055
|
|
|
1057
1056
|
|
|
1058
1057
|
def handle(message: dict[str, Any]) -> bool:
|
|
1059
|
-
global
|
|
1058
|
+
global CONNECTION
|
|
1060
1059
|
message_type = message.get("type")
|
|
1061
1060
|
if message_type == "init":
|
|
1062
|
-
SESSION_ID = str(message.get("sessionId", ""))
|
|
1063
1061
|
connection = message.get("connection")
|
|
1064
1062
|
if not isinstance(connection, dict):
|
|
1065
1063
|
emit({"type": "init-failed", "error": {"message": "missing bridge connection"}})
|
|
@@ -1097,10 +1095,33 @@ def _watch_parent(initial_ppid: int) -> None:
|
|
|
1097
1095
|
return
|
|
1098
1096
|
|
|
1099
1097
|
|
|
1098
|
+
def _watch_named_parent(parent_pid: int) -> None:
|
|
1099
|
+
# The ppid watch above can only observe a change from the ppid captured at boot. A
|
|
1100
|
+
# host that died before the interpreter reached that capture is already replaced in
|
|
1101
|
+
# getppid() by the posthumous value, so no transition ever fires. The host passes its
|
|
1102
|
+
# own pid at spawn (SENPI_PY_KERNEL_PARENT_PID) precisely so this loss is detectable:
|
|
1103
|
+
# poll the named pid instead of the ppid and take the whole group down once it is gone.
|
|
1104
|
+
while True:
|
|
1105
|
+
time.sleep(0.5)
|
|
1106
|
+
try:
|
|
1107
|
+
os.kill(parent_pid, 0)
|
|
1108
|
+
except ProcessLookupError:
|
|
1109
|
+
_terminate_process_group()
|
|
1110
|
+
return
|
|
1111
|
+
|
|
1112
|
+
|
|
1100
1113
|
def _start_parent_watch() -> None:
|
|
1101
1114
|
if os.name != "posix":
|
|
1102
1115
|
return
|
|
1103
1116
|
Thread(target=_watch_parent, args=(os.getppid(),), name="senpi-parent-watch", daemon=True).start()
|
|
1117
|
+
named_parent = os.environ.get("SENPI_PY_KERNEL_PARENT_PID")
|
|
1118
|
+
if named_parent and named_parent.isdigit():
|
|
1119
|
+
Thread(
|
|
1120
|
+
target=_watch_named_parent,
|
|
1121
|
+
args=(int(named_parent),),
|
|
1122
|
+
name="senpi-named-parent-watch",
|
|
1123
|
+
daemon=True,
|
|
1124
|
+
).start()
|
|
1104
1125
|
|
|
1105
1126
|
|
|
1106
1127
|
def main() -> None:
|
|
@@ -30,9 +30,13 @@ export class PythonKernelRetirementError extends Error {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export function defaultSpawn(options: KernelSpawnOptions): KernelChild {
|
|
33
|
+
// Detached children survive their host, so the prelude needs the host pid to
|
|
34
|
+
// watch for a parent that is already gone by the time the interpreter boots.
|
|
35
|
+
const env =
|
|
36
|
+
process.platform === "win32" ? options.env : { ...options.env, SENPI_PY_KERNEL_PARENT_PID: String(process.pid) };
|
|
33
37
|
return spawn(options.command, [...options.args], {
|
|
34
38
|
cwd: options.cwd,
|
|
35
|
-
env
|
|
39
|
+
env,
|
|
36
40
|
stdio: "pipe",
|
|
37
41
|
detached: process.platform !== "win32",
|
|
38
42
|
windowsHide: true,
|