@code-yeongyu/senpi-codemode 2026.10.21-2 → 2026.10.23-1
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 +26 -0
- package/package.json +4 -4
- package/src/bridges/schema-hint.ts +1 -1
- package/src/kernels/js/display-image.js +1 -1
- package/src/kernels/js/worker-indirect-eval.js +2 -0
- package/src/kernels/js/worker-shell-capture.js +1 -0
- package/src/kernels/js/worker-webview.js +1 -0
- package/src/kernels/py/prelude.py +1 -1
- package/src/prompt/eval-prompt-template.ts +3 -3
- package/src/tool/eval-request.ts +3 -3
- package/src/tool/image-resize.ts +0 -1
- package/src/tool/image.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,32 @@
|
|
|
12
12
|
|
|
13
13
|
### Removed
|
|
14
14
|
|
|
15
|
+
## [2026.10.23-1] - 2026-10-23
|
|
16
|
+
|
|
17
|
+
### Breaking Changes
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
### Removed
|
|
26
|
+
|
|
27
|
+
- Removed the built-in llama.cpp provider's image-format exception along with the `/llama` integration ([#3202](https://github.com/code-yeongyu/senpi/issues/3202)).
|
|
28
|
+
|
|
29
|
+
## [2026.10.21-3] - 2026-10-22
|
|
30
|
+
|
|
31
|
+
### Breaking Changes
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
### Removed
|
|
40
|
+
|
|
15
41
|
## [2026.10.21-2] - 2026-10-22
|
|
16
42
|
|
|
17
43
|
### Breaking Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi-codemode",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.23-1",
|
|
4
4
|
"description": "Source-only senpi extension package for codemode evaluation tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -33,15 +33,15 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@babel/parser": "8.0.4",
|
|
36
|
-
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.
|
|
36
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.23-1",
|
|
37
37
|
"quickjs-wasi": "3.6.2",
|
|
38
38
|
"typebox": "1.3.34"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@code-yeongyu/senpi": "2026.10.
|
|
41
|
+
"@code-yeongyu/senpi": "2026.10.23-1"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@code-yeongyu/senpi": "2026.10.
|
|
44
|
+
"@code-yeongyu/senpi": "2026.10.23-1"
|
|
45
45
|
},
|
|
46
46
|
"keywords": [
|
|
47
47
|
"senpi",
|
|
@@ -65,7 +65,7 @@ function describeProperty(value: unknown): string {
|
|
|
65
65
|
if (!isSchemaLike(value)) return "unknown";
|
|
66
66
|
const parts = [typeLabel(value)];
|
|
67
67
|
const description = firstLine(value.description);
|
|
68
|
-
if (description !== undefined) parts.push(
|
|
68
|
+
if (description !== undefined) parts.push(`(${truncate(description, DESCRIPTION_MAX_CHARS)})`);
|
|
69
69
|
return parts.join(" ");
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -150,7 +150,7 @@ function frame(mimeType, dataBase64) {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
function dropped(reason) {
|
|
153
|
-
return [{ kind: "text", text: `[display: image dropped
|
|
153
|
+
return [{ kind: "text", text: `[display: image dropped - ${reason}]` }];
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
export function resolveDisplayOps(value) {
|
|
@@ -11,6 +11,7 @@ let shadowedNames = null;
|
|
|
11
11
|
|
|
12
12
|
export function indirectEval(source, filename) {
|
|
13
13
|
const withPragma = filename ? `${source}\n//# sourceURL=${filename}` : source;
|
|
14
|
+
// oxlint-disable-next-line eslint/no-eval -- The persistent kernel evaluates user cells in global scope to retain their bindings.
|
|
14
15
|
const geval = globalThis.eval;
|
|
15
16
|
return geval(withPragma);
|
|
16
17
|
}
|
|
@@ -744,6 +745,7 @@ function captureLastExpression(code) {
|
|
|
744
745
|
return `${head}return ${tail.replace(/;+$/u, "")};`;
|
|
745
746
|
}
|
|
746
747
|
|
|
748
|
+
// oxlint-disable-next-line eslint/no-misleading-character-class -- ECMAScript identifiers permit ZWNJ and ZWJ as individual continuation characters.
|
|
747
749
|
const LABEL_PREFIX_RE = /^[\p{ID_Start}$_][\p{ID_Continue}\u200c\u200d$]*\s*:/u;
|
|
748
750
|
|
|
749
751
|
function isStatementOnly(source) {
|
|
@@ -99,6 +99,7 @@ function captureShellPromise(promise, options) {
|
|
|
99
99
|
return prototype[method].apply(this, args);
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
|
+
// oxlint-disable-next-line unicorn/no-thenable -- This wraps an existing shell promise to record when the cell awaits it.
|
|
102
103
|
promise.then = function then(onFulfilled, onRejected) {
|
|
103
104
|
if (!waiting && !settled) {
|
|
104
105
|
waiting = true;
|
|
@@ -101,6 +101,7 @@ function createKernelWebView(NativeWebView, requestPort) {
|
|
|
101
101
|
const ref = new WeakRef(view);
|
|
102
102
|
natives.add(ref);
|
|
103
103
|
onCellRelease(() => ref.deref()?.close());
|
|
104
|
+
// oxlint-disable-next-line eslint/no-constructor-return -- Worker-safe calls must return the native view instead of the main-thread proxy.
|
|
104
105
|
return view;
|
|
105
106
|
}
|
|
106
107
|
super();
|
|
@@ -162,7 +162,7 @@ def _display_image_dict(value: dict[str, Any]) -> bool:
|
|
|
162
162
|
return False
|
|
163
163
|
encoded = _image_base64(payload)
|
|
164
164
|
if encoded is None:
|
|
165
|
-
print(f"[display: image dropped
|
|
165
|
+
print(f"[display: image dropped - `data` must be base64, a data: URL, or bytes; got {type(payload).__name__}]")
|
|
166
166
|
return True
|
|
167
167
|
emit({"type": "display", "mimeType": mime_type, "dataBase64": encoded[0]})
|
|
168
168
|
return True
|
|
@@ -21,15 +21,15 @@ GPT eval: batch a step's independent tool calls in one cell with \`tool.<name>(a
|
|
|
21
21
|
- Write real code around the calls - loops, branches, joins, a try/except per risky item - and keep every failed or missing item in the result verbatim; re-read truncated output before deciding.
|
|
22
22
|
{{#if monitor}}- Long-running build, test run, deploy, or watch work starts with \`tool.monitor({ command, filter })\`, with the decisive-line filter inside the same cell; keep working until its event wakes the turn.{{/if}}{{/if}}
|
|
23
23
|
{{#if hostLine}}
|
|
24
|
-
Host: {{hostLine}}
|
|
24
|
+
Host: {{hostLine}}. Cells execute here. Size \`parallel(thunks)\` pools to its cores; \`tool.<name>()\` shell commands must fit this platform, even when the code you are writing targets another machine.
|
|
25
25
|
{{/if}}
|
|
26
26
|
|
|
27
27
|
\`language\`: {{#if py}}\`"py"\` IPython kernel{{/if}}{{#ifAll py js}}, {{/ifAll}}{{#if js}}\`"js"\` persistent JavaScript VM{{/if}}{{#if rb}}{{#ifAny py js}}, {{/ifAny}}\`"rb"\` persistent Ruby kernel{{/if}}{{#if jl}}{{#ifAny py js rb}}, {{/ifAny}}\`"jl"\` persistent Julia kernel{{/if}}.
|
|
28
28
|
|
|
29
|
-
A cell that outlives the foreground window detaches and keeps running; further cells on the same language queue behind it in submission order on the same kernel (state is shared, execution is serial) while other languages run at once, up to {{maxDetachedCells}} background cells per session; each detached cell completes as one notification. Do not re-run a detached or queued cell; read, cancel or enumerate them with \`eval({ action: "peek" | "stop", cell_id })\` / \`eval({ action: "list" })\`; list shows live and recently settled cells across languages without consuming notifications. Its own execution time is capped at {{runBudgetSeconds}}s
|
|
29
|
+
A cell that outlives the foreground window detaches and keeps running; further cells on the same language queue behind it in submission order on the same kernel (state is shared, execution is serial) while other languages run at once, up to {{maxDetachedCells}} background cells per session; each detached cell completes as one notification. Do not re-run a detached or queued cell; read, cancel or enumerate them with \`eval({ action: "peek" | "stop", cell_id })\` / \`eval({ action: "list" })\`; list shows live and recently settled cells across languages without consuming notifications. Its own execution time is capped at {{runBudgetSeconds}}s (queue time and time inside host tool calls are not charged; the hard limit stays wall-clock from submission; raise \`timeout\` only for a declared long run), and a killed js cell that cannot settle restarts its kernel with every global lost.
|
|
30
30
|
|
|
31
31
|
{{#if py}}Python runs on a live event loop: use top-level \`await\`; \`asyncio.run(…)\` raises.{{/if}}
|
|
32
|
-
{{#if js}}{{#if jsBun}}JS runs in-process on Bun {{jsVersion}}: top-level \`await\`/\`return\` work; \`Bun.*\` builtins available, including \`new Bun.WebView()
|
|
32
|
+
{{#if js}}{{#if jsBun}}JS runs in-process on Bun {{jsVersion}}: top-level \`await\`/\`return\` work; \`Bun.*\` builtins available, including \`new Bun.WebView()\`, a headless browser (navigate/click/evaluate/screenshot) to reach for before \`curl\` or a browser CLI when a page needs JS, a login, or a screenshot. Shell out through \`Bun.$\` or \`Bun.spawn\`, never \`Bun.spawnSync\`: a synchronous child blocks the worker and cannot be interrupted.{{#if bunSkillPath}} Before a cell that installs a package, spawns a server or PTY, or starts a long run, read the bun-1-4 skill at {{bunSkillPath}}; its builtins replace the npm packages you would otherwise install.{{/if}}{{else}}JS runs under Node.js worker: top-level \`await\`/\`return\` work; \`fetch\`/\`Buffer\` available.{{/if}}{{/if}}
|
|
33
33
|
{{#if rb}}Ruby: synchronous; helper options are keyword args{{#if spawns}} (e.g. \`output("id", limit: 2)\`){{/if}}; the last expression auto-displays unless it is \`nil\`, an assignment, or a definition (like IRB).{{/if}}
|
|
34
34
|
{{#if jl}}Julia: synchronous; helper options are standard keyword args{{#if spawns}} (e.g. \`output("id", limit=2)\`){{/if}}; the last expression auto-displays unless it is an assignment or a definition (like the Julia REPL).{{/if}}
|
|
35
35
|
On error, fix and re-run only the failing step; a normal error keeps state, while a timeout or stop message says whether the kernel restarted.
|
package/src/tool/eval-request.ts
CHANGED
|
@@ -44,11 +44,11 @@ export function parseEvalRequest(
|
|
|
44
44
|
if (!isEvalLanguage(params.language))
|
|
45
45
|
throw new TypeError(`eval run language must be one of: ${enabledLanguages.join(", ")}`);
|
|
46
46
|
if (typeof params.code !== "string")
|
|
47
|
-
throw new TypeError("eval run requires code
|
|
47
|
+
throw new TypeError("eval run requires code: the cell body to execute, verbatim");
|
|
48
48
|
const summary = normalizeEvalSummary(params.summary);
|
|
49
49
|
if (summary === undefined)
|
|
50
50
|
throw new TypeError(
|
|
51
|
-
"eval run requires summary
|
|
51
|
+
"eval run requires summary (one line in the user's language): what you are working on and for what purpose",
|
|
52
52
|
);
|
|
53
53
|
if (params.on_timeout !== undefined && params.on_timeout !== "detach" && params.on_timeout !== "error")
|
|
54
54
|
throw new TypeError(`Unknown eval on_timeout value "${String(params.on_timeout)}"`);
|
|
@@ -91,5 +91,5 @@ function isEvalLanguage(value: unknown): value is EvalToolInput["language"] {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
function evalRunRequiresLanguageMessage(languages: readonly EvalLanguage[]): string {
|
|
94
|
-
return `eval run requires language
|
|
94
|
+
return `eval run requires language: one of ${languages.map((language) => `"${language}"`).join(", ")}`;
|
|
95
95
|
}
|
package/src/tool/image-resize.ts
CHANGED
|
@@ -23,7 +23,6 @@ export function webpExclusionForModel(model: WebpModel): true | undefined {
|
|
|
23
23
|
if (model === undefined) return undefined;
|
|
24
24
|
return model.provider === "ollama" ||
|
|
25
25
|
model.provider === "ollama-cloud" ||
|
|
26
|
-
model.provider === "llama.cpp" ||
|
|
27
26
|
model.provider === "lm-studio" ||
|
|
28
27
|
model.provider === "local-server" ||
|
|
29
28
|
model.api === "ollama-chat"
|
package/src/tool/image.ts
CHANGED
|
@@ -122,7 +122,7 @@ export class EvalOutputCollector {
|
|
|
122
122
|
if (message.mimeType.startsWith("image/")) {
|
|
123
123
|
const image = validateDisplayImage(message.dataBase64);
|
|
124
124
|
if ("reason" in image) {
|
|
125
|
-
this.#sink.push(`[display: image dropped
|
|
125
|
+
this.#sink.push(`[display: image dropped - ${image.reason}]\n`);
|
|
126
126
|
return;
|
|
127
127
|
}
|
|
128
128
|
if (
|