@code-yeongyu/senpi-codemode 2026.9.5-3 → 2026.9.6

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.
@@ -1,4 +1,4 @@
1
- import type { EvalLanguage } from "./types.ts";
1
+ import type { EvalLanguage, KernelInterruptHandle } from "./types.ts";
2
2
 
3
3
  const TIMEOUT_STATE_GRACE_MS = 5_500;
4
4
 
@@ -13,30 +13,40 @@ function fallbackTimeoutMessage(base: string): string {
13
13
  */
14
14
  export async function describeTimeoutState(
15
15
  error: Error,
16
- execution: { readonly interruptStateRetained: Promise<boolean> | undefined },
16
+ execution: { readonly interruptHandle: Promise<KernelInterruptHandle> | undefined },
17
17
  ): Promise<Error> {
18
- const outcome = execution.interruptStateRetained;
19
- if (outcome === undefined) {
18
+ const pending = execution.interruptHandle;
19
+ if (pending === undefined) {
20
20
  error.message = fallbackTimeoutMessage(error.message);
21
21
  return error;
22
22
  }
23
- let timer: ReturnType<typeof setTimeout> | undefined;
24
- const retained = await Promise.race([
25
- outcome,
26
- new Promise<boolean | undefined>((resolve) => {
27
- timer = setTimeout(() => resolve(undefined), TIMEOUT_STATE_GRACE_MS);
28
- }),
29
- ]).finally(() => {
30
- if (timer !== undefined) clearTimeout(timer);
31
- });
32
- if (retained === undefined) error.message = fallbackTimeoutMessage(error.message);
33
- else if (retained)
23
+ const outcome = await withinGrace(
24
+ pending.then(async (handle) => ({ retained: await handle.stateRetained, note: handle.note })),
25
+ TIMEOUT_STATE_GRACE_MS,
26
+ );
27
+ if (outcome === undefined) error.message = fallbackTimeoutMessage(error.message);
28
+ else if (outcome.retained)
34
29
  error.message = `${error.message} The kernel remains running; its existing variables are preserved.`;
35
30
  else
36
31
  error.message = `${error.message} The kernel was unresponsive and restarted; variables from earlier cells are lost.`;
32
+ if (outcome?.note !== undefined) error.message = `${error.message} ${outcome.note.trim()}`;
37
33
  return error;
38
34
  }
39
35
 
36
+ async function withinGrace<T>(operation: Promise<T>, graceMs: number): Promise<T | undefined> {
37
+ let timer: ReturnType<typeof setTimeout> | undefined;
38
+ try {
39
+ return await Promise.race([
40
+ operation,
41
+ new Promise<undefined>((resolve) => {
42
+ timer = setTimeout(() => resolve(undefined), graceMs);
43
+ }),
44
+ ]);
45
+ } finally {
46
+ if (timer !== undefined) clearTimeout(timer);
47
+ }
48
+ }
49
+
40
50
  const LANGUAGE_LABEL: Record<EvalLanguage, string> = {
41
51
  py: "Python kernel",
42
52
  js: "JavaScript worker",
@@ -56,3 +66,7 @@ export function interruptionStateNote(language: EvalLanguage, stateRetained: boo
56
66
  if (stateRetained) return `${label} was interrupted and remains running; its existing variables are preserved.`;
57
67
  return `${label} was unresponsive to interrupt and was restarted; variables from earlier cells are lost.`;
58
68
  }
69
+
70
+ export function unknownInterruptionStateNote(language: EvalLanguage): string {
71
+ return `${LANGUAGE_LABEL[language]} interrupt outcome is unknown; re-establish any variables the next cell needs.`;
72
+ }
package/src/tool/types.ts CHANGED
@@ -112,6 +112,8 @@ export interface EvalKernelRunInput {
112
112
  export interface KernelInterruptHandle {
113
113
  /** Resolves once the kernel knows whether user state survived the interrupt. */
114
114
  readonly stateRetained: Promise<boolean>;
115
+ /** Extra outcome detail worth showing the model, e.g. that a blocked worker was abandoned. */
116
+ readonly note?: string;
115
117
  }
116
118
 
117
119
  export interface EvalKernel {