@code-yeongyu/senpi-codemode 2026.8.3-3 → 2026.8.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.
- package/CHANGELOG.md +16 -0
- package/README.md +13 -3
- package/package.json +4 -4
- package/src/extension/eval-status.ts +1 -1
- package/src/prompt/eval-prompt.ts +6 -6
- package/src/tool/cell-runtime.ts +4 -4
- package/src/tool/detached-cell-manager.ts +2 -2
- package/src/tool/detached-cell-snapshot.ts +2 -2
- package/src/tool/eval-request.ts +20 -2
- package/src/tool/eval-tool.ts +17 -2
- package/src/tool/render.ts +18 -7
- package/src/tool/types.ts +19 -5
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,22 @@
|
|
|
12
12
|
|
|
13
13
|
### Removed
|
|
14
14
|
|
|
15
|
+
## [2026.8.4] - 2026-08-04
|
|
16
|
+
|
|
17
|
+
### Breaking Changes
|
|
18
|
+
|
|
19
|
+
- Replaced the eval tool's optional presentation `title` with a required user-language `summary`: every eval call must now describe the cell's purpose in the user's language, callers using `title` must migrate to `summary`, and the generated tool schema, prompt contract, README examples, bridge fixtures, and test corpus all enforce the new argument ([#695](https://github.com/code-yeongyu/senpi/pull/695)).
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Rendered each eval summary inside its transcript cell frame and used the same summary to label detached cells and their completion notices, so concurrent or long-running JavaScript and Python work remains identifiable after detachment and when results arrive asynchronously ([#695](https://github.com/code-yeongyu/senpi/pull/695)).
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
### Removed
|
|
30
|
+
|
|
15
31
|
## [2026.8.3-3] - 2026-08-03
|
|
16
32
|
|
|
17
33
|
### Breaking Changes
|
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ task-tool names are known.
|
|
|
14
14
|
handle and continue in their existing kernel. Completion is injected with the
|
|
15
15
|
final value/error and buffered output; use `eval({ action: "peek"|"stop",
|
|
16
16
|
cell_id })` to inspect or terminate a detached cell. A running peek preserves
|
|
17
|
-
the original code and
|
|
17
|
+
the original code and summary together with current output, phase, status
|
|
18
18
|
events, tool-call summaries, elapsed duration, and structured display state;
|
|
19
19
|
a terminal peek preserves the exact final result.
|
|
20
20
|
- Loopback, bearer-authenticated kernel bridge with bounded JSONL frames.
|
|
@@ -127,6 +127,16 @@ updates, and transcripts remain owned by that engine.
|
|
|
127
127
|
`isolated`, `apply`, and `merge` are accepted for compatibility but emit a
|
|
128
128
|
warning because this task-engine integration has no isolation model.
|
|
129
129
|
|
|
130
|
+
## Required summary
|
|
131
|
+
|
|
132
|
+
Every `eval` run call MUST include a `summary` — one line in the user's
|
|
133
|
+
conversational language stating what the cell does and for what purpose (e.g.
|
|
134
|
+
a Korean conversation produces a Korean summary such as "src 전체에서
|
|
135
|
+
legacyClient 사용처 집계"). The summary is shown in the TUI while the cell
|
|
136
|
+
runs and in the finished result, so you can always tell what is running and
|
|
137
|
+
why. Values longer than 80 characters are force-truncated. A run request
|
|
138
|
+
without a `summary` fails with a teaching error.
|
|
139
|
+
|
|
130
140
|
## Detached cells
|
|
131
141
|
|
|
132
142
|
`eval` accepts `on_timeout: "detach"|"error"`. The default is `"detach"` in
|
|
@@ -137,8 +147,8 @@ a busy error with its cell id and output tail; calls in other languages continue
|
|
|
137
147
|
normally. Do not re-run the cell.
|
|
138
148
|
|
|
139
149
|
While any cell is detached, the interactive footer shows a highlighted
|
|
140
|
-
`↗ <language> · <
|
|
141
|
-
the call had no
|
|
150
|
+
`↗ <language> · <summary>` status on the extension status line (the cell id
|
|
151
|
+
when the call had no summary), clearing as soon as the last detached cell settles.
|
|
142
152
|
|
|
143
153
|
Use `eval({ action: "peek", cell_id })` for its state and buffered output, or
|
|
144
154
|
`eval({ action: "stop", cell_id })` to cancel it. Python stop interrupts the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi-codemode",
|
|
3
|
-
"version": "2026.8.
|
|
3
|
+
"version": "2026.8.4",
|
|
4
4
|
"description": "Source-only senpi extension package for codemode evaluation tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -30,14 +30,14 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@babel/parser": "8.0.4",
|
|
33
|
-
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.8.
|
|
33
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.8.4",
|
|
34
34
|
"typebox": "1.3.8"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@code-yeongyu/senpi": "2026.8.
|
|
37
|
+
"@code-yeongyu/senpi": "2026.8.4"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@code-yeongyu/senpi": "2026.8.
|
|
40
|
+
"@code-yeongyu/senpi": "2026.8.4"
|
|
41
41
|
},
|
|
42
42
|
"keywords": [
|
|
43
43
|
"senpi",
|
|
@@ -28,7 +28,7 @@ function packLabels(labels: readonly string[], budget: number): string {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function labelOf(entry: EvalDetachedCellStatusEntry): string {
|
|
31
|
-
return entry.
|
|
31
|
+
return entry.summary === undefined || entry.summary.length === 0 ? entry.cellId : entry.summary;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
@@ -59,7 +59,7 @@ type Context = Readonly<Record<string, ContextValue>>;
|
|
|
59
59
|
type EvalPromptExample = {
|
|
60
60
|
readonly caption: string;
|
|
61
61
|
readonly language: keyof EnabledLanguages;
|
|
62
|
-
readonly
|
|
62
|
+
readonly summary: string;
|
|
63
63
|
readonly code: string;
|
|
64
64
|
};
|
|
65
65
|
|
|
@@ -71,19 +71,19 @@ const REUSE_CHAIN_EXAMPLES = [
|
|
|
71
71
|
{
|
|
72
72
|
caption: "First call — set up once",
|
|
73
73
|
language: "py",
|
|
74
|
-
|
|
74
|
+
summary: "Count all TypeScript source files under src/ excluding tests",
|
|
75
75
|
code: "from pathlib import Path\nfrom collections import Counter\nfiles = [p for p in Path('src').rglob('*.ts') if 'test' not in p.parts]\nprint(len(files))",
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
78
|
caption: "Second call — reuse `files`, batch-read in one cell",
|
|
79
79
|
language: "py",
|
|
80
|
-
|
|
80
|
+
summary: "Find which files reference legacyClient so we know what to migrate",
|
|
81
81
|
code: "hits = Counter()\nfor p in files:\n hits[p.name] = read(p).count('legacyClient')\ndisplay({k: v for k, v in hits.items() if v})",
|
|
82
82
|
},
|
|
83
83
|
{
|
|
84
84
|
caption: "Third call — reuse results, fan out session tools in parallel",
|
|
85
85
|
language: "py",
|
|
86
|
-
|
|
86
|
+
summary: "Confirm exact callsite lines in each directory to plan the refactor",
|
|
87
87
|
code: "dirs = ['src/core', 'src/tools']\ndisplay(parallel([lambda d=d: tool.grep({'pattern': 'legacyClient', 'path': d}) for d in dirs]))",
|
|
88
88
|
},
|
|
89
89
|
] as const satisfies readonly EvalPromptExample[];
|
|
@@ -122,7 +122,7 @@ Fields:
|
|
|
122
122
|
|
|
123
123
|
- \`language\` — {{#if py}}\`"py"\` IPython kernel{{/if}}{{#ifAll py js}}, {{/ifAll}}{{#if js}}\`"js"\` persistent JavaScript VM{{/if}}{{#if rb}}{{#ifAny py js}}, {{/ifAny}}\`"rb"\` persistent Ruby kernel{{/if}}{{#if jl}}{{#ifAny py js rb}}, {{/ifAny}}\`"jl"\` persistent Julia kernel{{/if}}.
|
|
124
124
|
- \`code\` — cell body, verbatim. Newlines/quotes JSON-encoded; no fences, no headers.
|
|
125
|
-
- \`
|
|
125
|
+
- \`summary\` (REQUIRED for run) — ONE line in the USER'S conversational language stating WHAT this cell does and FOR WHAT PURPOSE (e.g. Korean conversation -> "src 전체에서 legacyClient 사용처 집계"); shown in the TUI while the cell runs; >80 chars is force-truncated.
|
|
126
126
|
- \`timeout\` (optional) — seconds. Raise only for heavy compute or long{{#if spawns}} non-agent{{/if}} tool calls.
|
|
127
127
|
- \`on_timeout\` (optional) — \`"detach"\` keeps pure computation running in interactive sessions (the default); \`"error"\` interrupts for deadline-sensitive work and is the print/json default.
|
|
128
128
|
- \`reset\` (optional) — wipe this language's kernel first.{{#ifAll py js}} Per-language: a \`py\` reset never touches the JS VM.{{/ifAll}}
|
|
@@ -214,7 +214,7 @@ export function buildEvalPrompt(
|
|
|
214
214
|
};
|
|
215
215
|
const examples = REUSE_CHAIN_EXAMPLES.filter((example) => enabled[example.language])
|
|
216
216
|
.map((example) => {
|
|
217
|
-
const call = { language: example.language,
|
|
217
|
+
const call = { language: example.language, summary: example.summary, code: example.code };
|
|
218
218
|
return `### ${example.caption}\n\`\`\`json\n${JSON.stringify(call, null, 2)}\n\`\`\``;
|
|
219
219
|
})
|
|
220
220
|
.join("\n\n");
|
package/src/tool/cell-runtime.ts
CHANGED
|
@@ -119,7 +119,7 @@ export class CellResultBuilder {
|
|
|
119
119
|
return {
|
|
120
120
|
language: this.#state.input.language,
|
|
121
121
|
languages: [this.#state.input.language],
|
|
122
|
-
...(this.#state.input.
|
|
122
|
+
...(this.#state.input.summary === undefined ? {} : { summary: this.#state.input.summary }),
|
|
123
123
|
durationMs: this.#state.durationMs,
|
|
124
124
|
toolCalls: [...this.#state.toolCalls],
|
|
125
125
|
truncated: output?.truncated ?? false,
|
|
@@ -128,7 +128,7 @@ export class CellResultBuilder {
|
|
|
128
128
|
cells: [
|
|
129
129
|
{
|
|
130
130
|
index: 0,
|
|
131
|
-
...(this.#state.input.
|
|
131
|
+
...(this.#state.input.summary === undefined ? {} : { summary: this.#state.input.summary }),
|
|
132
132
|
code: this.#state.input.code,
|
|
133
133
|
language: this.#state.input.language,
|
|
134
134
|
output: this.#state.output,
|
|
@@ -146,12 +146,12 @@ export class CellResultBuilder {
|
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
#liveUpdateText(): string {
|
|
149
|
-
const
|
|
149
|
+
const summary = this.#state.input.summary === undefined ? "" : ` ${this.#state.input.summary}`;
|
|
150
150
|
const aggregateOutput = this.#output.aggregateText();
|
|
151
151
|
const outputLines = aggregateOutput.split("\n");
|
|
152
152
|
const hasTrailingNewline = aggregateOutput.endsWith("\n");
|
|
153
153
|
if (hasTrailingNewline) outputLines.pop();
|
|
154
154
|
const output = `${outputLines.slice(-8).join("\n")}${hasTrailingNewline ? "\n" : ""}`;
|
|
155
|
-
return `1/1 cells ${this.#state.status}\n[1] ${this.#state.input.language}${
|
|
155
|
+
return `1/1 cells ${this.#state.status}\n[1] ${this.#state.input.language}${summary} ${this.#state.status}${output.length === 0 ? "" : `\n${output}`}`;
|
|
156
156
|
}
|
|
157
157
|
}
|
|
@@ -50,7 +50,7 @@ export interface EvalDetachedCellNotifier {
|
|
|
50
50
|
export interface EvalDetachedCellStatusEntry {
|
|
51
51
|
readonly cellId: string;
|
|
52
52
|
readonly language: EvalLanguage;
|
|
53
|
-
readonly
|
|
53
|
+
readonly summary?: string;
|
|
54
54
|
readonly startedAtMs: number;
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -193,7 +193,7 @@ export class EvalDetachedCellManager {
|
|
|
193
193
|
cellId: cell.cellId,
|
|
194
194
|
language: cell.input.language,
|
|
195
195
|
startedAtMs: cell.startedAtMs,
|
|
196
|
-
...(cell.input.
|
|
196
|
+
...(cell.input.summary === undefined ? {} : { summary: cell.input.summary }),
|
|
197
197
|
})),
|
|
198
198
|
);
|
|
199
199
|
}
|
|
@@ -52,14 +52,14 @@ function fallbackResult(input: EvalToolInput): AgentToolResult<EvalToolDetails>
|
|
|
52
52
|
details: {
|
|
53
53
|
language: input.language,
|
|
54
54
|
languages: [input.language],
|
|
55
|
-
...(input.
|
|
55
|
+
...(input.summary === undefined ? {} : { summary: input.summary }),
|
|
56
56
|
durationMs: 0,
|
|
57
57
|
toolCalls: [],
|
|
58
58
|
truncated: false,
|
|
59
59
|
cells: [
|
|
60
60
|
{
|
|
61
61
|
index: 0,
|
|
62
|
-
...(input.
|
|
62
|
+
...(input.summary === undefined ? {} : { summary: input.summary }),
|
|
63
63
|
code: input.code,
|
|
64
64
|
language: input.language,
|
|
65
65
|
output: "",
|
package/src/tool/eval-request.ts
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import type { ExtensionContext } from "@code-yeongyu/senpi";
|
|
2
|
-
import type
|
|
2
|
+
import { EVAL_SUMMARY_MAX_LENGTH, type EvalControlInput, type EvalToolInput, type EvalToolRequest } from "./types.ts";
|
|
3
3
|
|
|
4
4
|
const NON_INTERACTIVE_MODES = new Set(["print", "json"]);
|
|
5
5
|
|
|
6
|
+
const ELLIPSIS = "...";
|
|
7
|
+
|
|
8
|
+
// Harness-side enforcement of the schema maxLength: the tool advertises the limit, but an
|
|
9
|
+
// over-limit value is force-truncated here (prepareArguments runs before schema validation)
|
|
10
|
+
// instead of failing the call.
|
|
11
|
+
export function clampEvalSummary(value: unknown): string | undefined {
|
|
12
|
+
if (typeof value !== "string") return undefined;
|
|
13
|
+
const normalized = value.trim().replace(/\s+/gu, " ");
|
|
14
|
+
if (normalized.length === 0) return undefined;
|
|
15
|
+
if (normalized.length <= EVAL_SUMMARY_MAX_LENGTH) return normalized;
|
|
16
|
+
return `${normalized.slice(0, EVAL_SUMMARY_MAX_LENGTH - ELLIPSIS.length)}${ELLIPSIS}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
6
19
|
export function parseEvalRequest(params: unknown): EvalToolRequest {
|
|
7
20
|
if (!isRecord(params)) throw new TypeError("eval parameters must be an object");
|
|
8
21
|
if (params.action === "peek" || params.action === "stop") {
|
|
@@ -14,13 +27,18 @@ export function parseEvalRequest(params: unknown): EvalToolRequest {
|
|
|
14
27
|
throw new TypeError(`Unknown eval action "${String(params.action)}"`);
|
|
15
28
|
if (!isEvalLanguage(params.language)) throw new TypeError("eval run requires language");
|
|
16
29
|
if (typeof params.code !== "string") throw new TypeError("eval run requires code");
|
|
30
|
+
const summary = clampEvalSummary(params.summary);
|
|
31
|
+
if (summary === undefined)
|
|
32
|
+
throw new TypeError(
|
|
33
|
+
"eval run requires summary — one line in the user's language: what this cell does and for what purpose",
|
|
34
|
+
);
|
|
17
35
|
if (params.on_timeout !== undefined && params.on_timeout !== "detach" && params.on_timeout !== "error")
|
|
18
36
|
throw new TypeError(`Unknown eval on_timeout value "${String(params.on_timeout)}"`);
|
|
19
37
|
return {
|
|
20
38
|
language: params.language,
|
|
21
39
|
code: params.code,
|
|
40
|
+
summary,
|
|
22
41
|
...(params.action === "run" ? { action: "run" as const } : {}),
|
|
23
|
-
...(typeof params.title === "string" ? { title: params.title } : {}),
|
|
24
42
|
...(typeof params.timeout === "number" ? { timeout: params.timeout } : {}),
|
|
25
43
|
...(params.on_timeout === "detach" || params.on_timeout === "error" ? { on_timeout: params.on_timeout } : {}),
|
|
26
44
|
...(typeof params.reset === "boolean" ? { reset: params.reset } : {}),
|
package/src/tool/eval-tool.ts
CHANGED
|
@@ -8,10 +8,16 @@ import { abortError, CellExecution, defaultTimeoutFactory } from "./cell-executi
|
|
|
8
8
|
import { CellHandler, type CellState } from "./cell-handler.ts";
|
|
9
9
|
import { EvalDetachedCellManager } from "./detached-cell-manager.ts";
|
|
10
10
|
import { detachedKernelBusyError, executeEvalControl, resultAfterDetach } from "./detached-eval-result.ts";
|
|
11
|
-
import { evalTimeoutBehavior, isEvalControlRequest, parseEvalRequest } from "./eval-request.ts";
|
|
11
|
+
import { clampEvalSummary, evalTimeoutBehavior, isEvalControlRequest, parseEvalRequest } from "./eval-request.ts";
|
|
12
12
|
import type { CreateEvalToolOptions, EvalCellInvocation } from "./eval-tool-options.ts";
|
|
13
13
|
import { describeTimeoutState } from "./interrupt-note.ts";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
createEvalInputSchema,
|
|
16
|
+
type EvalInputSchema,
|
|
17
|
+
type EvalToolDetails,
|
|
18
|
+
type EvalToolRequest,
|
|
19
|
+
enabledLanguageList,
|
|
20
|
+
} from "./types.ts";
|
|
15
21
|
|
|
16
22
|
export type { EvalTimeoutFactory } from "./cell-execution.ts";
|
|
17
23
|
export type { CreateEvalToolOptions } from "./eval-tool-options.ts";
|
|
@@ -35,6 +41,15 @@ export function createEvalTool(options: CreateEvalToolOptions): ToolDefinition<E
|
|
|
35
41
|
promptGuidelines: [...prompt.promptGuidelines],
|
|
36
42
|
parameters,
|
|
37
43
|
executionMode: "sequential",
|
|
44
|
+
prepareArguments: (args) => {
|
|
45
|
+
if (typeof args !== "object" || args === null) return args as EvalToolRequest;
|
|
46
|
+
const record = args as Record<string, unknown>;
|
|
47
|
+
if (record.action === "peek" || record.action === "stop") return args as EvalToolRequest;
|
|
48
|
+
const summary = clampEvalSummary(record.summary);
|
|
49
|
+
if (summary === undefined) delete record.summary;
|
|
50
|
+
else record.summary = summary;
|
|
51
|
+
return args as EvalToolRequest;
|
|
52
|
+
},
|
|
38
53
|
...(options.renderers?.renderCall === undefined ? {} : { renderCall: options.renderers.renderCall }),
|
|
39
54
|
...(options.renderers?.renderResult === undefined ? {} : { renderResult: options.renderers.renderResult }),
|
|
40
55
|
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
package/src/tool/render.ts
CHANGED
|
@@ -255,8 +255,7 @@ function renderPrefixed(text: string, environment: RenderEnvironment, prefixStyl
|
|
|
255
255
|
|
|
256
256
|
function cellHeader(cell: EvalCellResult, environment: RenderEnvironment, badges: CellBadges): string {
|
|
257
257
|
const presentation = cellPresentation(cell.status, environment.spinnerFrame);
|
|
258
|
-
|
|
259
|
-
let header = `eval ${cell.language}${title} ${presentation.label} ${presentation.icon}`;
|
|
258
|
+
let header = `eval ${cell.language} ${presentation.label} ${presentation.icon}`;
|
|
260
259
|
if (cell.durationMs !== undefined) header += ` · ${formatDuration(cell.durationMs)}`;
|
|
261
260
|
if (badges.reset) header += " · reset";
|
|
262
261
|
if (badges.timeout !== undefined) header += ` · timeout ${badges.timeout}s`;
|
|
@@ -511,6 +510,16 @@ function renderCell(cell: EvalCellResult, environment: RenderEnvironment, badges
|
|
|
511
510
|
continuation: "│ ",
|
|
512
511
|
color: "borderAccent",
|
|
513
512
|
});
|
|
513
|
+
if (cell.summary !== undefined) {
|
|
514
|
+
appendLines(
|
|
515
|
+
lines,
|
|
516
|
+
renderPrefixed(style(environment.theme, "muted", cell.summary), environment, {
|
|
517
|
+
prefix: "│ ",
|
|
518
|
+
continuation: "│ ",
|
|
519
|
+
color: "muted",
|
|
520
|
+
}),
|
|
521
|
+
);
|
|
522
|
+
}
|
|
514
523
|
const innerWidth = Math.max(1, environment.width - 2);
|
|
515
524
|
const codePreview = previewText(
|
|
516
525
|
highlightedCode(cell.code, cell.language, environment.theme),
|
|
@@ -726,7 +735,6 @@ function resultHeader(
|
|
|
726
735
|
status: "running" | "done" | "error",
|
|
727
736
|
theme: Theme | undefined,
|
|
728
737
|
): string {
|
|
729
|
-
const title = details?.title === undefined ? "" : ` ${details.title}`;
|
|
730
738
|
let color: ThemeColor;
|
|
731
739
|
switch (status) {
|
|
732
740
|
case "running":
|
|
@@ -739,7 +747,7 @@ function resultHeader(
|
|
|
739
747
|
color = "error";
|
|
740
748
|
break;
|
|
741
749
|
}
|
|
742
|
-
return style(theme, color, `eval ${details?.language ?? "?"}
|
|
750
|
+
return style(theme, color, `eval ${details?.language ?? "?"} ${status}`);
|
|
743
751
|
}
|
|
744
752
|
|
|
745
753
|
function resultMetadata(
|
|
@@ -771,11 +779,11 @@ export function renderEvalCall(
|
|
|
771
779
|
return component;
|
|
772
780
|
}
|
|
773
781
|
if (theme === undefined && context.spinnerFrame === undefined) {
|
|
774
|
-
const title = args.title === undefined ? "" : ` ${args.title}`;
|
|
775
782
|
const reset = args.reset === true ? " reset" : "";
|
|
776
783
|
const timeout = args.timeout === undefined ? "" : ` timeout ${args.timeout}s`;
|
|
777
784
|
component.setBlocks([
|
|
778
|
-
{ kind: "text", text: style(theme, "toolTitle", `eval ${args.language}${
|
|
785
|
+
{ kind: "text", text: style(theme, "toolTitle", `eval ${args.language}${reset}${timeout}`) },
|
|
786
|
+
...(args.summary === undefined ? [] : [{ kind: "text" as const, text: style(theme, "muted", args.summary) }]),
|
|
779
787
|
{
|
|
780
788
|
kind: "text",
|
|
781
789
|
text: style(theme, "mdCodeBlock", args.code.trim().length > 0 ? args.code : "..."),
|
|
@@ -799,7 +807,7 @@ export function renderEvalCall(
|
|
|
799
807
|
};
|
|
800
808
|
const cell: EvalCellResult = {
|
|
801
809
|
index: 0,
|
|
802
|
-
...(args.
|
|
810
|
+
...(args.summary === undefined ? {} : { summary: args.summary }),
|
|
803
811
|
code: args.code,
|
|
804
812
|
language: args.language,
|
|
805
813
|
output: "",
|
|
@@ -850,6 +858,9 @@ export function renderEvalResult(
|
|
|
850
858
|
const status = resultStatus(details, options, context.isError);
|
|
851
859
|
const blocks: RenderBlock[] = [
|
|
852
860
|
{ kind: "text", text: resultHeader(details, status, theme) },
|
|
861
|
+
...(details?.summary === undefined
|
|
862
|
+
? []
|
|
863
|
+
: [{ kind: "text" as const, text: style(theme, "muted", details.summary) }]),
|
|
853
864
|
...resultMetadata(details, options, theme),
|
|
854
865
|
{ kind: "blank" },
|
|
855
866
|
];
|
package/src/tool/types.ts
CHANGED
|
@@ -11,11 +11,13 @@ export function enabledLanguageList(enabled: EnabledEvalLanguages): EvalLanguage
|
|
|
11
11
|
return evalLanguageOrder.filter((language) => enabled[language]);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
export const EVAL_SUMMARY_MAX_LENGTH = 80;
|
|
15
|
+
|
|
14
16
|
export interface EvalToolInput {
|
|
15
17
|
readonly language: EvalLanguage;
|
|
16
18
|
readonly code: string;
|
|
17
19
|
readonly action?: "run";
|
|
18
|
-
readonly
|
|
20
|
+
readonly summary: string;
|
|
19
21
|
readonly timeout?: number;
|
|
20
22
|
readonly on_timeout?: "detach" | "error";
|
|
21
23
|
readonly reset?: boolean;
|
|
@@ -38,7 +40,13 @@ const fullEvalInputSchema = Type.Object({
|
|
|
38
40
|
Type.Union([Type.Literal("py"), Type.Literal("js"), Type.Literal("rb"), Type.Literal("jl")]),
|
|
39
41
|
),
|
|
40
42
|
code: Type.Optional(Type.String({ description: "Cell body, verbatim." })),
|
|
41
|
-
|
|
43
|
+
summary: Type.Optional(
|
|
44
|
+
Type.String({
|
|
45
|
+
maxLength: EVAL_SUMMARY_MAX_LENGTH,
|
|
46
|
+
description:
|
|
47
|
+
"REQUIRED for run. ONE line in the USER'S conversational language (Korean conversation -> Korean summary) stating WHAT this cell does and FOR WHAT PURPOSE; shown in the TUI while the cell runs. Longer values are force-truncated to 80 chars.",
|
|
48
|
+
}),
|
|
49
|
+
),
|
|
42
50
|
timeout: Type.Optional(Type.Number({ minimum: 1, description: "Timeout in seconds." })),
|
|
43
51
|
on_timeout: Type.Optional(
|
|
44
52
|
Type.Union([Type.Literal("detach"), Type.Literal("error")], {
|
|
@@ -68,7 +76,13 @@ export function createEvalInputSchema(enabled: EnabledEvalLanguages): EvalInputS
|
|
|
68
76
|
),
|
|
69
77
|
language: Type.Optional(languageSchema),
|
|
70
78
|
code: Type.Optional(Type.String({ description: "Cell body, verbatim." })),
|
|
71
|
-
|
|
79
|
+
summary: Type.Optional(
|
|
80
|
+
Type.String({
|
|
81
|
+
maxLength: EVAL_SUMMARY_MAX_LENGTH,
|
|
82
|
+
description:
|
|
83
|
+
"REQUIRED for run. ONE line in the USER'S conversational language (Korean conversation -> Korean summary) stating WHAT this cell does and FOR WHAT PURPOSE; shown in the TUI while the cell runs. Longer values are force-truncated to 80 chars.",
|
|
84
|
+
}),
|
|
85
|
+
),
|
|
72
86
|
timeout: Type.Optional(Type.Number({ minimum: 1, description: "Timeout in seconds." })),
|
|
73
87
|
on_timeout: Type.Optional(
|
|
74
88
|
Type.Union([Type.Literal("detach"), Type.Literal("error")], {
|
|
@@ -134,7 +148,7 @@ export type EvalDisplayOutput =
|
|
|
134
148
|
|
|
135
149
|
export type EvalCellResult = {
|
|
136
150
|
readonly index: number;
|
|
137
|
-
readonly
|
|
151
|
+
readonly summary?: string;
|
|
138
152
|
readonly code: string;
|
|
139
153
|
readonly language: EvalLanguage;
|
|
140
154
|
readonly output: string;
|
|
@@ -148,7 +162,7 @@ export type EvalCellResult = {
|
|
|
148
162
|
export interface EvalToolDetails {
|
|
149
163
|
readonly language: EvalLanguage;
|
|
150
164
|
readonly languages?: readonly EvalLanguage[];
|
|
151
|
-
readonly
|
|
165
|
+
readonly summary?: string;
|
|
152
166
|
readonly durationMs: number;
|
|
153
167
|
readonly toolCalls: readonly EvalToolCallSummary[];
|
|
154
168
|
readonly truncated: boolean;
|