@pencil-agent/nano-pencil 1.13.5 → 1.13.7
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/dist/build-meta.json +6 -0
- package/dist/core/model-registry.js +2 -1
- package/dist/core/runtime/agent-session.js +29 -4
- package/dist/extensions/defaults/AGENT.md +1 -1
- package/dist/extensions/defaults/CLAUDE.md +4 -4
- package/dist/extensions/defaults/sal/eval/insforge-sink.d.ts +2 -1
- package/dist/extensions/defaults/sal/eval/insforge-sink.js +59 -8
- package/dist/extensions/defaults/sal/eval/types.d.ts +1 -1
- package/dist/extensions/defaults/sal/index.d.ts +38 -4
- package/dist/extensions/defaults/sal/index.js +315 -15
- package/dist/modes/interactive/components/footer.d.ts +4 -3
- package/dist/modes/interactive/components/footer.js +16 -8
- package/dist/modes/interactive/components/provider-selector.d.ts +18 -5
- package/dist/modes/interactive/components/provider-selector.js +128 -21
- package/dist/modes/interactive/interactive-mode.js +33 -15
- package/dist/node_modules/@pencil-agent/ai/cli.js +0 -0
- package/dist/node_modules/@pencil-agent/ai/models.generated.d.ts +633 -120
- package/dist/node_modules/@pencil-agent/ai/models.generated.js +644 -133
- package/package.json +3 -2
- package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/200/273/347/273/223.md" +0 -251
- package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/212/245/345/221/212.md" +0 -123
- package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210.md" +0 -1222
- package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210/345/256/236/347/216/260/346/212/245/345/221/212.md" +0 -158
- package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210/345/257/271/346/257/224/345/210/206/346/236/220.md" +0 -128
- package/docs/loop /351/207/215/346/236/204/350/256/241/345/210/222.md" +0 -321
- package/docs/loop-usage-examples.md +0 -215
- package/docs/planmode.md +0 -1987
|
@@ -207,8 +207,9 @@ export class ModelRegistry {
|
|
|
207
207
|
// Keep built-in models even if custom models failed to load
|
|
208
208
|
}
|
|
209
209
|
const builtInModels = this.useOnlyCustomModels
|
|
210
|
-
? this.loadBuiltInModels(overrides, modelOverrides, new Set(["openrouter"]), {
|
|
210
|
+
? this.loadBuiltInModels(overrides, modelOverrides, new Set(["openrouter", "zai"]), {
|
|
211
211
|
openrouter: new Set(NANOPENCIL_OPENROUTER_BUILTIN_MODEL_IDS),
|
|
212
|
+
// zai not specified = load all zai models
|
|
212
213
|
})
|
|
213
214
|
: this.loadBuiltInModels(overrides, modelOverrides);
|
|
214
215
|
let combined = this.mergeCustomModels(builtInModels, customModels);
|
|
@@ -1183,8 +1183,22 @@ export class AgentSession {
|
|
|
1183
1183
|
this.agent.setModel(model);
|
|
1184
1184
|
this.sessionManager.appendModelChange(model.provider, model.id);
|
|
1185
1185
|
this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
|
|
1186
|
-
//
|
|
1187
|
-
this.
|
|
1186
|
+
// Auto-select thinking level based on model capabilities
|
|
1187
|
+
const currentLevel = this.thinkingLevel;
|
|
1188
|
+
let newLevel;
|
|
1189
|
+
if (!model.reasoning) {
|
|
1190
|
+
// Model doesn't support thinking, force off
|
|
1191
|
+
newLevel = "off";
|
|
1192
|
+
}
|
|
1193
|
+
else if (currentLevel === "off") {
|
|
1194
|
+
// Model supports thinking but current level is off, default to medium
|
|
1195
|
+
newLevel = "medium";
|
|
1196
|
+
}
|
|
1197
|
+
else {
|
|
1198
|
+
// Keep current level but clamp to new model's capabilities
|
|
1199
|
+
newLevel = currentLevel;
|
|
1200
|
+
}
|
|
1201
|
+
this.setThinkingLevel(newLevel);
|
|
1188
1202
|
await this._emitModelSelect(model, previousModel, "set");
|
|
1189
1203
|
}
|
|
1190
1204
|
/**
|
|
@@ -1290,8 +1304,19 @@ export class AgentSession {
|
|
|
1290
1304
|
this.agent.setModel(nextModel);
|
|
1291
1305
|
this.sessionManager.appendModelChange(nextModel.provider, nextModel.id);
|
|
1292
1306
|
this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
|
|
1293
|
-
//
|
|
1294
|
-
this.
|
|
1307
|
+
// Auto-select thinking level based on model capabilities
|
|
1308
|
+
const currentLevel = this.thinkingLevel;
|
|
1309
|
+
let newLevel;
|
|
1310
|
+
if (!nextModel.reasoning) {
|
|
1311
|
+
newLevel = "off";
|
|
1312
|
+
}
|
|
1313
|
+
else if (currentLevel === "off") {
|
|
1314
|
+
newLevel = "medium";
|
|
1315
|
+
}
|
|
1316
|
+
else {
|
|
1317
|
+
newLevel = currentLevel;
|
|
1318
|
+
}
|
|
1319
|
+
this.setThinkingLevel(newLevel);
|
|
1295
1320
|
await this._emitModelSelect(nextModel, currentModel, "cycle");
|
|
1296
1321
|
return {
|
|
1297
1322
|
model: nextModel,
|
|
@@ -44,7 +44,7 @@ sal/weights.ts: SalWeights interface, SAL_DEFAULT_WEIGHTS, loadSalWeights() read
|
|
|
44
44
|
sal/eval/index.ts: createEvalSink() factory + barrel re-exports; adapter selection via options.adapter or endpoint scheme inference (http(s)→insforge, file://|/|./|../→jsonl, missing→noop); ONLY entry point SAL imports from
|
|
45
45
|
sal/eval/types.ts: EvalSink interface, EvalEventEnvelope/EvalEventType (run_start/run_end/turn_anchor), EvalAdapterId ("insforge"|"jsonl"|"noop"), CreateEvalSinkOptions, createEvalEvent factory; zero-dependency type surface
|
|
46
46
|
sal/eval/noop-sink.ts: noopSink — silent EvalSink used when eval disabled or no adapter configured
|
|
47
|
-
sal/eval/insforge-sink.ts: InsForgeEvalSink — PostgREST adapter, routes run_start→eval_runs INSERT (merge-duplicates), turn_anchor→eval_turns + eval_sal_anchors×2, run_end→eval_runs PATCH; allowSelfSigned TLS option, batching with default 2000ms interval
|
|
47
|
+
sal/eval/insforge-sink.ts: InsForgeEvalSink — PostgREST adapter, routes run_start→eval_runs INSERT (merge-duplicates), turn_anchor→eval_turns + eval_sal_anchors×2, tool_trace→eval_tool_traces with PGRST204 legacy-schema fallback, memory_recalls→eval_memory_recalls batch INSERT, run_end→eval_runs PATCH; allowSelfSigned TLS option, batching with default 2000ms interval
|
|
48
48
|
sal/eval/jsonl-sink.ts: JsonlEvalSink — append-only filesystem adapter, one JSON object per line, accepts file:// URLs or plain paths, auto-creates parent dir, batched writes
|
|
49
49
|
sal/README.md: SAL extension usage, sidecar output layout, weights override, pluggability contract
|
|
50
50
|
team/index.ts: AgentTeam extension entry, /team:/team:spawn/:send/:status/:stop/:terminate/:approve/:mode commands, TEAM_MESSAGE_TYPE renderer
|
|
@@ -42,14 +42,14 @@ plan/exit-plan-mode-tool.ts: createExitPlanModeTool() - ExitPlanMode tool with p
|
|
|
42
42
|
plan/plan-agents.ts: Explore/Plan subagent definitions with read-only tools for plan mode workflow
|
|
43
43
|
plan/plan-validation.ts: validatePlan() - validates plan has required sections (Context, Approach, Files, Verification)
|
|
44
44
|
plan/teammate-approval.ts: isInTeammateContext(), submitPlanToLeader(), formatPlanSubmittedMessage() - teammate plan approval integration
|
|
45
|
-
sal/index.ts: SAL extension entry, enabled by default, registers --nosal/--sal-rebuild-terrain flags, /sal:coverage /sal:status /sal:setup commands, before_agent_start/tool_execution_start/agent_end hooks; /sal:setup writes ~/.memory-experiments/credentials.json with adapter inference (insforge/jsonl/noop); publishes structuralAnchor via core/runtime/turn-context (no SAL-specific globals); emits run_start/turn_anchor/memory_recalls/run_end eval events through pluggable EvalSink; reads memoryRecallSnapshot from turn-context bus in agent_end; runtime no-op when --nosal is set
|
|
45
|
+
sal/index.ts: SAL extension entry, enabled by default, registers --nosal/--sal-rebuild-terrain flags, /sal:coverage /sal:status /sal:setup commands, before_agent_start/tool_execution_start/tool_execution_end/agent_end hooks; /sal:setup writes ~/.memory-experiments/credentials.json with adapter inference (insforge/jsonl/noop); publishes structuralAnchor via core/runtime/turn-context (no SAL-specific globals); emits run_start/turn_anchor/memory_recalls/tool_trace/run_end eval events through pluggable EvalSink; reads memoryRecallSnapshot from turn-context bus in agent_end; runtime no-op when --nosal is set; auto-injects pencil_version from build-meta.json into run_start; emergency flush on beforeExit/SIGHUP/SIGTERM; stale run cleanup is opt-in via NANOPENCIL_EVAL_CLEANUP_STALE_RUNS / credentials cleanup_stale_runs; tool_trace is a bounded per-turn summary and includes no-tool turns
|
|
46
46
|
sal/terrain.ts: TerrainSnapshot/TerrainNode/TerrainEdge model, async buildTerrainIndex()/isSnapshotStale() (fs/promises + periodic yields so TUI can flush under block terminals like Warp), checkDipCoverage(), moduleIdForPath(), parses P2 CLAUDE.md and P3 file headers
|
|
47
47
|
sal/anchors.ts: StructuralAnchor/AnchorResolution model, locateTask(), locateAction(), evidence-driven scoring with tunable SalWeights, CJK bigram tokenization
|
|
48
48
|
sal/weights.ts: SalWeights interface, SAL_DEFAULT_WEIGHTS, loadSalWeights() reads sal-config.json from workspace or .memory-experiments/sal/
|
|
49
49
|
sal/eval/index.ts: createEvalSink() factory + barrel re-exports; adapter selection via options.adapter or endpoint scheme inference (http(s)→insforge, file://|/|./|../→jsonl, missing→noop); ONLY entry point SAL imports from
|
|
50
|
-
sal/eval/types.ts: EvalSink interface, EvalEventEnvelope/EvalEventType (run_start/run_end/turn_anchor/memory_recalls), EvalAdapterId ("insforge"|"jsonl"|"noop"), CreateEvalSinkOptions, createEvalEvent factory; zero-dependency type surface
|
|
50
|
+
sal/eval/types.ts: EvalSink interface, EvalEventEnvelope/EvalEventType (run_start/run_end/turn_anchor/memory_recalls/tool_trace), EvalAdapterId ("insforge"|"jsonl"|"noop"), CreateEvalSinkOptions, createEvalEvent factory; zero-dependency type surface
|
|
51
51
|
sal/eval/noop-sink.ts: noopSink — silent EvalSink used when eval disabled or no adapter configured
|
|
52
|
-
sal/eval/insforge-sink.ts: InsForgeEvalSink — PostgREST adapter, routes run_start→eval_runs INSERT (merge-duplicates), turn_anchor→eval_turns + eval_sal_anchors×2, memory_recalls→eval_memory_recalls batch INSERT, run_end→eval_runs PATCH; allowSelfSigned TLS option, batching with default 2000ms interval
|
|
52
|
+
sal/eval/insforge-sink.ts: InsForgeEvalSink — PostgREST adapter, routes run_start→eval_runs INSERT (merge-duplicates), turn_anchor→eval_turns + eval_sal_anchors×2, tool_trace→eval_tool_traces bounded per-turn summaries (including no-tool turns and truncation counters), memory_recalls→eval_memory_recalls batch INSERT, run_end→eval_runs PATCH; allowSelfSigned TLS option, batching with default 2000ms interval
|
|
53
53
|
sal/eval/jsonl-sink.ts: JsonlEvalSink — append-only filesystem adapter, one JSON object per line, accepts file:// URLs or plain paths, auto-creates parent dir, batched writes
|
|
54
54
|
sal/README.md: SAL extension usage, sidecar output layout, weights override, pluggability contract
|
|
55
55
|
team/index.ts: AgentTeam extension entry, /team:/team:spawn/:send/:status/:stop/:terminate/:approve/:mode commands, TEAM_MESSAGE_TYPE renderer
|
|
@@ -64,4 +64,4 @@ team/TESTING.md: Manual & smoke-test guide for Phase B AgentTeam
|
|
|
64
64
|
|
|
65
65
|
Rule: Members complete, one item per line, parent links valid, precise terms first
|
|
66
66
|
|
|
67
|
-
[COVENANT]: Update this file header on changes and verify against parent CLAUDE.md
|
|
67
|
+
[COVENANT]: Update this file header on changes and verify against parent CLAUDE.md
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* [WHO]: Provides InsForgeEvalSink (PostgREST-backed adapter)
|
|
3
3
|
* [FROM]: Depends on node:https, node:http, node:url; ./types.js for EvalSink/EvalEventEnvelope/CreateEvalSinkOptions
|
|
4
4
|
* [TO]: Constructed by eval/index.ts factory when adapter resolves to "insforge"
|
|
5
|
-
* [HERE]: extensions/defaults/sal/eval/insforge-sink.ts - InsForge-specific routing: run_start→eval_runs INSERT (merge-duplicates), turn_anchor→eval_turns + eval_sal_anchors×2, run_end→eval_runs PATCH
|
|
5
|
+
* [HERE]: extensions/defaults/sal/eval/insforge-sink.ts - InsForge-specific routing: run_start→eval_runs INSERT (merge-duplicates, includes pencil_version), turn_anchor→eval_turns + eval_sal_anchors×2, tool_trace→eval_tool_traces with legacy-schema fallback, memory_recalls→eval_memory_recalls, run_end→eval_runs PATCH
|
|
6
6
|
*
|
|
7
7
|
* Pluggable: nothing in this file may be imported from outside the eval/ directory.
|
|
8
8
|
* To add a new backend, write a sibling file with the same EvalSink interface.
|
|
@@ -29,6 +29,7 @@ export declare class InsForgeEvalSink implements EvalSink {
|
|
|
29
29
|
private handleTurnAnchor;
|
|
30
30
|
private handleRunEnd;
|
|
31
31
|
private handleMemoryRecalls;
|
|
32
|
+
private handleToolTrace;
|
|
32
33
|
private postJson;
|
|
33
34
|
private patchJson;
|
|
34
35
|
private httpJson;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* [WHO]: Provides InsForgeEvalSink (PostgREST-backed adapter)
|
|
3
3
|
* [FROM]: Depends on node:https, node:http, node:url; ./types.js for EvalSink/EvalEventEnvelope/CreateEvalSinkOptions
|
|
4
4
|
* [TO]: Constructed by eval/index.ts factory when adapter resolves to "insforge"
|
|
5
|
-
* [HERE]: extensions/defaults/sal/eval/insforge-sink.ts - InsForge-specific routing: run_start→eval_runs INSERT (merge-duplicates), turn_anchor→eval_turns + eval_sal_anchors×2, run_end→eval_runs PATCH
|
|
5
|
+
* [HERE]: extensions/defaults/sal/eval/insforge-sink.ts - InsForge-specific routing: run_start→eval_runs INSERT (merge-duplicates, includes pencil_version), turn_anchor→eval_turns + eval_sal_anchors×2, tool_trace→eval_tool_traces with legacy-schema fallback, memory_recalls→eval_memory_recalls, run_end→eval_runs PATCH
|
|
6
6
|
*
|
|
7
7
|
* Pluggable: nothing in this file may be imported from outside the eval/ directory.
|
|
8
8
|
* To add a new backend, write a sibling file with the same EvalSink interface.
|
|
@@ -103,6 +103,9 @@ export class InsForgeEvalSink {
|
|
|
103
103
|
case "memory_recalls":
|
|
104
104
|
await this.handleMemoryRecalls(event);
|
|
105
105
|
break;
|
|
106
|
+
case "tool_trace":
|
|
107
|
+
await this.handleToolTrace(event);
|
|
108
|
+
break;
|
|
106
109
|
case "run_end":
|
|
107
110
|
await this.handleRunEnd(event);
|
|
108
111
|
break;
|
|
@@ -123,6 +126,7 @@ export class InsForgeEvalSink {
|
|
|
123
126
|
task_file: strOrNull(p.task_file),
|
|
124
127
|
model: strOrNull(p.model),
|
|
125
128
|
thinking: p.thinking === true,
|
|
129
|
+
pencil_version: strOrNull(p.pencil_version),
|
|
126
130
|
commit_hash: strOrNull(p.commit, "unknown"),
|
|
127
131
|
branch_name: strOrNull(p.branch, "unknown"),
|
|
128
132
|
workspace_root: strOrNull(p.workspace_root),
|
|
@@ -213,6 +217,39 @@ export class InsForgeEvalSink {
|
|
|
213
217
|
}));
|
|
214
218
|
await this.postJson(`${this.base}/api/database/records/eval_memory_recalls`, rows, { prefer: "resolution=ignore-duplicates" });
|
|
215
219
|
}
|
|
220
|
+
// INSERT into eval_tool_traces — one row per turn with tool usage summary
|
|
221
|
+
// InsForge columns are all TEXT; JSONB fields must be serialized to strings.
|
|
222
|
+
async handleToolTrace(ev) {
|
|
223
|
+
const p = ev.payload;
|
|
224
|
+
const taskSignals = p.task_signals;
|
|
225
|
+
const row = {
|
|
226
|
+
run_id: ev.run_id,
|
|
227
|
+
turn_id: String(p.turn_id ?? 0),
|
|
228
|
+
event_id: ev.event_id,
|
|
229
|
+
tool_calls: p.tool_calls ? JSON.stringify(p.tool_calls) : null,
|
|
230
|
+
tool_sequence: p.tool_sequence ? JSON.stringify(p.tool_sequence) : null,
|
|
231
|
+
intent: strOrNull(taskSignals?.intent),
|
|
232
|
+
prompt_length: String(taskSignals?.prompt_length ?? 0),
|
|
233
|
+
has_error_trace: String(taskSignals?.has_error_trace === true),
|
|
234
|
+
has_file_reference: String(taskSignals?.has_file_reference === true),
|
|
235
|
+
has_tool_usage: String(p.has_tool_usage === true),
|
|
236
|
+
total_tool_calls: String(p.total_tool_calls ?? 0),
|
|
237
|
+
total_errors: String(p.total_errors ?? 0),
|
|
238
|
+
completed_tool_calls: String(p.completed_tool_calls ?? 0),
|
|
239
|
+
truncated_tool_calls: String(p.truncated_tool_calls ?? 0),
|
|
240
|
+
truncated_tool_summary: String(p.truncated_tool_summary ?? 0),
|
|
241
|
+
duration_ms: String(p.duration_ms ?? 0),
|
|
242
|
+
recorded_at: ev.ts,
|
|
243
|
+
};
|
|
244
|
+
const url = `${this.base}/api/database/records/eval_tool_traces`;
|
|
245
|
+
const result = await this.postJson(url, [row], {
|
|
246
|
+
prefer: "resolution=ignore-duplicates",
|
|
247
|
+
quietErrorCodes: ["PGRST204"],
|
|
248
|
+
});
|
|
249
|
+
if (!result.ok && result.errorCode === "PGRST204") {
|
|
250
|
+
await this.postJson(url, [toLegacyToolTraceRow(row)], { prefer: "resolution=ignore-duplicates" });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
216
253
|
// ------------------------------------------------------------------
|
|
217
254
|
// HTTP helpers
|
|
218
255
|
// ------------------------------------------------------------------
|
|
@@ -220,12 +257,12 @@ export class InsForgeEvalSink {
|
|
|
220
257
|
const extraHeaders = {};
|
|
221
258
|
if (extra?.prefer)
|
|
222
259
|
extraHeaders["Prefer"] = extra.prefer;
|
|
223
|
-
return this.httpJson("POST", url, body, extraHeaders);
|
|
260
|
+
return this.httpJson("POST", url, body, extraHeaders, extra?.quietErrorCodes);
|
|
224
261
|
}
|
|
225
262
|
patchJson(url, body) {
|
|
226
263
|
return this.httpJson("PATCH", url, body, {});
|
|
227
264
|
}
|
|
228
|
-
httpJson(method, url, body, extraHeaders) {
|
|
265
|
+
httpJson(method, url, body, extraHeaders, quietErrorCodes = []) {
|
|
229
266
|
return new Promise((resolve) => {
|
|
230
267
|
const payload = JSON.stringify(body);
|
|
231
268
|
let parsed;
|
|
@@ -234,7 +271,7 @@ export class InsForgeEvalSink {
|
|
|
234
271
|
}
|
|
235
272
|
catch {
|
|
236
273
|
console.error(`[sal][eval] invalid URL: ${url}`);
|
|
237
|
-
resolve(false);
|
|
274
|
+
resolve({ ok: false });
|
|
238
275
|
return;
|
|
239
276
|
}
|
|
240
277
|
const isHttps = parsed.protocol === "https:";
|
|
@@ -258,20 +295,21 @@ export class InsForgeEvalSink {
|
|
|
258
295
|
res.on("data", (chunk) => { rawBody += chunk; });
|
|
259
296
|
res.on("end", () => {
|
|
260
297
|
const ok = res.statusCode !== undefined && res.statusCode < 300;
|
|
261
|
-
|
|
298
|
+
const errorCode = parsePostgrestErrorCode(rawBody);
|
|
299
|
+
if (!ok && !quietErrorCodes.includes(errorCode ?? "")) {
|
|
262
300
|
console.error(`[sal][eval] HTTP ${res.statusCode} ${method} ${parsed.pathname} — ${rawBody.slice(0, 300)}`);
|
|
263
301
|
}
|
|
264
|
-
resolve(ok);
|
|
302
|
+
resolve({ ok, statusCode: res.statusCode, body: rawBody, errorCode });
|
|
265
303
|
});
|
|
266
304
|
});
|
|
267
305
|
req.on("error", (err) => {
|
|
268
306
|
console.error(`[sal][eval] network error → ${parsed.hostname}: ${err.message}`);
|
|
269
|
-
resolve(false);
|
|
307
|
+
resolve({ ok: false });
|
|
270
308
|
});
|
|
271
309
|
req.on("timeout", () => {
|
|
272
310
|
console.error(`[sal][eval] timeout ${method} ${parsed.pathname}`);
|
|
273
311
|
req.destroy();
|
|
274
|
-
resolve(false);
|
|
312
|
+
resolve({ ok: false });
|
|
275
313
|
});
|
|
276
314
|
req.write(payload);
|
|
277
315
|
req.end();
|
|
@@ -292,3 +330,16 @@ function numOrNull(v) {
|
|
|
292
330
|
const n = Number(v);
|
|
293
331
|
return isNaN(n) ? null : n;
|
|
294
332
|
}
|
|
333
|
+
function parsePostgrestErrorCode(rawBody) {
|
|
334
|
+
try {
|
|
335
|
+
const parsed = JSON.parse(rawBody);
|
|
336
|
+
return typeof parsed?.code === "string" ? parsed.code : undefined;
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
function toLegacyToolTraceRow(row) {
|
|
343
|
+
const { has_tool_usage: _hasToolUsage, completed_tool_calls: _completedToolCalls, truncated_tool_calls: _truncatedToolCalls, truncated_tool_summary: _truncatedToolSummary, ...legacyRow } = row;
|
|
344
|
+
return legacyRow;
|
|
345
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* [HERE]: extensions/defaults/sal/eval/types.ts - transport-agnostic event types and the EvalSink contract; concrete adapters live in sibling files
|
|
6
6
|
*/
|
|
7
7
|
export type EvalVariant = "sal" | "control" | "baseline";
|
|
8
|
-
export type EvalEventType = "run_start" | "run_end" | "turn_anchor" | "memory_recalls";
|
|
8
|
+
export type EvalEventType = "run_start" | "run_end" | "turn_anchor" | "memory_recalls" | "tool_trace";
|
|
9
9
|
/** Wire format for eval events. Adapter implementations decide how to materialize. */
|
|
10
10
|
export interface EvalEventEnvelope {
|
|
11
11
|
run_id: string;
|
|
@@ -1,12 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* [WHO]: SAL extension entry - enabled by default, registers --nosal/--sal-rebuild-terrain flags, /sal:coverage /sal:status /sal:setup commands, before_agent_start/tool_execution_start/agent_end hooks; runtime no-op when --nosal is set
|
|
3
|
-
* [FROM]: Depends on core/extensions/types.ts, core/runtime/turn-context.ts (publishes structuralAnchor), extensions/defaults/sal/terrain.ts, anchors.ts, weights.ts, eval/index.ts (pluggable adapters)
|
|
2
|
+
* [WHO]: SAL extension entry - enabled by default, registers --nosal/--sal-rebuild-terrain flags, /sal:coverage /sal:status /sal:setup commands, before_agent_start/tool_execution_start/tool_execution_end/agent_end hooks; runtime no-op when --nosal is set
|
|
3
|
+
* [FROM]: Depends on core/extensions/types.ts (ToolExecutionStartEvent, ToolExecutionEndEvent), core/runtime/turn-context.ts (publishes structuralAnchor), extensions/defaults/sal/terrain.ts, anchors.ts, weights.ts, eval/index.ts (pluggable adapters)
|
|
4
4
|
* [TO]: Loaded by builtin-extensions.ts as a default extension entry point
|
|
5
|
-
* [HERE]: extensions/defaults/sal/index.ts - pluggable Structural Anchor Localization (SAL) extension; emits run_start/turn_anchor/run_end eval events;
|
|
5
|
+
* [HERE]: extensions/defaults/sal/index.ts - pluggable Structural Anchor Localization (SAL) extension; emits run_start/turn_anchor/tool_trace/run_end eval events; tool_trace captures per-turn tool usage profile (call counts, sequences, intent, errors) for self-awareness analytics
|
|
6
6
|
*/
|
|
7
|
+
import { type EvalAdapterId } from "./eval/index.js";
|
|
7
8
|
import type { ExtensionAPI } from "../../../core/extensions/types.js";
|
|
9
|
+
import { type AnchorResolution } from "./anchors.js";
|
|
8
10
|
import { SAL_DEFAULT_WEIGHTS } from "./weights.js";
|
|
11
|
+
interface ToolCallRecord {
|
|
12
|
+
toolCallId: string;
|
|
13
|
+
tool: string;
|
|
14
|
+
startMs: number;
|
|
15
|
+
endMs?: number;
|
|
16
|
+
isError?: boolean;
|
|
17
|
+
}
|
|
18
|
+
interface TurnState {
|
|
19
|
+
turnId: number;
|
|
20
|
+
startedAtMs: number;
|
|
21
|
+
taskResolution?: AnchorResolution;
|
|
22
|
+
touchedFiles: Set<string>;
|
|
23
|
+
toolCalls: ToolCallRecord[];
|
|
24
|
+
prompt?: string;
|
|
25
|
+
}
|
|
26
|
+
interface EvalCredentials {
|
|
27
|
+
insforge_url?: string;
|
|
28
|
+
endpoint?: string;
|
|
29
|
+
api_key?: string;
|
|
30
|
+
anon_key?: string;
|
|
31
|
+
api_key_header?: string;
|
|
32
|
+
headers?: Record<string, string>;
|
|
33
|
+
enabled?: boolean;
|
|
34
|
+
allow_self_signed?: boolean;
|
|
35
|
+
cleanup_stale_runs?: boolean;
|
|
36
|
+
/** Adapter selector. When omitted, inferred from endpoint scheme (http→insforge, file/path→jsonl). */
|
|
37
|
+
adapter?: EvalAdapterId;
|
|
38
|
+
}
|
|
39
|
+
declare function resolveStaleCleanupEnabled(envValue: string | undefined, credentials: EvalCredentials | undefined): boolean;
|
|
9
40
|
declare function normalizeExperimentId(experimentId?: string): string | undefined;
|
|
10
41
|
declare function resolveSalSidecarDir(workspaceRoot: string, experimentId?: string): string;
|
|
42
|
+
type TaskIntent = "fix" | "feat" | "refactor" | "explain" | "explore" | "unknown";
|
|
43
|
+
declare function inferIntent(prompt: string): TaskIntent;
|
|
44
|
+
declare function buildToolTracePayload(turn: TurnState, turnDuration: number): Record<string, unknown>;
|
|
11
45
|
export default function salExtension(api: ExtensionAPI): Promise<void>;
|
|
12
|
-
export { SAL_DEFAULT_WEIGHTS, normalizeExperimentId, resolveSalSidecarDir };
|
|
46
|
+
export { SAL_DEFAULT_WEIGHTS, buildToolTracePayload, inferIntent, normalizeExperimentId, resolveSalSidecarDir, resolveStaleCleanupEnabled, };
|