@code-yeongyu/senpi-codemode 2026.10.9 → 2026.10.10-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.
Files changed (45) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +52 -4
  3. package/package.json +6 -5
  4. package/src/config/executable-settings.json +5 -0
  5. package/src/config/project-trust.ts +51 -0
  6. package/src/config/settings-overrides.ts +2 -0
  7. package/src/environments/js-environments.ts +208 -0
  8. package/src/environments/js-installer.ts +268 -0
  9. package/src/environments/js-revision-files.ts +91 -0
  10. package/src/environments/no-links.ts +132 -0
  11. package/src/environments/project-resolves.ts +54 -0
  12. package/src/environments/revision-store.ts +20 -4
  13. package/src/extension/runtime-factory.ts +32 -2
  14. package/src/extension/session-manager.ts +8 -1
  15. package/src/index.ts +1 -0
  16. package/src/interpreters/detect.ts +34 -1
  17. package/src/kernels/js/cell-dispatch.ts +44 -0
  18. package/src/kernels/js/context-manager.ts +23 -27
  19. package/src/kernels/js/host-entries.ts +62 -0
  20. package/src/kernels/js/kernel-contract.ts +2 -0
  21. package/src/kernels/js/kernel-tools-registry.js +1 -0
  22. package/src/kernels/js/local-module-loader.ts +17 -6
  23. package/src/kernels/js/worker-core.js +3 -0
  24. package/src/kernels/js/worker-package-resolve.js +153 -0
  25. package/src/kernels/js/worker-runtime.js +1 -0
  26. package/src/kernels/js/worker-startup.ts +1 -0
  27. package/src/kernels/py/host-cell.ts +8 -31
  28. package/src/kernels/py/prelude.py +10 -2
  29. package/src/kernels/py/process.ts +4 -0
  30. package/src/kernels/sandbox/sandbox-cell.ts +116 -0
  31. package/src/kernels/sandbox/vendor/pi-codemode/VENDORED.md +1 -0
  32. package/src/kernels/sandbox/vendor/pi-codemode/runtime/prelude-source.ts +14 -1
  33. package/src/kernels/sandbox/vendor/pi-codemode/runtime/worker.ts +4 -1
  34. package/src/kernels/sandbox/vendor/pi-codemode/types.ts +2 -0
  35. package/src/kernels/shared/host-cell.ts +57 -0
  36. package/src/prompt/eval-prompt.ts +8 -1
  37. package/src/tool/cell-handler.ts +9 -11
  38. package/src/tool/eval-request.ts +23 -1
  39. package/src/tool/eval-tool-options.ts +3 -0
  40. package/src/tool/eval-tool.ts +17 -4
  41. package/src/tool/load-cell.ts +18 -3
  42. package/src/tool/magic-cell-host.ts +60 -2
  43. package/src/tool/magic-cells.ts +12 -2
  44. package/src/tool/run-eval-cell.ts +30 -3
  45. package/src/tool/types.ts +21 -1
@@ -7,12 +7,14 @@ import {
7
7
  kernelToolsStorage,
8
8
  } from "@code-yeongyu/senpi";
9
9
  import type { KernelToHostMessage } from "../bridge/protocol.ts";
10
+ import { resolveSandbox } from "../config/feature-settings.ts";
10
11
  import { DEFAULT_FOREGROUND_WINDOW_SECONDS, defaultCodemodeSettings } from "../config/settings.ts";
11
12
  import {
12
13
  KERNEL_TOOLS_CAPABILITIES,
13
14
  type KernelToolsCapability,
14
15
  type KernelToolsDescribeResult,
15
16
  } from "../kernels/js/kernel-tools-types.ts";
17
+ import { sandboxCellExecutor } from "../kernels/sandbox/sandbox-cell.ts";
16
18
  import { TIMEOUT_PAUSE_OP, TIMEOUT_RESUME_OP } from "../timeouts/bridge-timeout.ts";
17
19
  import { abortError, CellExecution, defaultTimeoutFactory } from "./cell-execution.ts";
18
20
  import { CellHandler, type CellState } from "./cell-handler.ts";
@@ -261,25 +263,50 @@ async function executeCell(
261
263
  }
262
264
  if (invocation.input.reset) await execution.wait(kernel.reset());
263
265
  execution.setKernel(kernel);
264
- const magic = planMagicCell(invocation.input.language, invocation.input.code, options.pythonEnvironments);
266
+ const magic = planMagicCell(
267
+ invocation.input.language,
268
+ invocation.input.code,
269
+ options.pythonEnvironments,
270
+ options.jsEnvironments,
271
+ );
265
272
  // Resolved when the cell's turn comes in the kernel's queue: a %load reads the file the cells ahead of it
266
273
  // wrote, and a refusal settles in queue order like any cell.
267
274
  const loadOptions = { cwd: invocation.ctx.cwd, artifactsDir: options.artifactsDir };
268
- const resolveAtStart =
269
- magic.kind === "load"
275
+ // An isolated cell never runs a file outside its sandbox: %load there is refused.
276
+ const isolatedLoad = invocation.input.isolate === true && magic.kind === "load";
277
+ const resolveAtStart = isolatedLoad
278
+ ? () => ({
279
+ ok: false as const,
280
+ message:
281
+ "isolate: true cannot run a %load cell: an isolated cell sees no host files, and loading one would break that isolation. Run %load in a normal cell, or paste the code into the isolated cell.",
282
+ })
283
+ : magic.kind === "load"
270
284
  ? () => loadCell(magic.target, loadOptions)
271
285
  : magic.kind === "refused"
272
286
  ? () => ({ ok: false as const, message: magic.message })
273
287
  : undefined;
288
+ const isolated =
289
+ invocation.input.isolate === true && resolveAtStart === undefined
290
+ ? sandboxCellExecutor(invocation.input.code, {
291
+ sandbox: resolveSandbox(options.settings ?? {}),
292
+ executeTool: options.executeTool,
293
+ toolNames: () => (options.listTools?.() ?? []).map((tool) => tool.name),
294
+ describeTool: (name) => options.listTools?.().find((tool) => tool.name === name)?.description,
295
+ })
296
+ : undefined;
274
297
  const environments = invocation.input.language === "py" ? options.pythonEnvironments : undefined;
275
298
  const envRoot = environments === undefined ? undefined : () => environments.activeRoot ?? "";
299
+ const jsEnvironments = invocation.input.language === "js" ? options.jsEnvironments : undefined;
300
+ const packageRoot = jsEnvironments === undefined ? undefined : () => jsEnvironments.packageRoot;
276
301
  const result = await execution.wait(
277
302
  kernel.run({
278
303
  cellId: invocation.cellId,
279
304
  code: invocation.input.code,
280
305
  ...(resolveAtStart === undefined ? {} : { resolveAtStart }),
281
306
  ...(magic.kind === "host" ? { host: magic.executor } : {}),
307
+ ...(isolated === undefined ? {} : { host: isolated }),
282
308
  ...(envRoot === undefined ? {} : { envRoot }),
309
+ ...(packageRoot === undefined ? {} : { packageRoot }),
283
310
  ...(bridgeCellToken === undefined ? {} : { bridgeCellToken }),
284
311
  kernelPreludes: options.kernelPreludes?.(),
285
312
  onMessage,
package/src/tool/types.ts CHANGED
@@ -52,6 +52,8 @@ export interface EvalToolInput {
52
52
  readonly timeout?: number;
53
53
  readonly on_timeout?: "detach" | "error";
54
54
  readonly reset?: boolean;
55
+ /** Run in a fresh sandbox VM instead of the persistent kernel; accepted only while `sandbox.enabled` is on. */
56
+ readonly isolate?: boolean;
55
57
  }
56
58
 
57
59
  export interface EvalListInput {
@@ -117,14 +119,26 @@ const fullEvalInputSchema = Type.Object(
117
119
  /** Runtime accepts a discriminated run/control union. */
118
120
  export type EvalInputSchema = TUnsafe<EvalToolRequest> & Pick<typeof fullEvalInputSchema, "properties">;
119
121
 
122
+ const ISOLATE_FIELD_DESCRIPTION =
123
+ "js only. Fresh sandbox per call: no persistence, no fs/net/process/timers; only tool.* plus print/display.";
124
+
120
125
  export function createEvalInputSchema(
121
126
  enabled: EnabledEvalLanguages,
122
127
  deadlines: EvalDeadlineSeconds = defaultEvalDeadlineSeconds,
128
+ options: { readonly sandbox?: boolean } = {},
123
129
  ): EvalInputSchema {
124
130
  const languages = enabledLanguageList(enabled);
125
131
  if (languages.length === 0) throw new Error("eval requires at least one enabled language");
126
132
  const languageSchema = evalLanguageUnion(languages);
127
- const properties = evalInputProperties(languageSchema, deadlines);
133
+ // The sandbox field exists in the schema only while sandbox cells are turned on, so the default schema
134
+ // (and the prompt built from it) stays exactly what it was.
135
+ const properties =
136
+ options.sandbox === true && languages.includes("js")
137
+ ? {
138
+ ...evalInputProperties(languageSchema, deadlines),
139
+ isolate: Type.Optional(Type.Boolean({ description: ISOLATE_FIELD_DESCRIPTION })),
140
+ }
141
+ : evalInputProperties(languageSchema, deadlines);
128
142
  return Type.Unsafe<EvalToolRequest>(
129
143
  Type.Object(properties, {
130
144
  // Keep branches self-contained for Mistral-hosted GLM (#2240).
@@ -175,6 +189,12 @@ export interface EvalKernelRunInput {
175
189
  readonly envRoot?: () => string;
176
190
  /** The file a `%load` cell runs: tracebacks name it and its relative imports resolve from its directory. */
177
191
  readonly sourceFile?: string;
192
+ /**
193
+ * JavaScript only: the session's managed package revision; bare imports that do not resolve from cwd fall back to
194
+ * it. Read when the cell starts running (not when it was queued), so a cell queued behind `%bun add` resolves the
195
+ * revision that install published.
196
+ */
197
+ readonly packageRoot?: () => string | undefined;
178
198
  /**
179
199
  * Called when the cell's turn comes in the kernel's queue, never earlier: what to run instead of `code` (a `%load`
180
200
  * cell's file), or a refusal that settles the cell as failed in queue order.