@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.
Files changed (27) hide show
  1. package/dist/build-meta.json +6 -0
  2. package/dist/core/model-registry.js +2 -1
  3. package/dist/core/runtime/agent-session.js +29 -4
  4. package/dist/extensions/defaults/AGENT.md +1 -1
  5. package/dist/extensions/defaults/CLAUDE.md +4 -4
  6. package/dist/extensions/defaults/sal/eval/insforge-sink.d.ts +2 -1
  7. package/dist/extensions/defaults/sal/eval/insforge-sink.js +59 -8
  8. package/dist/extensions/defaults/sal/eval/types.d.ts +1 -1
  9. package/dist/extensions/defaults/sal/index.d.ts +38 -4
  10. package/dist/extensions/defaults/sal/index.js +315 -15
  11. package/dist/modes/interactive/components/footer.d.ts +4 -3
  12. package/dist/modes/interactive/components/footer.js +16 -8
  13. package/dist/modes/interactive/components/provider-selector.d.ts +18 -5
  14. package/dist/modes/interactive/components/provider-selector.js +128 -21
  15. package/dist/modes/interactive/interactive-mode.js +33 -15
  16. package/dist/node_modules/@pencil-agent/ai/cli.js +0 -0
  17. package/dist/node_modules/@pencil-agent/ai/models.generated.d.ts +633 -120
  18. package/dist/node_modules/@pencil-agent/ai/models.generated.js +644 -133
  19. package/package.json +3 -2
  20. package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/200/273/347/273/223.md" +0 -251
  21. package/docs/loop /351/207/215/346/236/204/345/256/214/346/210/220/346/212/245/345/221/212.md" +0 -123
  22. package/docs/loop /351/207/215/346/236/204/346/226/271/346/241/210.md" +0 -1222
  23. 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
  24. 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
  25. package/docs/loop /351/207/215/346/236/204/350/256/241/345/210/222.md" +0 -321
  26. package/docs/loop-usage-examples.md +0 -215
  27. package/docs/planmode.md +0 -1987
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": "1.13.7",
3
+ "commitHash": "9b46faa",
4
+ "branch": "main",
5
+ "builtAt": "2026-04-25T14:10:25.443Z"
6
+ }
@@ -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
- // Re-clamp thinking level for new model's capabilities
1187
- this.setThinkingLevel(this.thinkingLevel);
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
- // Re-clamp thinking level for new model's capabilities
1294
- this.setThinkingLevel(this.thinkingLevel);
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
- if (!ok) {
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; /sal:setup writes ~/.memory-experiments/credentials.json
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, };