@code-yeongyu/senpi-codemode 2026.10.10-2 → 2026.10.10-4

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 (38) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +80 -2
  3. package/package.json +4 -4
  4. package/src/bridge/protocol.ts +8 -3
  5. package/src/extension/kernel-registration.ts +7 -2
  6. package/src/extension/runtime-factory.ts +8 -1
  7. package/src/extension/runtime-info.ts +6 -2
  8. package/src/extension/session-manager.ts +9 -2
  9. package/src/kernels/AGENTS.md +2 -0
  10. package/src/kernels/js/context-manager.ts +23 -24
  11. package/src/kernels/js/control-frames.ts +24 -0
  12. package/src/kernels/js/kernel-contract.ts +14 -1
  13. package/src/kernels/js/kernel-memory-bridge.ts +1 -1
  14. package/src/kernels/js/kernel-memory.ts +79 -0
  15. package/src/kernels/js/process-drain-scan.d.ts +8 -0
  16. package/src/kernels/js/process-drain-scan.js +28 -0
  17. package/src/kernels/js/process-entry.js +462 -0
  18. package/src/kernels/js/process-memory-host.ts +83 -0
  19. package/src/kernels/js/process-worker.ts +298 -0
  20. package/src/kernels/js/run-queue.ts +7 -0
  21. package/src/kernels/js/worker-core.js +4 -2
  22. package/src/kernels/js/worker-cwd.js +5 -2
  23. package/src/kernels/js/worker-host.ts +9 -2
  24. package/src/kernels/js/worker-slot.ts +9 -0
  25. package/src/kernels/js/worker-startup.ts +48 -1
  26. package/src/kernels/js/worker-webview.js +8 -0
  27. package/src/kernels/sandbox/sandbox-cell.ts +26 -1
  28. package/src/kernels/shared/subprocess-process.ts +14 -0
  29. package/src/tool/AGENTS.md +1 -0
  30. package/src/tool/cell-runtime.ts +4 -2
  31. package/src/tool/detached-cell-manager.ts +4 -4
  32. package/src/tool/detached-cell-snapshot.ts +17 -0
  33. package/src/tool/detached-eval-result.ts +13 -7
  34. package/src/tool/interrupt-note.ts +4 -5
  35. package/src/tool/render.ts +2 -0
  36. package/src/tool/run-eval-cell.ts +3 -2
  37. package/src/tool/runtime-label.ts +2 -1
  38. package/src/tool/types.ts +2 -0
@@ -63,6 +63,23 @@ export function currentDetachedResult(cell: DetachedCellResultSource): AgentTool
63
63
  return cell.terminalResult ?? cell.liveResult?.() ?? fallbackResult(cell.input);
64
64
  }
65
65
 
66
+ /**
67
+ * The result a cell cancelled before its handler finished settles with: its buffered output, not the live progress
68
+ * frame ("1/1 cells running"), so a stopped cell never reads as still running.
69
+ */
70
+ export function cancelledDetachedResult(cell: DetachedCellResultSource): AgentToolResult<EvalToolDetails> {
71
+ if (cell.terminalResult !== undefined) return cell.terminalResult;
72
+ const current = currentDetachedResult(cell);
73
+ const output = detachedOutputTail(current);
74
+ return {
75
+ content: [
76
+ { type: "text", text: output.length > 0 ? output : "(no output)" },
77
+ ...current.content.filter((part) => part.type === "image"),
78
+ ],
79
+ details: current.details,
80
+ };
81
+ }
82
+
66
83
  export function detachedErrorResult(cell: DetachedCellResultSource, error: Error): AgentToolResult<EvalToolDetails> {
67
84
  const current = currentDetachedResult(cell);
68
85
  const output = detachedOutputTail(current);
@@ -46,9 +46,7 @@ function createEvalListResult(cellManager: EvalDetachedCellManager): AgentToolRe
46
46
  language: snapshot.language,
47
47
  state: snapshot.state,
48
48
  startedAtMs: snapshot.startedAtMs,
49
- ...(snapshot.queuedBehind === undefined || snapshot.queuedBehind.length === 0
50
- ? {}
51
- : { queuedBehind: [...snapshot.queuedBehind] }),
49
+ ...(snapshot.queuedBehind === undefined ? {} : { queuedBehind: [...snapshot.queuedBehind] }),
52
50
  ...(summary ? { summary } : {}),
53
51
  };
54
52
  });
@@ -60,7 +58,7 @@ function createEvalListResult(cellManager: EvalDetachedCellManager): AgentToolRe
60
58
  " ",
61
59
  );
62
60
  const elapsed = Math.floor(snapshot.result.details.durationMs / 1000);
63
- const queued = cell.queuedBehind === undefined ? "" : ` queued behind ${cell.queuedBehind.join(", ")}`;
61
+ const queued = cell.queuedBehind === undefined ? "" : ` ${queuedPhrase(cell.queuedBehind, cell.language)}`;
64
62
  return `${cell.cellId} ${cell.language} ${cell.state} ${elapsed}s${queued} - ${preview}`;
65
63
  })
66
64
  .join("\n");
@@ -76,11 +74,13 @@ export function resultAfterDetach(
76
74
  otherLiveCells: number,
77
75
  ): AgentToolResult<EvalToolDetails> {
78
76
  if (snapshot.state !== "detached" && snapshot.state !== "running") return createDetachedControlResult(snapshot);
79
- const predecessors = snapshot.queuedBehind?.join(", ");
77
+ const queuedBehind = snapshot.queuedBehind;
80
78
  const text =
81
- predecessors === undefined
79
+ queuedBehind === undefined
82
80
  ? `Eval cell ${snapshot.cellId} detached and is running in the ${input.language} kernel (${otherLiveCells} other live cells). Completion arrives as a notification; do not re-run it. eval({ action: "peek" | "stop", cell_id }) or eval({ action: "list" }).`
83
- : `Eval cell ${snapshot.cellId} is queued behind ${predecessors} in the ${input.language} kernel and detached; it runs after ${predecessors} and completes as one notification. peek/stop/list with eval({ action, cell_id })`;
81
+ : queuedBehind.length === 0
82
+ ? `Eval cell ${snapshot.cellId} is detached and waiting for the ${input.language} kernel to be ready; it runs first once the kernel is ready and completes as one notification. peek/stop/list with eval({ action, cell_id })`
83
+ : `Eval cell ${snapshot.cellId} is queued behind ${queuedBehind.join(", ")} in the ${input.language} kernel and detached; it runs after ${queuedBehind.join(", ")} and completes as one notification. peek/stop/list with eval({ action, cell_id })`;
84
84
  return {
85
85
  content: [
86
86
  {
@@ -164,6 +164,12 @@ export function resultForDetachedState(
164
164
  };
165
165
  }
166
166
 
167
+ function queuedPhrase(queuedBehind: readonly string[], language: string): string {
168
+ return queuedBehind.length === 0
169
+ ? `waiting for the ${language} kernel to be ready`
170
+ : `queued behind ${queuedBehind.join(", ")}`;
171
+ }
172
+
167
173
  function textContent(result: AgentToolResult<EvalToolDetails>): string {
168
174
  return result.content
169
175
  .filter((part) => part.type === "text")
@@ -26,9 +26,8 @@ export async function describeTimeoutState(
26
26
  );
27
27
  if (outcome === undefined) error.message = fallbackTimeoutMessage(error.message);
28
28
  else if (outcome.retained)
29
- error.message = `${error.message} The kernel remains running; its existing variables are preserved.`;
30
- else
31
- error.message = `${error.message} The kernel was unresponsive and restarted; variables from earlier cells are lost.`;
29
+ error.message = `${error.message} The kernel was not restarted; variables from earlier cells are kept.`;
30
+ else error.message = `${error.message} The kernel was restarted; variables from earlier cells are lost.`;
32
31
  if (outcome?.note !== undefined) error.message = `${error.message} ${outcome.note.trim()}`;
33
32
  return error;
34
33
  }
@@ -63,8 +62,8 @@ const LANGUAGE_LABEL: Record<EvalLanguage, string> = {
63
62
  export function interruptionStateNote(language: EvalLanguage, stateRetained: boolean | undefined): string | undefined {
64
63
  if (stateRetained === undefined) return undefined;
65
64
  const label = LANGUAGE_LABEL[language];
66
- if (stateRetained) return `${label} was interrupted and remains running; its existing variables are preserved.`;
67
- return `${label} was unresponsive to interrupt and was restarted; variables from earlier cells are lost.`;
65
+ if (stateRetained) return `The ${label} was not restarted; variables from earlier cells are kept.`;
66
+ return `The ${label} was restarted; variables from earlier cells are lost.`;
68
67
  }
69
68
 
70
69
  export function unknownInterruptionStateNote(language: EvalLanguage): string {
@@ -344,6 +344,8 @@ function cellHeader(cell: EvalCellResult, environment: RenderEnvironment, badges
344
344
  let header = `eval ${cell.language}${runtimeBadge} ${presentation.label} ${presentation.icon}`;
345
345
  if (cell.queuedBehind !== undefined && cell.queuedBehind.length > 0)
346
346
  header += ` · queued behind ${cell.queuedBehind.map(sanitizeTerminalLabel).join(", ")}`;
347
+ else if (cell.queuedBehind !== undefined && cell.status === "queued")
348
+ header += ` · waiting for the ${cell.language} kernel to be ready`;
347
349
  const throughputBadge = badges.throughput === undefined ? undefined : formatThroughputBadge(badges.throughput);
348
350
  if (throughputBadge !== undefined) header += ` · ${throughputBadge}`;
349
351
  const elapsedMs = badges.throughput?.wallDurationMs ?? cellElapsedMs(cell, environment);
@@ -14,7 +14,7 @@ import {
14
14
  type KernelToolsCapability,
15
15
  type KernelToolsDescribeResult,
16
16
  } from "../kernels/js/kernel-tools-types.ts";
17
- import { sandboxCellExecutor } from "../kernels/sandbox/sandbox-cell.ts";
17
+ import { sandboxCellExecutor, sandboxRuntimeInfo } from "../kernels/sandbox/sandbox-cell.ts";
18
18
  import { TIMEOUT_PAUSE_OP, TIMEOUT_RESUME_OP } from "../timeouts/bridge-timeout.ts";
19
19
  import { abortError, CellExecution, defaultTimeoutFactory } from "./cell-execution.ts";
20
20
  import { CellHandler, type CellState } from "./cell-handler.ts";
@@ -44,7 +44,8 @@ export async function runEvalCell(
44
44
  const bridgeAbortController = new AbortController();
45
45
  const cellSignal = AbortSignal.any([invocation.signal, bridgeAbortController.signal]);
46
46
  const bridgeContext: ExtensionContext = { ...invocation.ctx, signal: cellSignal };
47
- const runtime = options.runtimes?.[invocation.input.language];
47
+ const runtime =
48
+ invocation.input.isolate === true ? sandboxRuntimeInfo() : options.runtimes?.[invocation.input.language];
48
49
  const state: CellState = {
49
50
  input: invocation.input,
50
51
  ...(runtime === undefined ? {} : { runtime }),
@@ -10,7 +10,8 @@ const ELLIPSIS = "\u2026";
10
10
  * runtime name because node and bun are otherwise indistinguishable.
11
11
  */
12
12
  export function formatRuntimeBadge(language: EvalLanguage, runtime: EvalRuntimeInfo, home: string = homedir()): string {
13
- const label = language === "js" ? `${runtime.name} ${runtime.version}` : runtime.version;
13
+ const base = language === "js" ? `${runtime.name} ${runtime.version}` : runtime.version;
14
+ const label = runtime.isolation === undefined ? base : `${base}, ${runtime.isolation}`;
14
15
  if (runtime.path === undefined || runtime.path.length === 0) return label;
15
16
  return `${label}, ${minifyPath(runtime.path, home)}`;
16
17
  }
package/src/tool/types.ts CHANGED
@@ -279,6 +279,8 @@ export interface EvalRuntimeInfo {
279
279
  readonly name: string;
280
280
  readonly version: string;
281
281
  readonly path?: string;
282
+ /** JS only: the kernel runs in a child process (`process`), or the cell ran in an isolated QuickJS VM (`sandbox`). */
283
+ readonly isolation?: "process" | "sandbox";
282
284
  }
283
285
 
284
286
  export type EvalRuntimes = Readonly<Partial<Record<EvalLanguage, EvalRuntimeInfo>>>;