Skip to content

feat(senpi-codemode): expose CommonJS in JS eval cells - #1286

Open
jc01rho wants to merge 5 commits into
code-yeongyu:mainfrom
jc01rho:feat/js-eval-commonjs
Open

feat(senpi-codemode): expose CommonJS in JS eval cells#1286
jc01rho wants to merge 5 commits into
code-yeongyu:mainfrom
jc01rho:feat/js-eval-commonjs

Conversation

@jc01rho

@jc01rho jc01rho commented Sep 2, 2026

Copy link
Copy Markdown

Summary

JS eval cells run through an ESM worker eval, so Node never wraps them with CommonJS parameters. typeof require was undefined.

This installs cwd-resolved CommonJS bindings on the persistent JS worker and passes exports, require, module, __filename, and __dirname into the cell wrapper.

Changes

  • worker-runtime.js: createRequire(cwd/eval-cell.cjs) plus module/exports/__filename/__dirname; resync exports to module.exports before each cell
  • worker-indirect-eval.js: wrap user cells with Node's CJS parameter list
  • Prompt/prelude/README document the bindings
  • Kernel tests cover builtin require, relative .cjs, metadata, and module.exports persistence across cells

Verification

  • vitest test/js-kernel.test.ts test/prompt.test.ts test/js-rewrite-imports.test.ts test/js-runtime-isolation.test.ts — 60 passed
  • Direct kernel QA: builtin require('node:path'), relative ./module.cjs, and module.exports persistence
  • scripts/check-pr-changelog.mjs --base origin/main — PASS
  • biome check on changed files — PASS

qa-js-cell.ts / full npm run check were not run end-to-end here because this worktree was installed with --ignore-scripts, so workspace dist/ artifacts (@earendil-works/pi-tui, pi-telemetry) are absent. Kernel-path tests do not need those packages.

Test plan

  • require('node:path') from a JS eval cell
  • require('./module.cjs') from session cwd
  • module.exports reassignment is visible to the next cell via exports
  • CI package tests / changelog gate

Summary by cubic

JS eval cells now expose CommonJS bindings—require, module, exports, __filename, and __dirname—instead of running without them through the ESM worker eval. Relative modules resolve from the session cwd, and rebinding a CommonJS name now raises a SyntaxError rather than replacing the persistent binding.

  • Resolves relative or empty cwd values before creating require, with __filename set to eval-cell.cjs.
  • Resynchronizes exports with module.exports before each cell so reassignment persists across cells.
  • Documents the bindings and adds coverage for builtins, relative .cjs files, metadata, cwd resolution, and reserved-name redeclarations.

Written for commit 8c6f1eb. Summary will update on new commits.

Review in cubic

JavaScript eval cells run through ESM-worker eval, so Node never wraps them
with require/module/exports. Install cwd-resolved CommonJS bindings and pass
them into the cell wrapper.

Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings September 2, 2026 08:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 9 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/senpi-codemode/src/kernels/js/worker-runtime.js
Comment thread packages/senpi-codemode/src/kernels/js/worker-indirect-eval.js
Resolve the session cwd before createRequire so a relative cwd still
produces an absolute filename. Leave top-level declarations that rebind
require/module/exports/__filename/__dirname unrewritten so they cannot
overwrite the persistent CommonJS bindings.

Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
@jc01rho

jc01rho commented Sep 2, 2026

Copy link
Copy Markdown
Author

Pushed a94e4e37c addressing both cubic P2s:

  1. Relative/empty cwdJsWorkerRuntime now resolve()s the session cwd before createRequire, so the CommonJS filename is always absolute.
  2. Shadowing require/module/exports/__filename/__dirname — those names are no longer rewritten onto globalThis, so a colliding top-level declaration SyntaxErrors instead of overwriting the persistent bindings.

Kernel tests cover both cases (js-kernel.test.ts, 31 passed). Fork-PR CI (CI, Changelog gate) is waiting on maintainer workflow approval (action_required). I cannot approve or merge from jc01rho (read-only on code-yeongyu/senpi).

@jc01rho

jc01rho commented Sep 2, 2026

Copy link
Copy Markdown
Author

@code-yeongyu fork PR CI is stuck on GitHub's first-time-contributor workflow approval (CI and Changelog gate are action_required). I cannot approve those runs or merge from jc01rho (read-only).

Cubic's two P2s are addressed in a94e4e37c (absolute cwd for createRequire; CJS reserved names no longer persist-rewritten). Kernel tests: 31 passed.

Please approve the workflow runs on this PR so Check and test can run, then merge when green.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 4 files (changes from recent commits).

You’re at about 91% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/senpi-codemode/src/kernels/js/worker-indirect-eval.js">

<violation number="1" location="packages/senpi-codemode/src/kernels/js/worker-indirect-eval.js:269">
P3: The guard makes a reserved-name declaration "SyntaxError against the CJS wrapper parameters instead of overwriting the persistent bindings", but that holds only for `const`/`let`. A top-level `var module = ...` (or `var require`/`var exports`/`var __filename`) is left in the cell body unchanged, and in sloppy mode `var` may redeclare a function parameter without error — it silently shadows the wrapper parameter for that cell only. A cell ending with `var exports = {}; exports.foo = 1` therefore writes to a local object and never reaches `module.exports`, silently diverging from the documented CommonJS behavior with no error surfaced. Consider handling `var` reserved bindings explicitly (error or rewrite them) or documenting the inconsistency, and add a test covering `var` declarations.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

const bindings = [];
collectPatternNames(pattern, bindings);
if (bindings.length === 0) return undefined;
if (bindings.some((name) => COMMONJS_RESERVED_NAMES.has(name))) return undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The guard makes a reserved-name declaration "SyntaxError against the CJS wrapper parameters instead of overwriting the persistent bindings", but that holds only for const/let. A top-level var module = ... (or var require/var exports/var __filename) is left in the cell body unchanged, and in sloppy mode var may redeclare a function parameter without error — it silently shadows the wrapper parameter for that cell only. A cell ending with var exports = {}; exports.foo = 1 therefore writes to a local object and never reaches module.exports, silently diverging from the documented CommonJS behavior with no error surfaced. Consider handling var reserved bindings explicitly (error or rewrite them) or documenting the inconsistency, and add a test covering var declarations.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/senpi-codemode/src/kernels/js/worker-indirect-eval.js, line 269:

<comment>The guard makes a reserved-name declaration "SyntaxError against the CJS wrapper parameters instead of overwriting the persistent bindings", but that holds only for `const`/`let`. A top-level `var module = ...` (or `var require`/`var exports`/`var __filename`) is left in the cell body unchanged, and in sloppy mode `var` may redeclare a function parameter without error — it silently shadows the wrapper parameter for that cell only. A cell ending with `var exports = {}; exports.foo = 1` therefore writes to a local object and never reaches `module.exports`, silently diverging from the documented CommonJS behavior with no error surfaced. Consider handling `var` reserved bindings explicitly (error or rewrite them) or documenting the inconsistency, and add a test covering `var` declarations.</comment>

<file context>
@@ -265,6 +266,7 @@ function rewriteDeclaration(code, declarationStart, start, end, keyword) {
 		const bindings = [];
 		collectPatternNames(pattern, bindings);
 		if (bindings.length === 0) return undefined;
+		if (bindings.some((name) => COMMONJS_RESERVED_NAMES.has(name))) return undefined;
 		if (preserveDeclaration) {
 			for (const name of bindings) assignments.push(`globalThis[${JSON.stringify(name)}] = ${name};`);
</file context>

@code-yeongyu

Copy link
Copy Markdown
Owner

Thanks @jc01rho. Nice catch on the eval cells missing CJS bindings, and leaving the five CommonJS names unrewritten in the declaration pass so they SyntaxError instead of silently clobbering the worker globals is the right call.

Pulled the branch locally and re-ran the kernel tests myself (31/31, incl. the new builtin require / relative .cjs / cross-cell module.exports cases). Approved the pending fork workflows — merging once CI is green.

Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
@jc01rho

jc01rho commented Sep 2, 2026

Copy link
Copy Markdown
Author

Merged latest main into this branch (1f5b5a0f5) so changelog-gate compares against current main instead of the stale first-run base. Local scripts/check-pr-changelog.mjs --base origin/main now PASSes; only senpi-codemode files remain in the PR diff.

The new head SHA needs another first-time-contributor workflow approval:

@code-yeongyu please approve those two runs (same fork-PR gate as before).

@jc01rho

jc01rho commented Sep 2, 2026

Copy link
Copy Markdown
Author

The merge-main commit landed after your approval of the previous SHA. New head is — changelog-gate should now PASS against current main. The two runs that still need approval are 33614942705 (CI) and 33614942689 (Changelog gate).

@jc01rho

jc01rho commented Sep 2, 2026

Copy link
Copy Markdown
Author

The merge-main commit landed after your approval of the previous SHA. New head is 1f5b5a0 — changelog-gate should now PASS against current main. The two runs that still need approval are:

@jc01rho

jc01rho commented Sep 2, 2026

Copy link
Copy Markdown
Author

CI is green except a Windows-only flake in RPC named pipes (Windows):

test/rpc-host-lifecycle.test.ts > starts a fresh host transparently on the next ensure after an idle exit

Stop-Process -Id 9212 -Force returned status 1 during teardown. Unrelated to the JS eval CommonJS change (senpi-codemode only). Changelog gate, static checks, workspace tests, and all three coding-agent shards passed.

I cannot rerun failed jobs from jc01rho (403). Please rerun the failed jobs on https://github.com/code-yeongyu/senpi/actions/runs/33614942705 and merge when green.

@jc01rho

jc01rho commented Sep 2, 2026

Copy link
Copy Markdown
Author

The remaining red check is still the Windows RPC flake, not the JS eval CommonJS change.

packages/coding-agent/test/rpc-host-lifecycle.test.ts terminateSupervisor() runs:

Stop-Process -Id <pid> -Force

with stdio: ignore and no try/catch. On the idle-exit path the supervisor is already gone, so PowerShell returns status 1 and the test fails. Unix already ignores ESRCH via signalIfAlive().

I cannot rerun failed jobs from this fork (403), and pushing a Windows-only test fix would retrigger first-time-contributor workflow approval.

Please either:

  1. rerun failed jobs on https://github.com/code-yeongyu/senpi/actions/runs/33614942705, or
  2. merge despite that flake (changelog gate, static checks, workspace tests, and all three coding-agent shards already passed).

Bring in code-yeongyu#1284 so CI no longer fails on Stop-Process racing an idle-exited supervisor.

Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
@jc01rho

jc01rho commented Sep 2, 2026

Copy link
Copy Markdown
Author

Merged latest main so this branch now includes #1284 (test(rpc): tolerate an already-exited host in Windows supervisor teardown). That is the exact Stop-Process race that failed RPC named pipes (Windows) on the previous SHA.

New head: 94a350422. Auto-merge is still enabled. The two new runs need first-time-contributor approval again:

@code-yeongyu please approve those two runs. Once they go green, auto-merge should land this.

@jc01rho

jc01rho commented Sep 2, 2026

Copy link
Copy Markdown
Author

The new head (94a350422) is green except the known Windows RPC flake tracked in #1290 variant 1 — not the JS eval CommonJS change, and not the #1284 Stop-Process race we just merged.

Failed job: https://github.com/code-yeongyu/senpi/actions/runs/33625644260/job/100235422233 (RPC named pipes (Windows) / Run RPC host ensure tests)

FAIL  test/rpc-host-ensure.test.ts > ensureHost > serializes concurrent starts for one socket across agent directories
Error: RPC socket host exited with code null (SIGTERM) before answering get_protocol_info
ENOTEMPTY: directory not empty, rmdir '...senpi-host-ensure-cross-agent-race-...'

Lifecycle tests were skipped after that. Changelog gate, static checks, workspace tests, all three coding-agent shards, and the other Windows jobs passed. Auto-merge is still enabled.

I cannot rerun from jc01rho (403). Please rerun failed jobs on https://github.com/code-yeongyu/senpi/actions/runs/33625644260 — a bare rerun of this exact test is the documented #1290 workaround until #1294 lands.

@jc01rho

jc01rho commented Sep 2, 2026

Copy link
Copy Markdown
Author

#1294 just went fully green, including RPC named pipes (Windows) (run 33628874723 attempt 5). That is the same job still red on this PR.

This head (94a350422) is otherwise green: changelog gate, static checks, workspace tests, all three coding-agent shards. Auto-merge is still enabled. The remaining blocker is the #1290 variant-1 flake (rpc-host-ensure concurrent-start SIGTERM), not the CommonJS eval change.

Please either:

  1. rerun failed jobs on https://github.com/code-yeongyu/senpi/actions/runs/33625644260, or
  2. merge fix(rpc): root-fix the flaky Windows named-pipes CI job #1294 first so I can refresh this branch onto that Windows RPC fix.

I cannot rerun from jc01rho (403).

Bring in code-yeongyu#1294 so CI no longer fails on rpc-host-ensure SIGTERM / lifecycle probe races.

Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
@jc01rho

jc01rho commented Sep 2, 2026

Copy link
Copy Markdown
Author

Merged latest main so this branch now includes #1294 (fix(rpc): root-fix the flaky Windows named-pipes CI job). New head: 8c6f1eb6d. Auto-merge is still enabled.

The two new runs need first-time-contributor approval again:

@code-yeongyu please approve those two runs. Once they go green, auto-merge should land this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants