@code-yeongyu/senpi-codemode 2026.9.28 → 2026.9.29-3
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 +123 -0
- package/README.md +13 -1
- package/package.json +4 -4
- package/src/bridge/memory-protocol.ts +39 -0
- package/src/bridge/protocol.ts +18 -0
- package/src/bridge/reserved.ts +2 -0
- package/src/config/memory-settings.ts +149 -0
- package/src/config/settings.ts +8 -2
- package/src/extension/session-manager-proxy.ts +6 -0
- package/src/extension/session-manager.ts +27 -25
- package/src/host-sdk.ts +1 -1
- package/src/index.ts +4 -0
- package/src/kernels/jl/kernel.ts +4 -1
- package/src/kernels/js/context-manager.ts +43 -5
- package/src/kernels/js/inline-worker.ts +2 -1
- package/src/kernels/js/interrupt-bounds.ts +8 -2
- package/src/kernels/js/kernel-contract.ts +13 -0
- package/src/kernels/js/local-module-loader.ts +2 -0
- package/src/kernels/js/webview-host.ts +59 -0
- package/src/kernels/js/worker-core.js +32 -3
- package/src/kernels/js/worker-global-sizes.js +123 -0
- package/src/kernels/js/worker-heap.js +59 -0
- package/src/kernels/js/worker-host.ts +2 -1
- package/src/kernels/js/worker-memory.js +94 -0
- package/src/kernels/js/worker-runtime.js +7 -2
- package/src/kernels/js/worker-slot.ts +16 -5
- package/src/kernels/js/worker-startup.ts +1 -0
- package/src/kernels/js/worker-webview-client.js +79 -0
- package/src/kernels/js/worker-webview.js +204 -0
- package/src/kernels/py/kernel-contract.ts +3 -0
- package/src/kernels/py/kernel.ts +15 -2
- package/src/kernels/py/prelude.py +327 -11
- package/src/kernels/py/transport.ts +4 -1
- package/src/kernels/rb/kernel.ts +4 -1
- package/src/kernels/shared/kernel-memory-host.ts +63 -0
- package/src/kernels/shared/kernel-memory.ts +136 -0
- package/src/kernels/shared/subprocess-contract.ts +10 -1
- package/src/kernels/shared/subprocess-kernel.ts +23 -2
- package/src/kernels/shared/subprocess-queue.ts +2 -0
- package/src/output/streaming-output-buffer.ts +229 -12
- package/src/output/streaming-output.ts +2 -2
- package/src/prompt/eval-prompt.ts +1 -1
- package/src/skill/bun-1-4/references/runtime-apis.md +1 -0
- package/src/tool/cell-runtime.ts +52 -10
- package/src/tool/detached-cell-contract.ts +4 -0
- package/src/tool/detached-cell-manager.ts +16 -1
- package/src/tool/detached-cell-notification.ts +10 -1
- package/src/tool/image.ts +6 -2
- package/src/tool/run-eval-cell.ts +19 -17
- package/src/tool/settled-image-spill.ts +108 -0
- package/src/tool/terminal-snapshot-store.ts +82 -10
- package/src/tool/types.ts +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,129 @@
|
|
|
12
12
|
|
|
13
13
|
### Removed
|
|
14
14
|
|
|
15
|
+
## [2026.9.29-3] - 2026-09-29
|
|
16
|
+
|
|
17
|
+
### Breaking Changes
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
### Removed
|
|
26
|
+
|
|
27
|
+
## [2026.9.29-2] - 2026-09-29
|
|
28
|
+
|
|
29
|
+
### Breaking Changes
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
### Removed
|
|
38
|
+
|
|
39
|
+
## [2026.9.29] - 2026-09-29
|
|
40
|
+
|
|
41
|
+
### Breaking Changes
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
### Removed
|
|
50
|
+
|
|
51
|
+
## [2026.9.28-7] - 2026-09-28
|
|
52
|
+
|
|
53
|
+
### Breaking Changes
|
|
54
|
+
|
|
55
|
+
### Added
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
### Fixed
|
|
60
|
+
|
|
61
|
+
### Removed
|
|
62
|
+
|
|
63
|
+
## [2026.9.28-6] - 2026-09-28
|
|
64
|
+
|
|
65
|
+
### Breaking Changes
|
|
66
|
+
|
|
67
|
+
### Added
|
|
68
|
+
|
|
69
|
+
### Changed
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
|
|
73
|
+
### Removed
|
|
74
|
+
|
|
75
|
+
## [2026.9.28-5] - 2026-09-28
|
|
76
|
+
|
|
77
|
+
### Breaking Changes
|
|
78
|
+
|
|
79
|
+
### Added
|
|
80
|
+
|
|
81
|
+
### Changed
|
|
82
|
+
|
|
83
|
+
### Fixed
|
|
84
|
+
|
|
85
|
+
### Removed
|
|
86
|
+
|
|
87
|
+
## [2026.9.28-4] - 2026-09-28
|
|
88
|
+
|
|
89
|
+
### Breaking Changes
|
|
90
|
+
|
|
91
|
+
### Added
|
|
92
|
+
|
|
93
|
+
- JavaScript eval kernels report their memory: a result whose kernel holds at least `memory.noticeMb` (default 1 GiB) live after a collection carries one bracketed notice naming the largest globals and how to drop them, plus `details.memory` (`liveBytes`, `gcRan`, `globals`). A kernel whose live memory reaches `memory.ceilingMb` (default a quarter of physical memory, 2-8 GiB) says so in that result and restarts once no cell is running or queued on it; the next result reports `details.memory.recycled`. Settings `memory.gcWatermarkMb`, `memory.noticeMb`, `memory.ceilingMb` and their `SENPI_CODEMODE_MEMORY_*_MB` overrides; `0` disables each. ([#2261](https://github.com/code-yeongyu/senpi/issues/2261))
|
|
94
|
+
- Python eval kernels follow the same memory contract: after each cell the kernel reads its process footprint (macOS `phys_footprint`, Linux `RssAnon`, Windows `PrivateUsage`), runs `gc.collect()` plus glibc `malloc_trim(0)` when it grew past `memory.gcWatermarkMb`, and a result at `memory.noticeMb` names the largest globals (numpy `nbytes`, pandas `memory_usage(deep=True)`, sampled containers) with `del <name>` advice; at `memory.ceilingMb` the Python kernel restarts once its queue is empty and the next result reports `details.memory.recycled`. Ruby and Julia kernels get the ceiling restart from the interpreter footprint the host reads after each result (no globals list). ([#2261](https://github.com/code-yeongyu/senpi/issues/2261))
|
|
95
|
+
|
|
96
|
+
### Changed
|
|
97
|
+
|
|
98
|
+
### Fixed
|
|
99
|
+
|
|
100
|
+
- Retiring a JavaScript eval worker (timeout, interrupt, reset, crash, or memory-ceiling restart) no longer leaves each child process the cell was still running as a zombie of the host: the host now collects them after killing them. Measured: 30 retirements that each left two children went from 60 zombies to 0. ([#1962](https://github.com/code-yeongyu/senpi/issues/1962))
|
|
101
|
+
- A persistent eval kernel no longer pins its first cell's handler for the whole kernel generation. The kernel dispatcher is now a bound method of the session manager instead of a closure over the `getKernel` call that created the kernel (under Bun/JSC that closure retained the creating cell's `onMessage` — its output buffers and display images — until the kernel was reset), and every cell releases its kernel listener once it settles, so nothing keeps a settled cell's state alive. Interpreter startup stderr still reaches the cell that created the kernel; a message arriving between cells reaches no settled handler. ([#2260](https://github.com/code-yeongyu/senpi/issues/2260))
|
|
102
|
+
- Memory a JavaScript eval cell no longer references returns to the machine without a reset: a finished cell whose heap grew past `memory.gcWatermarkMb` (default 256 MiB) runs a full collection, and on Node and on Bun before 1.4.3 a kernel still holding that much runs an idle collection about a second after its last cell, so `delete globalThis.rows` no longer leaves gigabytes resident until the kernel is reset (Bun 1.4.3 collects idle threads itself). ([#2261](https://github.com/code-yeongyu/senpi/issues/2261))
|
|
103
|
+
|
|
104
|
+
- Settled eval cells kept for `peek`/`list` no longer pin up to ~800 MB of image data in the session's memory. Their images are written to `<session artifacts>/settled-images/` and read back on `peek`, which still returns the full result. The files are bounded by the new `memory.retainedImagesMb` setting (default 256, env `SENPI_CODEMODE_RETAINED_IMAGES_MB`), deleted with an evicted cell, and removed when the session ends. The in-memory snapshots are bounded by the new `memory.retainedResultsMb` setting (default 32, env `SENPI_CODEMODE_RETAINED_RESULTS_MB`) on top of the 32-cell count cap. ([#2259](https://github.com/code-yeongyu/senpi/issues/2259))
|
|
105
|
+
|
|
106
|
+
- The JavaScript eval kernel no longer compiles a fresh code-cache entry for its loader prelude on every cell: the prelude is evaluated under one stable source URL (`senpi:kernel-prelude`) instead of a per-cell `` `${cellId}:prelude` `` URL, so identical prelude text reuses the engine's eval code cache: per-cell kernel heap growth over a 1,000-cell series drops from ~2.3 KB to ~1.1 KB (the remainder is the cell body's own per-cell source URL, kept for stack attribution and bounded by the engine's code-cache cap). Stack frames from prelude code now attribute to `senpi:kernel-prelude` for every cell. ([#2263](https://github.com/code-yeongyu/senpi/issues/2263))
|
|
107
|
+
|
|
108
|
+
### Removed
|
|
109
|
+
|
|
110
|
+
## [2026.9.28-3] - 2026-09-28
|
|
111
|
+
|
|
112
|
+
### Breaking Changes
|
|
113
|
+
|
|
114
|
+
### Added
|
|
115
|
+
|
|
116
|
+
### Changed
|
|
117
|
+
|
|
118
|
+
### Fixed
|
|
119
|
+
|
|
120
|
+
- Live output updates for eval cells no longer rebuild the retained output tail on every chunk. The streaming preview keeps the last eight lines of the trailing byte window, appends in time proportional to the chunk, and output-driven updates are coalesced to one per 100 ms (the core bash tool's cadence), so a cell printing 30,000 lines sends about 10 updates instead of 30,008 and finishes about 4x faster with a lower memory peak. The final result and the text of each update are unchanged. ([#2262](https://github.com/code-yeongyu/senpi/issues/2262))
|
|
121
|
+
|
|
122
|
+
- `new Bun.WebView()` in a JavaScript eval cell no longer fails with `Bun.WebView with backend "chrome" is only available on the main thread` (every call on Windows and Linux, `backend: "chrome"` on macOS). Cells see a `Bun` whose `WebView` (also through `import { WebView } from "bun"`) hands Chrome-backed views to the process main thread with the same API: navigation, input, `evaluate`, screenshots, `cdp()` and its events, `console` capture, `url`/`title`/`loading`, `close()` and `await using`. The macOS default (WebKit) stays a native view in the kernel worker. `Bun.WebView.closeAll()` in a cell closes only that kernel's views. ([#2248](https://github.com/code-yeongyu/senpi/issues/2248))
|
|
123
|
+
|
|
124
|
+
### Removed
|
|
125
|
+
|
|
126
|
+
## [2026.9.28-2] - 2026-09-28
|
|
127
|
+
|
|
128
|
+
### Breaking Changes
|
|
129
|
+
|
|
130
|
+
### Added
|
|
131
|
+
|
|
132
|
+
### Changed
|
|
133
|
+
|
|
134
|
+
### Fixed
|
|
135
|
+
|
|
136
|
+
### Removed
|
|
137
|
+
|
|
15
138
|
## [2026.9.28] - 2026-09-28
|
|
16
139
|
|
|
17
140
|
### Breaking Changes
|
package/README.md
CHANGED
|
@@ -105,7 +105,14 @@ Configuration is loaded in this order:
|
|
|
105
105
|
"headBytes": 20480,
|
|
106
106
|
"maxColumns": 768
|
|
107
107
|
},
|
|
108
|
-
"statusEvents": true
|
|
108
|
+
"statusEvents": true,
|
|
109
|
+
"memory": {
|
|
110
|
+
"gcWatermarkMb": 256,
|
|
111
|
+
"noticeMb": 1024,
|
|
112
|
+
"ceilingMb": 8192,
|
|
113
|
+
"retainedResultsMb": 32,
|
|
114
|
+
"retainedImagesMb": 256
|
|
115
|
+
}
|
|
109
116
|
}
|
|
110
117
|
```
|
|
111
118
|
|
|
@@ -123,6 +130,11 @@ Configuration is loaded in this order:
|
|
|
123
130
|
| `outputSink.headBytes` | `20480` | Bytes retained from the beginning of a middle-truncated preview; `0` disables it. |
|
|
124
131
|
| `outputSink.maxColumns` | `768` | Maximum rendered output columns; `0` disables column clamping. |
|
|
125
132
|
| `statusEvents` | `true` | Enables kernel status-event forwarding and rendering. Each cell retains at most 100 status rows; after overflow, one omitted-count row precedes the latest 99 events. |
|
|
133
|
+
| `memory.gcWatermarkMb` | `256` | JavaScript kernel: a finished cell whose heap reached this size and grew runs a full collection before its result; whenever at least this much stays live, an idle full collection runs about a second after the cell, so dropped globals return their memory without a reset. Python kernel: a finished cell whose process footprint reached this size and grew runs `gc.collect()` (plus glibc `malloc_trim(0)` on Linux) before its result, and while at least this much stays live the next cells collect too (at most 1/20 of the time), so `del rows` returns its memory. `0` disables. Env override: `SENPI_CODEMODE_MEMORY_GC_WATERMARK_MB`. |
|
|
134
|
+
| `memory.noticeMb` | `1024` | JavaScript and Python kernels: when live memory after a collection (JS heap, Python process footprint) reaches this size (first time, or 25% more than at the last notice), the result gets one bracketed notice naming the largest globals and how to drop them (`rows = undefined`, `del rows`), plus `details.memory`. Ruby and Julia get no notice. `0` disables. Env override: `SENPI_CODEMODE_MEMORY_NOTICE_MB`. |
|
|
135
|
+
| `memory.ceilingMb` | a quarter of physical memory, 2048-8192 | Every kernel (JS heap and Python footprint after a collection; Ruby and Julia interpreter footprint read by the host after each result, without a globals list): when live memory reaches this size, the result says so and the kernel restarts once no cell is running or queued on it; the next result says it was restarted (`details.memory.recycled`). `0` disables. Env override: `SENPI_CODEMODE_MEMORY_CEILING_MB`. Non-zero memory thresholds must satisfy watermark <= notice <= ceiling, otherwise all three use their defaults. |
|
|
136
|
+
| `memory.retainedResultsMb` | `32` | In-memory byte budget (MiB) for the settled cells kept for `peek`/`list`, on top of the 32-cell count cap; the oldest go first and the newest is always kept. `0` keeps only the count cap. Env override: `SENPI_CODEMODE_RETAINED_RESULTS_MB` (a non-negative integer). |
|
|
137
|
+
| `memory.retainedImagesMb` | `256` | Disk budget (MiB) for settled-cell images. Images of settled cells (foreground and detached) are written as base64 files under `<session artifacts>/settled-images/` instead of staying in memory, and `peek` reads them back, so it returns the full result. Beyond the budget the oldest files are deleted first; an evicted cell's files are deleted with it; the directory is removed when the session ends. A `peek` whose image file is gone returns the text plus a one-line note. `0` keeps only the count cap. Env override: `SENPI_CODEMODE_RETAINED_IMAGES_MB` (a non-negative integer). |
|
|
126
138
|
|
|
127
139
|
`SENPI_CODEMODE_PY`, `SENPI_CODEMODE_JS`, `SENPI_CODEMODE_RB`, and
|
|
128
140
|
`SENPI_CODEMODE_JL` override the corresponding file setting. `1` or `true`
|
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.29-3",
|
|
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.
|
|
33
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.29-3",
|
|
34
34
|
"typebox": "1.3.34"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@code-yeongyu/senpi": "2026.9.
|
|
37
|
+
"@code-yeongyu/senpi": "2026.9.29-3"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@code-yeongyu/senpi": "2026.9.
|
|
40
|
+
"@code-yeongyu/senpi": "2026.9.29-3"
|
|
41
41
|
},
|
|
42
42
|
"keywords": [
|
|
43
43
|
"senpi",
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type Static, Type } from "typebox";
|
|
2
|
+
|
|
3
|
+
/** Host -> kernel (`init`): byte thresholds for post-cell collection, the large-globals notice, and the ceiling; 0 disables each. */
|
|
4
|
+
export const kernelMemoryThresholdsSchema = Type.Object({
|
|
5
|
+
gcWatermarkBytes: Type.Integer({ minimum: 0 }),
|
|
6
|
+
noticeBytes: Type.Integer({ minimum: 0 }),
|
|
7
|
+
ceilingBytes: Type.Integer({ minimum: 0 }),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const kernelMemoryGlobalSchema = Type.Object({
|
|
11
|
+
name: Type.String({ minLength: 1 }),
|
|
12
|
+
bytes: Type.Integer({ minimum: 0 }),
|
|
13
|
+
approximate: Type.Optional(Type.Boolean()),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Kernel -> host memory report carried on the `result` frame. The kernel sets `liveBytes`, `measure`,
|
|
18
|
+
* `gcRan`, and `globals`; the kernel host adds `overCeiling`, `recycled`, and the `notice` text.
|
|
19
|
+
*/
|
|
20
|
+
export const kernelMemoryReportSchema = Type.Object({
|
|
21
|
+
liveBytes: Type.Integer({ minimum: 0 }),
|
|
22
|
+
measure: Type.Union([Type.Literal("heap"), Type.Literal("footprint")]),
|
|
23
|
+
/** `liveBytes` is a peak-RSS fallback (no footprint counter on this platform), not the current size. */
|
|
24
|
+
approximate: Type.Optional(Type.Boolean()),
|
|
25
|
+
/** `liveBytes` was measured right after a full collection in this cell. */
|
|
26
|
+
gcRan: Type.Optional(Type.Boolean()),
|
|
27
|
+
/** Largest user globals, measured only when live memory after collection reached the notice threshold. */
|
|
28
|
+
globals: Type.Optional(Type.Array(kernelMemoryGlobalSchema, { maxItems: 8 })),
|
|
29
|
+
/** Host-set: live memory after collection reached the ceiling; the kernel restarts once its queue drains. */
|
|
30
|
+
overCeiling: Type.Optional(Type.Boolean()),
|
|
31
|
+
/** Host-set: this is the first result on a kernel that was restarted for exceeding the ceiling. */
|
|
32
|
+
recycled: Type.Optional(Type.Boolean()),
|
|
33
|
+
/** Host-set: the bracketed notice shown to the model as its own text part. */
|
|
34
|
+
notice: Type.Optional(Type.String()),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export type KernelMemoryThresholds = Static<typeof kernelMemoryThresholdsSchema>;
|
|
38
|
+
export type KernelMemoryReport = Static<typeof kernelMemoryReportSchema>;
|
|
39
|
+
export type KernelMemoryGlobal = Static<typeof kernelMemoryGlobalSchema>;
|
package/src/bridge/protocol.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypt
|
|
|
2
2
|
import { type Static, Type } from "typebox";
|
|
3
3
|
import { Value } from "typebox/value";
|
|
4
4
|
import { kernelToolHostToKernelSchemas, kernelToolKernelToHostSchemas } from "./kernel-tools-protocol.ts";
|
|
5
|
+
import { kernelMemoryReportSchema, kernelMemoryThresholdsSchema } from "./memory-protocol.ts";
|
|
5
6
|
|
|
6
7
|
export const BRIDGE_FRAME_MAX_BYTES = 10 * 1024 * 1024;
|
|
7
8
|
|
|
@@ -31,6 +32,7 @@ const hostToKernelMessageSchema = Type.Union([
|
|
|
31
32
|
kernelGeneration: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
32
33
|
hostToolNames: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
33
34
|
foreignLanguageNames: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
35
|
+
memory: Type.Optional(kernelMemoryThresholdsSchema),
|
|
34
36
|
}),
|
|
35
37
|
Type.Object({
|
|
36
38
|
type: Type.Literal("run"),
|
|
@@ -66,6 +68,19 @@ const hostToKernelMessageSchema = Type.Union([
|
|
|
66
68
|
Type.Object({
|
|
67
69
|
type: Type.Literal("close"),
|
|
68
70
|
}),
|
|
71
|
+
/** JS only: the kernel's private port to the main-thread Bun.WebView service, sent in the transfer list. */
|
|
72
|
+
Type.Object({
|
|
73
|
+
type: Type.Literal("webview-port"),
|
|
74
|
+
requestId: Type.String({ minLength: 1 }),
|
|
75
|
+
ok: Type.Literal(true),
|
|
76
|
+
port: Type.Unknown(),
|
|
77
|
+
}),
|
|
78
|
+
Type.Object({
|
|
79
|
+
type: Type.Literal("webview-port"),
|
|
80
|
+
requestId: Type.String({ minLength: 1 }),
|
|
81
|
+
ok: Type.Literal(false),
|
|
82
|
+
error: bridgeErrorSchema,
|
|
83
|
+
}),
|
|
69
84
|
...kernelToolHostToKernelSchemas,
|
|
70
85
|
]);
|
|
71
86
|
|
|
@@ -97,6 +112,7 @@ const kernelToHostMessageSchema = Type.Union([
|
|
|
97
112
|
ok: Type.Literal(true),
|
|
98
113
|
valueRepr: Type.Optional(Type.String()),
|
|
99
114
|
durationMs: Type.Integer({ minimum: 0 }),
|
|
115
|
+
memory: Type.Optional(kernelMemoryReportSchema),
|
|
100
116
|
}),
|
|
101
117
|
Type.Object({
|
|
102
118
|
type: Type.Literal("result"),
|
|
@@ -104,8 +120,10 @@ const kernelToHostMessageSchema = Type.Union([
|
|
|
104
120
|
ok: Type.Literal(false),
|
|
105
121
|
error: bridgeErrorSchema,
|
|
106
122
|
durationMs: Type.Integer({ minimum: 0 }),
|
|
123
|
+
memory: Type.Optional(kernelMemoryReportSchema),
|
|
107
124
|
}),
|
|
108
125
|
Type.Object({ type: Type.Literal("closed") }),
|
|
126
|
+
Type.Object({ type: Type.Literal("webview-connect"), requestId: Type.String({ minLength: 1 }) }),
|
|
109
127
|
...kernelToolKernelToHostSchemas,
|
|
110
128
|
]);
|
|
111
129
|
|
package/src/bridge/reserved.ts
CHANGED
|
@@ -13,3 +13,5 @@ export const TIMEOUT_RESUME_OP = "timeout-resume" as const;
|
|
|
13
13
|
export const INTERRUPT_ACK_OP = "interrupt-ack" as const;
|
|
14
14
|
/** Status op the JS worker emits when a cell child is tracked or exits, so the host can retire it if the worker is lost. */
|
|
15
15
|
export const CHILD_LIFECYCLE_OP = "child" as const;
|
|
16
|
+
/** Status op the JS worker emits after an idle full collection between cells, carrying the live heap it measured. */
|
|
17
|
+
export const MEMORY_COLLECTED_OP = "memory-collected" as const;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { totalmem } from "node:os";
|
|
2
|
+
import { type Static, Type } from "typebox";
|
|
3
|
+
import type { KernelMemoryThresholds } from "../bridge/memory-protocol.ts";
|
|
4
|
+
import type { CodemodeSettings, Environment } from "./settings.ts";
|
|
5
|
+
|
|
6
|
+
export const memorySettingsSchema = Type.Object(
|
|
7
|
+
{
|
|
8
|
+
gcWatermarkMb: Type.Optional(Type.Number({ minimum: 0 })),
|
|
9
|
+
noticeMb: Type.Optional(Type.Number({ minimum: 0 })),
|
|
10
|
+
ceilingMb: Type.Optional(Type.Number({ minimum: 0 })),
|
|
11
|
+
retainedResultsMb: Type.Optional(Type.Number({ minimum: 0 })),
|
|
12
|
+
retainedImagesMb: Type.Optional(Type.Number({ minimum: 0 })),
|
|
13
|
+
},
|
|
14
|
+
{ additionalProperties: false },
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export type CodemodeMemorySettingsInput = Static<typeof memorySettingsSchema>;
|
|
18
|
+
|
|
19
|
+
export interface CodemodeMemorySettings {
|
|
20
|
+
/** Live kernel memory from which a finished cell triggers a full collection. */
|
|
21
|
+
readonly gcWatermarkMb: number;
|
|
22
|
+
/** Live kernel memory after collection from which a result names the largest globals. */
|
|
23
|
+
readonly noticeMb: number;
|
|
24
|
+
/** Live kernel memory after collection from which the kernel restarts once its queue drains. */
|
|
25
|
+
readonly ceilingMb: number;
|
|
26
|
+
/** In-memory byte budget (MiB) for settled-cell snapshots kept for `peek`/`list`; 0 keeps only the count cap. */
|
|
27
|
+
readonly retainedResultsMb: number;
|
|
28
|
+
/** Disk budget (MiB) for settled-cell images spilled under the session artifacts dir; 0 keeps only the count cap. */
|
|
29
|
+
readonly retainedImagesMb: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const DEFAULT_MEMORY_GC_WATERMARK_MB = 256;
|
|
33
|
+
export const DEFAULT_MEMORY_NOTICE_MB = 1024;
|
|
34
|
+
export const DEFAULT_RETAINED_RESULTS_MB = 32;
|
|
35
|
+
export const DEFAULT_RETAINED_IMAGES_MB = 256;
|
|
36
|
+
const CEILING_FLOOR_MB = 2048;
|
|
37
|
+
const CEILING_CAP_MB = 8192;
|
|
38
|
+
|
|
39
|
+
export const MEMORY_GC_WATERMARK_ENVIRONMENT_FLAG = "SENPI_CODEMODE_MEMORY_GC_WATERMARK_MB";
|
|
40
|
+
export const MEMORY_NOTICE_ENVIRONMENT_FLAG = "SENPI_CODEMODE_MEMORY_NOTICE_MB";
|
|
41
|
+
export const MEMORY_CEILING_ENVIRONMENT_FLAG = "SENPI_CODEMODE_MEMORY_CEILING_MB";
|
|
42
|
+
export const RETAINED_RESULTS_ENVIRONMENT_FLAG = "SENPI_CODEMODE_RETAINED_RESULTS_MB";
|
|
43
|
+
export const RETAINED_IMAGES_ENVIRONMENT_FLAG = "SENPI_CODEMODE_RETAINED_IMAGES_MB";
|
|
44
|
+
|
|
45
|
+
const MIB = 1024 * 1024;
|
|
46
|
+
|
|
47
|
+
export function defaultMemoryCeilingMb(totalBytes: number = totalmem()): number {
|
|
48
|
+
const quarterMb = Math.floor(totalBytes / MIB / 4);
|
|
49
|
+
return Math.min(CEILING_CAP_MB, Math.max(CEILING_FLOOR_MB, quarterMb));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function defaultMemorySettings(totalBytes?: number): CodemodeMemorySettings {
|
|
53
|
+
return {
|
|
54
|
+
gcWatermarkMb: DEFAULT_MEMORY_GC_WATERMARK_MB,
|
|
55
|
+
noticeMb: DEFAULT_MEMORY_NOTICE_MB,
|
|
56
|
+
ceilingMb: defaultMemoryCeilingMb(totalBytes),
|
|
57
|
+
retainedResultsMb: DEFAULT_RETAINED_RESULTS_MB,
|
|
58
|
+
retainedImagesMb: DEFAULT_RETAINED_IMAGES_MB,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function mergeMemorySettings(input: CodemodeMemorySettingsInput | undefined): CodemodeMemorySettings {
|
|
63
|
+
const defaults = defaultMemorySettings();
|
|
64
|
+
return {
|
|
65
|
+
gcWatermarkMb: input?.gcWatermarkMb ?? defaults.gcWatermarkMb,
|
|
66
|
+
noticeMb: input?.noticeMb ?? defaults.noticeMb,
|
|
67
|
+
ceilingMb: input?.ceilingMb ?? defaults.ceilingMb,
|
|
68
|
+
retainedResultsMb: input?.retainedResultsMb ?? defaults.retainedResultsMb,
|
|
69
|
+
retainedImagesMb: input?.retainedImagesMb ?? defaults.retainedImagesMb,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function memorySettingsOrderWarning(memory: CodemodeMemorySettings): string | undefined {
|
|
74
|
+
const enabled = [memory.gcWatermarkMb, memory.noticeMb, memory.ceilingMb].filter((value) => value > 0);
|
|
75
|
+
const ordered = enabled.every((value, index) => index === 0 || (enabled[index - 1] ?? 0) <= value);
|
|
76
|
+
if (ordered) return undefined;
|
|
77
|
+
return `Codemode memory thresholds must satisfy gcWatermarkMb <= noticeMb <= ceilingMb (got ${memory.gcWatermarkMb}, ${memory.noticeMb}, ${memory.ceilingMb}); using the defaults.`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** File thresholds out of order fall back to the defaults with a warning; the settled-cell budgets are kept. */
|
|
81
|
+
export function validatedMemorySettings(input: CodemodeMemorySettingsInput | undefined): {
|
|
82
|
+
readonly settings: CodemodeMemorySettings;
|
|
83
|
+
readonly warnings: readonly string[];
|
|
84
|
+
} {
|
|
85
|
+
const merged = mergeMemorySettings(input);
|
|
86
|
+
const warning = memorySettingsOrderWarning(merged);
|
|
87
|
+
if (warning === undefined) return { settings: merged, warnings: [] };
|
|
88
|
+
const defaults = defaultMemorySettings();
|
|
89
|
+
return {
|
|
90
|
+
settings: {
|
|
91
|
+
...defaults,
|
|
92
|
+
retainedResultsMb: merged.retainedResultsMb,
|
|
93
|
+
retainedImagesMb: merged.retainedImagesMb,
|
|
94
|
+
},
|
|
95
|
+
warnings: [warning],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Environment overrides win over the settings file; `0` disables a threshold, and a malformed value is
|
|
101
|
+
* ignored. Thresholds out of order after the overrides fall back to the defaults.
|
|
102
|
+
*/
|
|
103
|
+
export function resolveKernelMemoryThresholds(
|
|
104
|
+
memory: CodemodeMemorySettings | undefined,
|
|
105
|
+
env: Environment = process.env,
|
|
106
|
+
): KernelMemoryThresholds {
|
|
107
|
+
const base = memory ?? defaultMemorySettings();
|
|
108
|
+
const resolved: CodemodeMemorySettings = {
|
|
109
|
+
...base,
|
|
110
|
+
gcWatermarkMb: megabytesOverride(env[MEMORY_GC_WATERMARK_ENVIRONMENT_FLAG]) ?? base.gcWatermarkMb,
|
|
111
|
+
noticeMb: megabytesOverride(env[MEMORY_NOTICE_ENVIRONMENT_FLAG]) ?? base.noticeMb,
|
|
112
|
+
ceilingMb: megabytesOverride(env[MEMORY_CEILING_ENVIRONMENT_FLAG]) ?? base.ceilingMb,
|
|
113
|
+
};
|
|
114
|
+
const effective = memorySettingsOrderWarning(resolved) === undefined ? resolved : defaultMemorySettings();
|
|
115
|
+
return {
|
|
116
|
+
gcWatermarkBytes: Math.round(effective.gcWatermarkMb * MIB),
|
|
117
|
+
noticeBytes: Math.round(effective.noticeMb * MIB),
|
|
118
|
+
ceilingBytes: Math.round(effective.ceilingMb * MIB),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Settled-cell in-memory snapshot byte budget; the environment override accepts 0 (count cap only). */
|
|
123
|
+
export function resolveRetainedResultsBytes(settings: CodemodeSettings, env: Environment = process.env): number {
|
|
124
|
+
const megabytes =
|
|
125
|
+
nonNegativeIntegerOverride(env[RETAINED_RESULTS_ENVIRONMENT_FLAG]) ??
|
|
126
|
+
settings.memory?.retainedResultsMb ??
|
|
127
|
+
DEFAULT_RETAINED_RESULTS_MB;
|
|
128
|
+
return megabytes * MIB;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Settled-cell image spill disk budget; the environment override accepts 0 (count cap only). */
|
|
132
|
+
export function resolveRetainedImagesBytes(settings: CodemodeSettings, env: Environment = process.env): number {
|
|
133
|
+
const megabytes =
|
|
134
|
+
nonNegativeIntegerOverride(env[RETAINED_IMAGES_ENVIRONMENT_FLAG]) ??
|
|
135
|
+
settings.memory?.retainedImagesMb ??
|
|
136
|
+
DEFAULT_RETAINED_IMAGES_MB;
|
|
137
|
+
return megabytes * MIB;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function megabytesOverride(value: string | undefined): number | undefined {
|
|
141
|
+
if (value === undefined || value.trim() === "") return undefined;
|
|
142
|
+
const parsed = Number(value);
|
|
143
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : undefined;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function nonNegativeIntegerOverride(value: string | undefined): number | undefined {
|
|
147
|
+
if (value === undefined || !/^\s*\d+\s*$/u.test(value)) return undefined;
|
|
148
|
+
return Number.parseInt(value, 10);
|
|
149
|
+
}
|
package/src/config/settings.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
import type { Static } from "typebox";
|
|
5
5
|
import { Type } from "typebox";
|
|
6
6
|
import { Check } from "typebox/value";
|
|
7
|
+
import { type CodemodeMemorySettings, memorySettingsSchema, validatedMemorySettings } from "./memory-settings.ts";
|
|
7
8
|
|
|
8
9
|
export const codemodeSettingsSchema = Type.Object(
|
|
9
10
|
{
|
|
@@ -43,6 +44,7 @@ export const codemodeSettingsSchema = Type.Object(
|
|
|
43
44
|
),
|
|
44
45
|
),
|
|
45
46
|
statusEvents: Type.Optional(Type.Boolean()),
|
|
47
|
+
memory: Type.Optional(memorySettingsSchema),
|
|
46
48
|
},
|
|
47
49
|
{ additionalProperties: false },
|
|
48
50
|
);
|
|
@@ -87,6 +89,7 @@ export interface CodemodeSettings {
|
|
|
87
89
|
readonly taskTools?: CodemodeTaskTools;
|
|
88
90
|
readonly outputSink?: CodemodeOutputSink;
|
|
89
91
|
readonly statusEvents?: boolean;
|
|
92
|
+
readonly memory?: CodemodeMemorySettings;
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
export type ResolvedCodemodeSettings = CodemodeSettings & {
|
|
@@ -94,6 +97,7 @@ export type ResolvedCodemodeSettings = CodemodeSettings & {
|
|
|
94
97
|
readonly taskTools: CodemodeTaskTools;
|
|
95
98
|
readonly outputSink: CodemodeOutputSink;
|
|
96
99
|
readonly statusEvents: boolean;
|
|
100
|
+
readonly memory: CodemodeMemorySettings;
|
|
97
101
|
};
|
|
98
102
|
|
|
99
103
|
export interface LoadCodemodeSettingsOptions {
|
|
@@ -162,6 +166,7 @@ export const defaultCodemodeSettings: ResolvedCodemodeSettings = {
|
|
|
162
166
|
maxColumns: 768,
|
|
163
167
|
},
|
|
164
168
|
statusEvents: true,
|
|
169
|
+
memory: validatedMemorySettings(undefined).settings,
|
|
165
170
|
};
|
|
166
171
|
|
|
167
172
|
const languageEnvironmentFlags = {
|
|
@@ -171,7 +176,7 @@ const languageEnvironmentFlags = {
|
|
|
171
176
|
jl: "SENPI_CODEMODE_JL",
|
|
172
177
|
} as const;
|
|
173
178
|
|
|
174
|
-
type Environment = Readonly<Record<string, string | undefined>>;
|
|
179
|
+
export type Environment = Readonly<Record<string, string | undefined>>;
|
|
175
180
|
|
|
176
181
|
export async function loadCodemodeSettings(options: LoadCodemodeSettingsOptions = {}): Promise<LoadedCodemodeSettings> {
|
|
177
182
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -252,7 +257,7 @@ async function loadSettingsFile(path: string): Promise<LoadedCodemodeSettings> {
|
|
|
252
257
|
};
|
|
253
258
|
}
|
|
254
259
|
|
|
255
|
-
return { settings: mergeSettings(parsed), source: path, warnings:
|
|
260
|
+
return { settings: mergeSettings(parsed), source: path, warnings: validatedMemorySettings(parsed.memory).warnings };
|
|
256
261
|
}
|
|
257
262
|
|
|
258
263
|
function mergeSettings(input: CodemodeSettingsInput): ResolvedCodemodeSettings {
|
|
@@ -278,6 +283,7 @@ function mergeSettings(input: CodemodeSettingsInput): ResolvedCodemodeSettings {
|
|
|
278
283
|
maxColumns: input.outputSink?.maxColumns ?? defaultCodemodeSettings.outputSink.maxColumns,
|
|
279
284
|
},
|
|
280
285
|
statusEvents: input.statusEvents ?? defaultCodemodeSettings.statusEvents,
|
|
286
|
+
memory: validatedMemorySettings(input.memory).settings,
|
|
281
287
|
};
|
|
282
288
|
}
|
|
283
289
|
|
|
@@ -100,6 +100,12 @@ export class SessionManagerProxy implements CodemodeSessionManager, EvalExecutio
|
|
|
100
100
|
return await current.getKernel(language, onMessage);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
releaseKernelListener(language: EvalLanguage, onMessage: (message: KernelToHostMessage) => void): void {
|
|
104
|
+
// Best-effort like the rest of the proxy surface: a replaced or disposed
|
|
105
|
+
// generation has already cleared its listener refs, so there is nothing to release.
|
|
106
|
+
this.#current?.releaseKernelListener?.(language, onMessage);
|
|
107
|
+
}
|
|
108
|
+
|
|
103
109
|
async complete(request: CompletionRequest, ctx: ExtensionContext): Promise<CompletionResult> {
|
|
104
110
|
this.assertEvalExecutionAllowed();
|
|
105
111
|
const current = this.#current;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
import type
|
|
2
|
+
import { type ExtensionContext, readProcessFootprint } from "@code-yeongyu/senpi";
|
|
3
3
|
import { type BridgeServerHandle, startBridgeServer } from "../bridge/http-server.ts";
|
|
4
4
|
import type { KernelToHostMessage } from "../bridge/protocol.ts";
|
|
5
5
|
import { isReservedToolName, runReservedTool } from "../bridges/reserved-dispatch.ts";
|
|
6
6
|
import type { EvalSchemaToolInfo } from "../bridges/schema-bridge.ts";
|
|
7
7
|
import type { CompletionRequest, CompletionResult } from "../completion/handler.ts";
|
|
8
|
+
import { resolveKernelMemoryThresholds } from "../config/memory-settings.ts";
|
|
8
9
|
import { type CodemodeSettings, defaultCodemodeSettings } from "../config/settings.ts";
|
|
10
|
+
import { collectOrphanedChildren } from "../host-sdk.ts";
|
|
9
11
|
import type { InterpreterAvailability } from "../interpreters/detect.ts";
|
|
10
12
|
import { JuliaKernel } from "../kernels/jl/kernel.ts";
|
|
11
13
|
import { JavaScriptKernel } from "../kernels/js/context-manager.ts";
|
|
@@ -131,7 +133,11 @@ class DefaultCodemodeSessionManager implements CodemodeSessionManager {
|
|
|
131
133
|
if (existing) return existing;
|
|
132
134
|
const pending = this.#kernelCreations.get(language);
|
|
133
135
|
if (pending) return await pending;
|
|
134
|
-
|
|
136
|
+
// A bound method, never a closure in this frame: the dispatcher outlives every
|
|
137
|
+
// cell, and a closure here would capture this call's lexical environment — under
|
|
138
|
+
// JSC that keeps the creating cell's onMessage (CellHandler, output buffers,
|
|
139
|
+
// display images) alive for the whole kernel generation (#2260).
|
|
140
|
+
const dispatch = this.#dispatchTo.bind(this, language);
|
|
135
141
|
const generation = this.#generation;
|
|
136
142
|
const creation = this.#createAndStoreKernel(language, dispatch, generation);
|
|
137
143
|
this.#kernelCreations.set(language, creation);
|
|
@@ -142,6 +148,15 @@ class DefaultCodemodeSessionManager implements CodemodeSessionManager {
|
|
|
142
148
|
}
|
|
143
149
|
}
|
|
144
150
|
|
|
151
|
+
releaseKernelListener(language: EvalLanguage, onMessage: (message: KernelToHostMessage) => void): void {
|
|
152
|
+
// Identity-checked: a cell settling late must not unbind a newer cell that already re-registered.
|
|
153
|
+
if (this.#onMessageRefs.get(language) === onMessage) this.#onMessageRefs.delete(language);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
#dispatchTo(language: EvalLanguage, message: KernelToHostMessage): void {
|
|
157
|
+
this.#onMessageRefs.get(language)?.(message);
|
|
158
|
+
}
|
|
159
|
+
|
|
145
160
|
async complete(request: CompletionRequest, ctx: ExtensionContext): Promise<CompletionResult> {
|
|
146
161
|
return await this.#options.complete(request, ctx);
|
|
147
162
|
}
|
|
@@ -217,6 +232,8 @@ class DefaultCodemodeSessionManager implements CodemodeSessionManager {
|
|
|
217
232
|
onMessage,
|
|
218
233
|
hostToolNames: () => this.#options.listTools?.().map((tool) => tool.name) ?? [],
|
|
219
234
|
foreignLanguageNames: () => this.#foreignKernelToolNames(),
|
|
235
|
+
memory: resolveKernelMemoryThresholds(this.#options.settings.memory),
|
|
236
|
+
collectOrphanedChildren,
|
|
220
237
|
...(this.#options.sessionEnv ? { sessionEnv: this.#options.sessionEnv } : {}),
|
|
221
238
|
...(localRoots ? { localRoots: { ...localRoots } } : {}),
|
|
222
239
|
...(this.#options.artifactsDir ? { artifactsDir: this.#options.artifactsDir } : {}),
|
|
@@ -231,34 +248,19 @@ class DefaultCodemodeSessionManager implements CodemodeSessionManager {
|
|
|
231
248
|
...(localRoots ? { localRoots: { ...localRoots } } : {}),
|
|
232
249
|
...(this.#options.artifactsDir ? { artifactsDir: this.#options.artifactsDir } : {}),
|
|
233
250
|
};
|
|
234
|
-
|
|
235
|
-
return await PythonKernel.start({
|
|
236
|
-
interpreterPath: detected.path,
|
|
237
|
-
sessionId: this.#options.sessionId,
|
|
238
|
-
cwd: this.#options.cwd,
|
|
239
|
-
...(this.#options.sessionEnv ? { sessionEnv: this.#options.sessionEnv } : {}),
|
|
240
|
-
connection,
|
|
241
|
-
onMessage,
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
if (language === "rb") {
|
|
245
|
-
return RubyKernel.start({
|
|
246
|
-
command: detected.path,
|
|
247
|
-
sessionId: this.#options.sessionId,
|
|
248
|
-
cwd: this.#options.cwd,
|
|
249
|
-
...(this.#options.sessionEnv ? { sessionEnv: this.#options.sessionEnv } : {}),
|
|
250
|
-
connection,
|
|
251
|
-
onMessage,
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
return JuliaKernel.start({
|
|
255
|
-
command: detected.path,
|
|
251
|
+
const shared = {
|
|
256
252
|
sessionId: this.#options.sessionId,
|
|
257
253
|
cwd: this.#options.cwd,
|
|
258
254
|
...(this.#options.sessionEnv ? { sessionEnv: this.#options.sessionEnv } : {}),
|
|
259
255
|
connection,
|
|
260
256
|
onMessage,
|
|
261
|
-
}
|
|
257
|
+
};
|
|
258
|
+
const memory = resolveKernelMemoryThresholds(this.#options.settings.memory);
|
|
259
|
+
if (language === "py") return await PythonKernel.start({ ...shared, interpreterPath: detected.path, memory });
|
|
260
|
+
// rb/jl runners report no memory: the host reads the interpreter footprint for the ceiling only.
|
|
261
|
+
const hostMeasured = { thresholds: memory, readFootprint: readProcessFootprint };
|
|
262
|
+
if (language === "rb") return RubyKernel.start({ ...shared, command: detected.path, memory: hostMeasured });
|
|
263
|
+
return JuliaKernel.start({ ...shared, command: detected.path, memory: hostMeasured });
|
|
262
264
|
}
|
|
263
265
|
|
|
264
266
|
#foreignKernelToolNames(): string[] {
|
package/src/host-sdk.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, truncateTail } from "@code-yeongyu/senpi";
|
|
1
|
+
export { collectOrphanedChildren, DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, truncateTail } from "@code-yeongyu/senpi";
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { ExtensionContext } from "@code-yeongyu/senpi";
|
|
|
3
3
|
import type { AgentExecuteTool } from "./bridges/agent-bridge.ts";
|
|
4
4
|
import type { EvalSchemaToolInfo } from "./bridges/schema-bridge.ts";
|
|
5
5
|
import { type CompletionRequest, type CompletionResult, createCompletionHandler } from "./completion/handler.ts";
|
|
6
|
+
import { resolveRetainedImagesBytes, resolveRetainedResultsBytes } from "./config/memory-settings.ts";
|
|
6
7
|
import {
|
|
7
8
|
defaultCodemodeSettings,
|
|
8
9
|
resolveForegroundWindowSeconds,
|
|
@@ -192,6 +193,7 @@ export default function senpiCodemode(pi: CodemodeExtensionAPI, options: SenpiCo
|
|
|
192
193
|
cellManager: new EvalDetachedCellManager({
|
|
193
194
|
notifier,
|
|
194
195
|
maxDetachedCells: resolveMaxDetachedCells(defaultCodemodeSettings),
|
|
196
|
+
retainedResultsBytes: resolveRetainedResultsBytes(defaultCodemodeSettings),
|
|
195
197
|
hardLimitSeconds: resolveHardLimitSeconds(defaultCodemodeSettings),
|
|
196
198
|
runBudgetSeconds: resolveRunBudgetSeconds(defaultCodemodeSettings),
|
|
197
199
|
onStatusChange: showDetachedCells,
|
|
@@ -231,6 +233,8 @@ export default function senpiCodemode(pi: CodemodeExtensionAPI, options: SenpiCo
|
|
|
231
233
|
artifactsDir: runtime.artifactsDir,
|
|
232
234
|
notifier,
|
|
233
235
|
maxDetachedCells: resolveMaxDetachedCells(runtime.settings),
|
|
236
|
+
retainedResultsBytes: resolveRetainedResultsBytes(runtime.settings),
|
|
237
|
+
retainedImagesBytes: resolveRetainedImagesBytes(runtime.settings),
|
|
234
238
|
hardLimitSeconds: resolveHardLimitSeconds(runtime.settings),
|
|
235
239
|
runBudgetSeconds: resolveRunBudgetSeconds(runtime.settings),
|
|
236
240
|
onStatusChange: showDetachedCells,
|
package/src/kernels/jl/kernel.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { join } from "node:path";
|
|
|
2
2
|
import type { BridgeConnectionConfig, KernelToHostMessage } from "../../bridge/protocol.ts";
|
|
3
3
|
import type { SessionEnvironment } from "../session-env.ts";
|
|
4
4
|
import { type CodemodeRuntimeAssetEnvironment, requireCodemodeRuntimeAsset } from "../shared/runtime-asset.ts";
|
|
5
|
-
import { SubprocessKernel, type SubprocessSpawn } from "../shared/subprocess-kernel.ts";
|
|
5
|
+
import { SubprocessKernel, type SubprocessKernelMemory, type SubprocessSpawn } from "../shared/subprocess-kernel.ts";
|
|
6
6
|
|
|
7
7
|
export interface JuliaKernelStartOptions {
|
|
8
8
|
readonly cwd: string;
|
|
@@ -13,6 +13,8 @@ export interface JuliaKernelStartOptions {
|
|
|
13
13
|
readonly command?: string;
|
|
14
14
|
readonly spawn?: SubprocessSpawn;
|
|
15
15
|
readonly onMessage?: (message: KernelToHostMessage) => void;
|
|
16
|
+
/** Ceiling-only memory management (no notice or globals list: the runner reports no memory). */
|
|
17
|
+
readonly memory?: SubprocessKernelMemory;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export interface JuliaRunnerPathOptions extends CodemodeRuntimeAssetEnvironment {
|
|
@@ -49,6 +51,7 @@ export class JuliaKernel extends SubprocessKernel {
|
|
|
49
51
|
connection: options.connection,
|
|
50
52
|
spawn: options.spawn,
|
|
51
53
|
onMessage: options.onMessage,
|
|
54
|
+
memory: options.memory && { language: "jl", ...options.memory },
|
|
52
55
|
});
|
|
53
56
|
}
|
|
54
57
|
}
|