@code-yeongyu/senpi-codemode 2026.10.10-4 → 2026.10.10-5
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 +30 -0
- package/README.md +28 -5
- package/package.json +4 -4
- package/src/extension/eval-notifier.ts +13 -2
- package/src/index.ts +2 -2
- package/src/kernels/js/cell-ownership.js +268 -0
- package/src/kernels/js/cell-run-context.js +89 -0
- package/src/kernels/js/process-entry.js +5 -1
- package/src/kernels/js/rejection-reports.js +61 -0
- package/src/kernels/js/worker-core.js +46 -6
- package/src/kernels/js/worker-indirect-eval.js +37 -13
- package/src/kernels/js/worker-require.d.ts +19 -0
- package/src/kernels/js/worker-require.js +52 -0
- package/src/kernels/js/worker-runtime.js +31 -9
- package/src/kernels/js/worker-shadow-guard.js +2 -6
- package/src/kernels/js/worker-shell-capture.js +5 -0
- package/src/kernels/js/worker-webview.js +7 -1
- package/src/tool/detached-cell-contract.ts +3 -0
- package/src/tool/detached-cell-manager.ts +0 -4
- package/src/tool/detached-cell-notification.ts +11 -63
- package/src/tool/detached-notification-queue.ts +1 -2
- package/src/tool/live-headline.ts +60 -0
- package/src/tool/managed-cell.ts +0 -4
- package/src/tool/render.ts +72 -15
package/src/tool/managed-cell.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { AgentToolResult } from "@code-yeongyu/senpi";
|
|
2
2
|
import { type CellDeadlineExpiry, CellDeadlines } from "./cell-deadlines.ts";
|
|
3
3
|
import type { EvalDetachedCellSnapshot, EvalDetachedCellState } from "./detached-cell-contract.ts";
|
|
4
|
-
import { detachedNotificationSpillPath } from "./detached-cell-notification.ts";
|
|
5
4
|
import type { EvalKernel, EvalToolDetails, EvalToolInput } from "./types.ts";
|
|
6
5
|
|
|
7
6
|
export type LiveResultProvider = () => AgentToolResult<EvalToolDetails>;
|
|
@@ -9,7 +8,6 @@ export type LiveResultProvider = () => AgentToolResult<EvalToolDetails>;
|
|
|
9
8
|
export type ManagedCell = {
|
|
10
9
|
readonly cellId: string;
|
|
11
10
|
readonly input: EvalToolInput;
|
|
12
|
-
readonly spillPath: string | undefined;
|
|
13
11
|
readonly startedAtMs: number;
|
|
14
12
|
readonly terminal: PromiseWithResolvers<EvalDetachedCellSnapshot>;
|
|
15
13
|
state: Exclude<EvalDetachedCellState, "detached">;
|
|
@@ -37,7 +35,6 @@ export type ManagedCell = {
|
|
|
37
35
|
export interface ManagedCellInit {
|
|
38
36
|
readonly cellId: string;
|
|
39
37
|
readonly input: EvalToolInput;
|
|
40
|
-
readonly artifactsDir: string | undefined;
|
|
41
38
|
readonly now: () => number;
|
|
42
39
|
readonly defaultHardLimitSeconds: number;
|
|
43
40
|
readonly defaultRunBudgetSeconds: number;
|
|
@@ -60,7 +57,6 @@ export function createManagedCell(init: ManagedCellInit): ManagedCell {
|
|
|
60
57
|
return {
|
|
61
58
|
cellId: init.cellId,
|
|
62
59
|
input: init.input,
|
|
63
|
-
spillPath: detachedNotificationSpillPath(init.artifactsDir, init.cellId),
|
|
64
60
|
startedAtMs: init.now(),
|
|
65
61
|
state: "queued",
|
|
66
62
|
runStartedAtMs: undefined,
|
package/src/tool/render.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type ToolDefinition,
|
|
8
8
|
type ToolRenderResultOptions,
|
|
9
9
|
truncateToVisualLines,
|
|
10
|
+
visibleWidth,
|
|
10
11
|
} from "@code-yeongyu/senpi";
|
|
11
12
|
import type { TruncationMeta } from "../output/output-meta.ts";
|
|
12
13
|
import { highlightedCode } from "./code-preview.ts";
|
|
@@ -21,6 +22,7 @@ import {
|
|
|
21
22
|
JSON_TREE_SCALAR_LEN_EXPANDED,
|
|
22
23
|
renderJsonTreeLines,
|
|
23
24
|
} from "./json-tree.ts";
|
|
25
|
+
import { knownLanguage, leadsWithHeadline, liveHeadline } from "./live-headline.ts";
|
|
24
26
|
import { formatRuntimeBadge } from "./runtime-label.ts";
|
|
25
27
|
import { codePointPrefix, formatDuration, renderToolCallWidget } from "./tool-widgets.ts";
|
|
26
28
|
import type {
|
|
@@ -341,7 +343,9 @@ function cellHeader(cell: EvalCellResult, environment: RenderEnvironment, badges
|
|
|
341
343
|
environment.spinnerFrame ?? Math.floor((cellElapsedMs(cell, environment) ?? 0) / LIVE_RENDER_TICK_MS),
|
|
342
344
|
);
|
|
343
345
|
const runtimeBadge = cell.runtime === undefined ? "" : ` (${formatRuntimeBadge(cell.language, cell.runtime)})`;
|
|
344
|
-
let header =
|
|
346
|
+
let header = leadsWithHeadline(cell.status)
|
|
347
|
+
? `eval ${cell.language}${runtimeBadge} ${presentation.label}`
|
|
348
|
+
: `eval ${cell.language}${runtimeBadge} ${presentation.label} ${presentation.icon}`;
|
|
345
349
|
if (cell.queuedBehind !== undefined && cell.queuedBehind.length > 0)
|
|
346
350
|
header += ` · queued behind ${cell.queuedBehind.map(sanitizeTerminalLabel).join(", ")}`;
|
|
347
351
|
else if (cell.queuedBehind !== undefined && cell.status === "queued")
|
|
@@ -352,9 +356,24 @@ function cellHeader(cell: EvalCellResult, environment: RenderEnvironment, badges
|
|
|
352
356
|
if (elapsedMs !== undefined) header += ` · ${formatDuration(elapsedMs)}`;
|
|
353
357
|
if (badges.reset) header += " · reset";
|
|
354
358
|
if (badges.timeout !== undefined) header += ` · timeout ${badges.timeout}s`;
|
|
359
|
+
if (leadsWithHeadline(cell.status))
|
|
360
|
+
header = headlined(presentation.icon, cell.summary, cell.code, header, environment);
|
|
355
361
|
return style(environment.theme, presentation.color, header);
|
|
356
362
|
}
|
|
357
363
|
|
|
364
|
+
// An in-progress row leads with what the cell is doing (senpi#2802); collapsed, the headline is cut so the whole
|
|
365
|
+
// header stays on one line, and the code moves behind expand.
|
|
366
|
+
function headlined(
|
|
367
|
+
icon: string,
|
|
368
|
+
summary: string | undefined,
|
|
369
|
+
code: string | undefined,
|
|
370
|
+
rest: string,
|
|
371
|
+
environment: RenderEnvironment,
|
|
372
|
+
): string {
|
|
373
|
+
const budget = environment.expanded ? undefined : environment.width - 3 - visibleWidth(`${icon} · ${rest}`);
|
|
374
|
+
return `${icon} ${liveHeadline(summary, code, budget)} · ${rest}`;
|
|
375
|
+
}
|
|
376
|
+
|
|
358
377
|
function previewText(
|
|
359
378
|
text: string,
|
|
360
379
|
maxLines: number,
|
|
@@ -614,12 +633,23 @@ function renderAgentProgressEvents(events: readonly EvalStatusEvent[], environme
|
|
|
614
633
|
}
|
|
615
634
|
|
|
616
635
|
function renderCell(cell: EvalCellResult, environment: RenderEnvironment, badges: CellBadges): string[] {
|
|
636
|
+
if (leadsWithHeadline(cell.status) && !environment.expanded && cell.output.trim().length === 0) {
|
|
637
|
+
const statusEvents = (cell.statusEvents ?? []).filter((event) => event.op !== "agent");
|
|
638
|
+
if (statusEvents.length === 0) {
|
|
639
|
+
// Nothing to frame yet: the collapsed live row is its headline alone, one line, no empty box.
|
|
640
|
+
const lines = renderPrefixed(cellHeader(cell, environment, badges), environment, LIVE_LINE_PREFIX);
|
|
641
|
+
const agentEvents = (cell.statusEvents ?? []).filter((event) => event.op === "agent");
|
|
642
|
+
if (agentEvents.length > 0) appendLines(lines, renderAgentProgressEvents(agentEvents, environment));
|
|
643
|
+
return lines;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
617
646
|
const lines = renderPrefixed(cellHeader(cell, environment, badges), environment, {
|
|
618
647
|
prefix: "╭─ ",
|
|
619
648
|
continuation: "│ ",
|
|
620
649
|
color: "borderAccent",
|
|
621
650
|
});
|
|
622
|
-
|
|
651
|
+
const headlined = leadsWithHeadline(cell.status);
|
|
652
|
+
if (cell.summary !== undefined && !headlined) {
|
|
623
653
|
appendLines(
|
|
624
654
|
lines,
|
|
625
655
|
summaryVisualLines(cell.summary, Math.max(1, environment.width - 2), environment.expanded).map(
|
|
@@ -628,11 +658,14 @@ function renderCell(cell: EvalCellResult, environment: RenderEnvironment, badges
|
|
|
628
658
|
);
|
|
629
659
|
}
|
|
630
660
|
const innerWidth = Math.max(1, environment.width - 2);
|
|
631
|
-
const codePreview =
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
661
|
+
const codePreview =
|
|
662
|
+
headlined && !environment.expanded
|
|
663
|
+
? { lines: [], skipped: 0 }
|
|
664
|
+
: previewText(
|
|
665
|
+
highlightedCode(cell.code, cell.language, environment.theme, environment.repaint),
|
|
666
|
+
environment.expanded ? Number.POSITIVE_INFINITY : CODE_PREVIEW_LINES,
|
|
667
|
+
innerWidth,
|
|
668
|
+
);
|
|
636
669
|
if (codePreview.skipped > 0) {
|
|
637
670
|
appendLines(
|
|
638
671
|
lines,
|
|
@@ -881,6 +914,10 @@ function displaySummary(summary: string | undefined): string | undefined {
|
|
|
881
914
|
return normalizeEvalSummary(summary);
|
|
882
915
|
}
|
|
883
916
|
|
|
917
|
+
function cellIdSuffix(cellId: unknown): string {
|
|
918
|
+
return typeof cellId === "string" && cellId !== "" ? ` ${sanitizeTerminalLabel(cellId)}` : "";
|
|
919
|
+
}
|
|
920
|
+
|
|
884
921
|
export function renderEvalCall(
|
|
885
922
|
args: EvalToolRequest,
|
|
886
923
|
theme: Theme | undefined,
|
|
@@ -894,15 +931,23 @@ export function renderEvalCall(
|
|
|
894
931
|
return component;
|
|
895
932
|
}
|
|
896
933
|
if (!isEvalRunInput(args)) {
|
|
897
|
-
|
|
934
|
+
// While a peek/stop call streams in, `cell_id` can still be missing; the title is the action alone until it arrives.
|
|
935
|
+
const title = args.action === "list" ? "eval list" : `eval ${args.action}${cellIdSuffix(args.cell_id)}`;
|
|
898
936
|
component.setBlocks([{ kind: "text", text: style(theme, "toolTitle", title) }]);
|
|
899
937
|
return component;
|
|
900
938
|
}
|
|
939
|
+
// While the model streams the call, `code` usually arrives before `language` and `summary`; a partial call still
|
|
940
|
+
// renders (the host would otherwise fall back to a raw key=value row).
|
|
941
|
+
const code = typeof args.code === "string" ? args.code : "";
|
|
942
|
+
const language = knownLanguage(args.language);
|
|
901
943
|
if (theme === undefined && context.spinnerFrame === undefined) {
|
|
902
944
|
const reset = args.reset === true ? " reset" : "";
|
|
903
945
|
const timeout = args.timeout === undefined ? "" : ` timeout ${args.timeout}s`;
|
|
904
946
|
component.setBlocks([
|
|
905
|
-
{
|
|
947
|
+
{
|
|
948
|
+
kind: "text",
|
|
949
|
+
text: style(theme, "toolTitle", `eval${language === undefined ? "" : ` ${language}`}${reset}${timeout}`),
|
|
950
|
+
},
|
|
906
951
|
...(displaySummary(args.summary) === undefined
|
|
907
952
|
? []
|
|
908
953
|
: [summaryBlock(displaySummary(args.summary) ?? "", theme, context.expanded)]),
|
|
@@ -911,9 +956,11 @@ export function renderEvalCall(
|
|
|
911
956
|
text: style(
|
|
912
957
|
theme,
|
|
913
958
|
"mdCodeBlock",
|
|
914
|
-
|
|
915
|
-
?
|
|
916
|
-
:
|
|
959
|
+
code.trim().length === 0
|
|
960
|
+
? "..."
|
|
961
|
+
: language === undefined
|
|
962
|
+
? code
|
|
963
|
+
: displayCode(code, language, context.argsComplete ? context.invalidate : undefined),
|
|
917
964
|
),
|
|
918
965
|
maxVisualLines: context.expanded ? undefined : CODE_PREVIEW_LINES,
|
|
919
966
|
collapseKind: "code",
|
|
@@ -935,11 +982,13 @@ export function renderEvalCall(
|
|
|
935
982
|
now: renderNow(context),
|
|
936
983
|
...(context.argsComplete ? { repaint: context.invalidate } : {}),
|
|
937
984
|
};
|
|
985
|
+
const summary = displaySummary(args.summary);
|
|
986
|
+
if (language === undefined) return streamingCallLines(summary, code, environment);
|
|
938
987
|
const cell: EvalCellResult = {
|
|
939
988
|
index: 0,
|
|
940
|
-
...(
|
|
941
|
-
code
|
|
942
|
-
language
|
|
989
|
+
...(summary === undefined ? {} : { summary }),
|
|
990
|
+
code,
|
|
991
|
+
language,
|
|
943
992
|
output: "",
|
|
944
993
|
status: context.spinnerFrame === undefined ? "pending" : "running",
|
|
945
994
|
};
|
|
@@ -954,6 +1003,14 @@ export function renderEvalCall(
|
|
|
954
1003
|
return component;
|
|
955
1004
|
}
|
|
956
1005
|
|
|
1006
|
+
/** A call whose language has not streamed in yet: the headline row alone, until the full frame can render. */
|
|
1007
|
+
function streamingCallLines(summary: string | undefined, code: string, environment: RenderEnvironment): string[] {
|
|
1008
|
+
const icon = environment.spinnerFrame === undefined ? "○" : spinner(environment.spinnerFrame);
|
|
1009
|
+
return renderPrefixed(headlined(icon, summary, code, "eval", environment), environment, LIVE_LINE_PREFIX);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
const LIVE_LINE_PREFIX: PrefixStyle = { prefix: "╶─ ", continuation: " ", color: "borderAccent" };
|
|
1013
|
+
|
|
957
1014
|
export function renderEvalResult(
|
|
958
1015
|
result: AgentToolResult<EvalResultDetails>,
|
|
959
1016
|
options: ToolRenderResultOptions,
|