@kernel.chat/kbot 2.26.0 → 3.0.0

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 (97) hide show
  1. package/dist/agent.d.ts +15 -0
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +187 -48
  4. package/dist/agent.js.map +1 -1
  5. package/dist/checkpoint.d.ts +79 -0
  6. package/dist/checkpoint.d.ts.map +1 -0
  7. package/dist/checkpoint.js +220 -0
  8. package/dist/checkpoint.js.map +1 -0
  9. package/dist/cli.js +55 -4
  10. package/dist/cli.js.map +1 -1
  11. package/dist/ide/bridge.d.ts +2 -0
  12. package/dist/ide/bridge.d.ts.map +1 -1
  13. package/dist/ide/bridge.js +1 -0
  14. package/dist/ide/bridge.js.map +1 -1
  15. package/dist/ide/mcp-server.d.ts.map +1 -1
  16. package/dist/ide/mcp-server.js +27 -7
  17. package/dist/ide/mcp-server.js.map +1 -1
  18. package/dist/learned-router.d.ts +9 -7
  19. package/dist/learned-router.d.ts.map +1 -1
  20. package/dist/learned-router.js +36 -10
  21. package/dist/learned-router.js.map +1 -1
  22. package/dist/learning.d.ts +4 -0
  23. package/dist/learning.d.ts.map +1 -1
  24. package/dist/learning.js +17 -0
  25. package/dist/learning.js.map +1 -1
  26. package/dist/mcp-plugins.js +1 -1
  27. package/dist/mcp-plugins.js.map +1 -1
  28. package/dist/multimodal.d.ts +6 -0
  29. package/dist/multimodal.d.ts.map +1 -1
  30. package/dist/multimodal.js +45 -0
  31. package/dist/multimodal.js.map +1 -1
  32. package/dist/sdk.d.ts +165 -0
  33. package/dist/sdk.d.ts.map +1 -0
  34. package/dist/sdk.js +230 -0
  35. package/dist/sdk.js.map +1 -0
  36. package/dist/serve.d.ts.map +1 -1
  37. package/dist/serve.js +38 -0
  38. package/dist/serve.js.map +1 -1
  39. package/dist/skill-rating.d.ts +92 -0
  40. package/dist/skill-rating.d.ts.map +1 -0
  41. package/dist/skill-rating.js +352 -0
  42. package/dist/skill-rating.js.map +1 -0
  43. package/dist/streaming.d.ts +75 -1
  44. package/dist/streaming.d.ts.map +1 -1
  45. package/dist/streaming.js +211 -3
  46. package/dist/streaming.js.map +1 -1
  47. package/dist/task-ledger.d.ts +10 -0
  48. package/dist/task-ledger.d.ts.map +1 -1
  49. package/dist/task-ledger.js +28 -0
  50. package/dist/task-ledger.js.map +1 -1
  51. package/dist/telemetry.d.ts +52 -0
  52. package/dist/telemetry.d.ts.map +1 -0
  53. package/dist/telemetry.js +219 -0
  54. package/dist/telemetry.js.map +1 -0
  55. package/dist/terminal-caps.d.ts +20 -0
  56. package/dist/terminal-caps.d.ts.map +1 -0
  57. package/dist/terminal-caps.js +80 -0
  58. package/dist/terminal-caps.js.map +1 -0
  59. package/dist/tool-pipeline.d.ts +86 -0
  60. package/dist/tool-pipeline.d.ts.map +1 -0
  61. package/dist/tool-pipeline.js +200 -0
  62. package/dist/tool-pipeline.js.map +1 -0
  63. package/dist/tools/containers.js +7 -7
  64. package/dist/tools/containers.js.map +1 -1
  65. package/dist/tools/database.js +6 -6
  66. package/dist/tools/database.js.map +1 -1
  67. package/dist/tools/deploy.js +3 -3
  68. package/dist/tools/deploy.js.map +1 -1
  69. package/dist/tools/e2b-sandbox.js +5 -5
  70. package/dist/tools/e2b-sandbox.js.map +1 -1
  71. package/dist/tools/index.d.ts +45 -1
  72. package/dist/tools/index.d.ts.map +1 -1
  73. package/dist/tools/index.js +168 -108
  74. package/dist/tools/index.js.map +1 -1
  75. package/dist/tools/index.test.js +1 -1
  76. package/dist/tools/index.test.js.map +1 -1
  77. package/dist/tools/research.js +1 -1
  78. package/dist/tools/research.js.map +1 -1
  79. package/dist/tools/training.js +7 -7
  80. package/dist/tools/training.js.map +1 -1
  81. package/dist/tools/vfx.js +2 -2
  82. package/dist/tools/vfx.js.map +1 -1
  83. package/dist/tui.d.ts +1 -0
  84. package/dist/tui.d.ts.map +1 -1
  85. package/dist/tui.js +71 -11
  86. package/dist/tui.js.map +1 -1
  87. package/dist/ui-adapter.d.ts +73 -0
  88. package/dist/ui-adapter.d.ts.map +1 -0
  89. package/dist/ui-adapter.js +139 -0
  90. package/dist/ui-adapter.js.map +1 -0
  91. package/dist/ui.d.ts +3 -0
  92. package/dist/ui.d.ts.map +1 -1
  93. package/dist/ui.js +21 -2
  94. package/dist/ui.js.map +1 -1
  95. package/dist/workflows.js +3 -3
  96. package/dist/workflows.js.map +1 -1
  97. package/package.json +22 -3
package/dist/agent.d.ts CHANGED
@@ -1,5 +1,9 @@
1
+ import { ToolPipeline } from './tool-pipeline.js';
1
2
  import { type ProjectContext } from './context.js';
3
+ import type { UIAdapter } from './ui-adapter.js';
2
4
  import { type ParsedMessage } from './multimodal.js';
5
+ import { ResponseStream } from './streaming.js';
6
+ import { type Checkpoint } from './checkpoint.js';
3
7
  export interface AgentOptions {
4
8
  agent?: string;
5
9
  model?: string;
@@ -14,6 +18,12 @@ export interface AgentOptions {
14
18
  multimodal?: ParsedMessage;
15
19
  /** Skip planner re-entry (prevents infinite loop when planner calls runAgent) */
16
20
  skipPlanner?: boolean;
21
+ /** UIAdapter for decoupled output (SDK use). Defaults to TerminalUIAdapter. */
22
+ ui?: UIAdapter;
23
+ /** Custom tool execution pipeline (overrides default permission/hook/metrics chain) */
24
+ pipeline?: ToolPipeline;
25
+ /** ResponseStream for structured event streaming (SDK/MCP/HTTP consumers) */
26
+ responseStream?: ResponseStream;
17
27
  }
18
28
  export interface AgentResponse {
19
29
  content: string;
@@ -31,4 +41,9 @@ export interface AgentResponse {
31
41
  export declare function runAgent(message: string, options?: AgentOptions): Promise<AgentResponse>;
32
42
  /** One-shot: run agent and print response */
33
43
  export declare function runAndPrint(message: string, options?: AgentOptions): Promise<void>;
44
+ /**
45
+ * Resume an agent session from a checkpoint.
46
+ * Restores conversation messages and state, then continues execution.
47
+ */
48
+ export declare function runAgentFromCheckpoint(checkpoint: Checkpoint, options?: AgentOptions): Promise<AgentResponse>;
34
49
  //# sourceMappingURL=agent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAwBA,OAAO,EAA0B,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAc1E,OAAO,EAA8E,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAkEhI,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,cAAc,CAAA;IACxB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uDAAuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,iDAAiD;IACjD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,sDAAsD;IACtD,UAAU,CAAC,EAAE,aAAa,CAAA;IAC1B,iFAAiF;IACjF,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAGD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAC1E;AAqqBD,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,aAAa,CAAC,CAuhBxB;AAGD,6CAA6C;AAC7C,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAkEf"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAyBA,OAAO,EACL,YAAY,EAGb,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAA0B,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAc1E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAA8E,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAChI,OAAO,EAAiD,cAAc,EAAoB,MAAM,gBAAgB,CAAA;AAShH,OAAO,EAAmC,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAA;AA0DlF,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,cAAc,CAAA;IACxB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uDAAuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,iDAAiD;IACjD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,sDAAsD;IACtD,UAAU,CAAC,EAAE,aAAa,CAAA;IAC1B,iFAAiF;IACjF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,+EAA+E;IAC/E,EAAE,CAAC,EAAE,SAAS,CAAA;IACd,uFAAuF;IACvF,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,6EAA6E;IAC7E,cAAc,CAAC,EAAE,cAAc,CAAA;CAChC;AAGD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAC1E;AAsqBD,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,aAAa,CAAC,CAyoBxB;AAGD,6CAA6C;AAC7C,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAkEf;AAGD;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,aAAa,CAAC,CAgCxB"}
package/dist/agent.js CHANGED
@@ -10,7 +10,8 @@
10
10
  // 6. Verify: always confirm the output exists and is correct
11
11
  // 7. Learn: extract knowledge from every interaction for future use
12
12
  import { getByokKey, getByokProvider, getProviderModel, getProvider, estimateCost, isLocalProvider, warmOllamaModelCache, } from './auth.js';
13
- import { executeTool, getTool, getToolDefinitionsForApi, } from './tools/index.js';
13
+ import { executeTool, getTool, getToolDefinitionsForApi, ensureLazyToolsLoaded, } from './tools/index.js';
14
+ import { createDefaultPipeline, } from './tool-pipeline.js';
14
15
  import { formatContextForPrompt } from './context.js';
15
16
  import { getMatrixSystemPrompt } from './matrix.js';
16
17
  import { buildFullLearningContext, findPattern, recordPattern, cacheSolution, updateProfile, classifyTask, extractKeywords, learnFromExchange, updateProjectMemory, shouldAutoTrain, selfTrain, } from './learning.js';
@@ -19,7 +20,8 @@ import { autoCompact, compressToolResult } from './context-manager.js';
19
20
  import { learnedRoute, recordRoute } from './learned-router.js';
20
21
  import { buildCacheablePrompt, createPromptSections } from './prompt-cache.js';
21
22
  import { saveEmbeddingCache } from './embeddings.js';
22
- import { createSpinner, printToolCall, printToolResult, printResponse, printError, printInfo, printWarn } from './ui.js';
23
+ import { printResponse, printError, printInfo, printWarn } from './ui.js';
24
+ import { TerminalUIAdapter } from './ui-adapter.js';
23
25
  import { parseMultimodalMessage, toAnthropicContent } from './multimodal.js';
24
26
  import { streamAnthropicResponse, streamOpenAIResponse } from './streaming.js';
25
27
  import { checkPermission } from './permissions.js';
@@ -29,6 +31,8 @@ import { recordSuccess, recordFailure } from './provider-fallback.js';
29
31
  import { isSelfEvalEnabled, evaluateResponse } from './self-eval.js';
30
32
  import { EntropyScorer } from './entropy-context.js';
31
33
  import { LoopDetector } from './godel-limits.js';
34
+ import { CheckpointManager, newSessionId } from './checkpoint.js';
35
+ import { TelemetryEmitter } from './telemetry.js';
32
36
  const MAX_TOOL_LOOPS = 75;
33
37
  /** Maximum cumulative cost (USD) before auto-stopping tool loops */
34
38
  const MAX_COST_CEILING = 1.00;
@@ -488,10 +492,10 @@ async function callProviderStreaming(provider, apiKey, model, systemContext, mes
488
492
  const p = getProvider(provider);
489
493
  let state;
490
494
  if (p.apiStyle === 'anthropic') {
491
- state = await streamAnthropicResponse(apiKey, p.apiUrl, model, systemContext, messages.map(m => ({ role: m.role, content: m.content })), tools, { thinking: options?.thinking, thinkingBudget: options?.thinkingBudget });
495
+ state = await streamAnthropicResponse(apiKey, p.apiUrl, model, systemContext, messages.map(m => ({ role: m.role, content: m.content })), tools, { thinking: options?.thinking, thinkingBudget: options?.thinkingBudget, responseStream: options?.responseStream });
492
496
  }
493
497
  else {
494
- state = await streamOpenAIResponse(apiKey, p.apiUrl, model, systemContext, messages.map(m => ({ role: m.role, content: m.content })), tools);
498
+ state = await streamOpenAIResponse(apiKey, p.apiUrl, model, systemContext, messages.map(m => ({ role: m.role, content: m.content })), tools, { responseStream: options?.responseStream });
495
499
  }
496
500
  const result = {
497
501
  content: state.content,
@@ -653,6 +657,8 @@ async function callProvider(provider, apiKey, model, systemContext, messages, to
653
657
  }
654
658
  // ── Main agent loop ──
655
659
  export async function runAgent(message, options = {}) {
660
+ // UIAdapter: defaults to TerminalUIAdapter for CLI, can be overridden for SDK use
661
+ const ui = options.ui ?? new TerminalUIAdapter();
656
662
  const apiKey = getByokKey();
657
663
  const byokProvider = getByokProvider();
658
664
  const isLocal = byokProvider ? isLocalProvider(byokProvider) : false;
@@ -666,7 +672,7 @@ export async function runAgent(message, options = {}) {
666
672
  // Step 0: Parse multimodal content (images in message)
667
673
  const parsed = options.multimodal || parseMultimodalMessage(message);
668
674
  if (parsed.isMultimodal) {
669
- printInfo(`(${parsed.imageCount} image${parsed.imageCount > 1 ? 's' : ''} attached)`);
675
+ ui.onInfo(`(${parsed.imageCount} image${parsed.imageCount > 1 ? 's' : ''} attached)`);
670
676
  }
671
677
  // Step 1: Local-first (skip if multimodal — needs AI to interpret)
672
678
  if (!parsed.isMultimodal) {
@@ -674,13 +680,13 @@ export async function runAgent(message, options = {}) {
674
680
  if (localResult !== null) {
675
681
  addTurn({ role: 'user', content: message });
676
682
  addTurn({ role: 'assistant', content: localResult });
677
- printInfo('(handled locally — 0 tokens used)');
683
+ ui.onInfo('(handled locally — 0 tokens used)');
678
684
  return { content: localResult, agent: 'local', model: 'none', toolCalls: 0 };
679
685
  }
680
686
  }
681
687
  // Step 1.5: Complexity detection — auto-plan complex tasks
682
688
  if (isComplexTask(message) && !message.startsWith('/plan') && !options.skipPlanner) {
683
- printInfo('Complex task detected. Using autonomous planner...');
689
+ ui.onInfo('Complex task detected. Using autonomous planner...');
684
690
  try {
685
691
  const { autonomousExecute, formatPlanSummary } = await import('./planner.js');
686
692
  const plan = await autonomousExecute(message, {
@@ -699,7 +705,7 @@ export async function runAgent(message, options = {}) {
699
705
  }
700
706
  catch {
701
707
  // Planner failed — fall through to regular agent loop
702
- printWarn('Planner failed, falling back to direct execution...');
708
+ ui.onWarning('Planner failed, falling back to direct execution...');
703
709
  }
704
710
  }
705
711
  // Step 1.7: Learned routing — try cached route before defaulting
@@ -710,6 +716,9 @@ export async function runAgent(message, options = {}) {
710
716
  }
711
717
  }
712
718
  const tier = options.tier || 'free';
719
+ // Ensure lazy tools are loaded before building the tool list for the API.
720
+ // In one-shot mode, lazy tools may still be loading in background — await them here.
721
+ await ensureLazyToolsLoaded();
713
722
  const allTools = getToolDefinitionsForApi(tier);
714
723
  const casual = isCasualMessage(message);
715
724
  // Smart tool filtering:
@@ -830,6 +839,15 @@ Always quote file paths that contain spaces. Never reference internal system nam
830
839
  const toolSequenceLog = [];
831
840
  const originalMessage = message;
832
841
  let cumulativeCostUsd = 0;
842
+ // ── Checkpointing & Telemetry ──
843
+ const sessionId = newSessionId();
844
+ const checkpointManager = new CheckpointManager();
845
+ const telemetry = new TelemetryEmitter(sessionId);
846
+ telemetry.emit('session_start', {
847
+ agent: options.agent || 'kernel',
848
+ model: options.model || 'auto',
849
+ message: originalMessage.slice(0, 200),
850
+ });
833
851
  // ── Gödel limits: detect undecidable loops and hand off to human ──
834
852
  const loopDetector = new LoopDetector({
835
853
  maxToolRepeats: 5,
@@ -839,6 +857,23 @@ Always quote file paths that contain spaces. Never reference internal system nam
839
857
  });
840
858
  // ── Entropy scorer: information-theoretic context management ──
841
859
  const entropyScorer = new EntropyScorer();
860
+ // ── Tool execution pipeline ──
861
+ const pipeline = options.pipeline ?? createDefaultPipeline({
862
+ checkPermission,
863
+ runPreHook: (name, args) => {
864
+ const h = runPreToolHook(name, args, options.agent || 'kernel');
865
+ return { blocked: h.blocked ?? false, blockReason: h.blockReason };
866
+ },
867
+ runPostHook: (name, args, result) => { runPostToolHook(name, args, result, options.agent || 'kernel'); },
868
+ executeTool: async (name, args) => {
869
+ const r = await executeTool({ id: name, name, arguments: args });
870
+ return { result: r.result, error: r.error ? r.result : undefined };
871
+ },
872
+ recordMetrics: (name, duration, error) => {
873
+ telemetry.emit('tool_call_end', { tool: name, duration_ms: duration, error });
874
+ },
875
+ emit: (event, data) => telemetry.emit(event, data),
876
+ });
842
877
  // Loop messages track the full conversation within a multi-tool execution.
843
878
  // This includes assistant responses (with tool-use reasoning) and tool results,
844
879
  // so the AI maintains context across tool iterations.
@@ -846,7 +881,7 @@ Always quote file paths that contain spaces. Never reference internal system nam
846
881
  for (let i = 0; i < MAX_TOOL_LOOPS; i++) {
847
882
  // Cost ceiling — stop burning money on runaway loops
848
883
  if (cumulativeCostUsd > MAX_COST_CEILING) {
849
- printWarn(`Cost ceiling reached ($${cumulativeCostUsd.toFixed(2)} > $${MAX_COST_CEILING}). Stopping tool loop.`);
884
+ ui.onWarning(`Cost ceiling reached ($${cumulativeCostUsd.toFixed(2)} > $${MAX_COST_CEILING}). Stopping tool loop.`);
850
885
  break;
851
886
  }
852
887
  // ── Gödel check: detect stuck loops before burning more tokens ──
@@ -857,23 +892,22 @@ Always quote file paths that contain spaces. Never reference internal system nam
857
892
  ? `Loop detected (${decidability.pattern}): ${decidability.evidence}`
858
893
  : decidability.evidence;
859
894
  if (decidability.recommendation === 'handoff') {
860
- printWarn(`${msg}\nHanding off to you — I need your input to continue.`);
895
+ ui.onWarning(`${msg}\nHanding off to you — I need your input to continue.`);
861
896
  break;
862
897
  }
863
898
  else if (decidability.recommendation === 'decompose') {
864
- printWarn(`${msg}\nBreaking this into smaller steps...`);
899
+ ui.onWarning(`${msg}\nBreaking this into smaller steps...`);
865
900
  break;
866
901
  }
867
902
  else if (decidability.recommendation === 'simplify') {
868
- printInfo(`${msg} Trying a different approach...`);
903
+ ui.onInfo(`${msg} Trying a different approach...`);
869
904
  loopMessages.push({ role: 'user', content: `Your approach isn't working. Try a completely different strategy. ${decidability.evidence}` });
870
905
  }
871
906
  }
872
907
  }
873
908
  // Don't use spinner when streaming (conflicts with stdout)
874
909
  const useSpinner = !options.stream;
875
- const spinner = useSpinner ? createSpinner(i === 0 ? 'Thinking...' : `Running tools (${toolCallCount})...`) : null;
876
- spinner?.start();
910
+ const spinnerHandle = useSpinner ? ui.onSpinnerStart(i === 0 ? 'Thinking...' : `Running tools (${toolCallCount})...`) : null;
877
911
  try {
878
912
  // ── BYOK: Call provider directly with tool-use support ──
879
913
  // If user passed an explicit model name (not a speed alias), use it directly
@@ -911,7 +945,7 @@ Always quote file paths that contain spaces. Never reference internal system nam
911
945
  role: t.role,
912
946
  content: t.content,
913
947
  }));
914
- spinner?.stop();
948
+ spinnerHandle?.stop();
915
949
  // Use streaming if requested and provider supports it
916
950
  // Disable streaming for local models when tools are active — local models
917
951
  // often emit tool calls as raw JSON text, which can't be cleaned up after streaming
@@ -920,18 +954,39 @@ Always quote file paths that contain spaces. Never reference internal system nam
920
954
  && p.apiStyle !== 'google'
921
955
  && p.apiStyle !== 'cohere'
922
956
  && !(isLocal && byokTools.length > 0); // Don't stream local + tools (inline tool parsing needs full response)
923
- const result = canStream
924
- ? await callProviderStreaming(provider, apiKey || 'local', model, systemContext, messages, byokTools, {
925
- thinking: options.thinking,
926
- thinkingBudget: options.thinkingBudget,
927
- })
928
- : await callProvider(provider, apiKey || 'local', model, systemContext, messages, byokTools, {
929
- multimodal: i === 0 ? parsed : undefined,
930
- thinking: options.thinking,
931
- thinkingBudget: options.thinkingBudget,
932
- });
957
+ // ── Telemetry: API call ──
958
+ const apiCallStartMs = Date.now();
959
+ telemetry.emit('api_call', { provider, model, iteration: i, streaming: !!canStream });
960
+ let result;
961
+ try {
962
+ result = canStream
963
+ ? await callProviderStreaming(provider, apiKey || 'local', model, systemContext, messages, byokTools, {
964
+ thinking: options.thinking,
965
+ thinkingBudget: options.thinkingBudget,
966
+ responseStream: options.responseStream,
967
+ })
968
+ : await callProvider(provider, apiKey || 'local', model, systemContext, messages, byokTools, {
969
+ multimodal: i === 0 ? parsed : undefined,
970
+ thinking: options.thinking,
971
+ thinkingBudget: options.thinkingBudget,
972
+ });
973
+ }
974
+ catch (apiErr) {
975
+ telemetry.emit('api_error', {
976
+ provider, model, iteration: i,
977
+ error: apiErr instanceof Error ? apiErr.message : String(apiErr),
978
+ }, Date.now() - apiCallStartMs);
979
+ throw apiErr;
980
+ }
933
981
  const iterationCost = estimateCost(provider, result.usage.input_tokens, result.usage.output_tokens);
934
982
  cumulativeCostUsd += iterationCost;
983
+ telemetry.emit('cost_update', {
984
+ iteration: i,
985
+ iterationCost,
986
+ cumulativeCostUsd,
987
+ inputTokens: result.usage.input_tokens,
988
+ outputTokens: result.usage.output_tokens,
989
+ }, Date.now() - apiCallStartMs);
935
990
  // ── Feed Gödel detector with cost/token data ──
936
991
  loopDetector.recordCost(iterationCost);
937
992
  loopDetector.recordTokens(result.usage.input_tokens + result.usage.output_tokens);
@@ -980,7 +1035,7 @@ Always quote file paths that contain spaces. Never reference internal system nam
980
1035
  if (classification && classification.confidence > 0.7) {
981
1036
  const { applyCorrection } = await import('./error-correction.js');
982
1037
  const correctionPrompt = applyCorrection(originalMessage, content, classification.errorType, classification.evidence);
983
- printWarn(`Error correction: ${classification.errorType} (${(classification.confidence * 100).toFixed(0)}%) — retrying with fix...`);
1038
+ ui.onWarning(`Error correction: ${classification.errorType} (${(classification.confidence * 100).toFixed(0)}%) — retrying with fix...`);
984
1039
  loopMessages.push({ role: 'assistant', content });
985
1040
  loopMessages.push({ role: 'user', content: correctionPrompt });
986
1041
  continue;
@@ -993,7 +1048,7 @@ Always quote file paths that contain spaces. Never reference internal system nam
993
1048
  try {
994
1049
  const evalResult = await evaluateResponse(originalMessage, content);
995
1050
  if (evalResult.shouldRetry && evalResult.feedback) {
996
- printWarn(`Self-eval: low score (${evalResult.overall.toFixed(2)}), retrying...`);
1051
+ ui.onWarning(`Self-eval: low score (${evalResult.overall.toFixed(2)}), retrying...`);
997
1052
  loopMessages.push({ role: 'assistant', content });
998
1053
  loopMessages.push({ role: 'user', content: `Your previous response scored low on quality. Feedback: ${evalResult.feedback}\n\nPlease try again with a better response.` });
999
1054
  continue;
@@ -1020,13 +1075,15 @@ Always quote file paths that contain spaces. Never reference internal system nam
1020
1075
  if (content.length > 50 && toolCallCount <= 5) {
1021
1076
  cacheSolution(originalMessage, content.slice(0, 2000));
1022
1077
  }
1023
- // Update user profile
1078
+ // Update user profile + Bayesian skill ratings
1024
1079
  updateProfile({
1025
1080
  tokens: totalTokens,
1026
1081
  tokensSaved: findPattern(originalMessage)?.avgTokensSaved || 0,
1027
1082
  agent: lastResponse.agent || 'kernel',
1028
1083
  taskType: classifyTask(originalMessage),
1029
1084
  techTerms: extractKeywords(originalMessage),
1085
+ message: originalMessage,
1086
+ success: true,
1030
1087
  });
1031
1088
  // Record routing decision for learned router
1032
1089
  const routeMethod = learnedRoute(originalMessage)?.method || 'llm';
@@ -1076,6 +1133,14 @@ Always quote file paths that contain spaces. Never reference internal system nam
1076
1133
  }
1077
1134
  catch { /* learning failures are non-critical */ }
1078
1135
  });
1136
+ // ── Checkpoint & Telemetry: session completed successfully ──
1137
+ checkpointManager.markCompleted(sessionId).catch(() => { });
1138
+ telemetry.emit('session_end', {
1139
+ status: 'completed',
1140
+ toolCallCount,
1141
+ cumulativeCostUsd,
1142
+ });
1143
+ telemetry.destroy().catch(() => { });
1079
1144
  return {
1080
1145
  content,
1081
1146
  agent: lastResponse.agent || 'kernel',
@@ -1091,28 +1156,39 @@ Always quote file paths that contain spaces. Never reference internal system nam
1091
1156
  for (const call of toolCalls) {
1092
1157
  toolCallCount++;
1093
1158
  toolSequenceLog.push(call.name);
1094
- printToolCall(call.name, call.arguments || {});
1095
- // Permission check confirm destructive operations
1096
- const permitted = await checkPermission(call.name, call.arguments || {});
1097
- if (!permitted) {
1098
- results.push({ tool_call_id: call.id, result: 'Denied by user — operation skipped.', error: true });
1099
- printToolResult('Denied by user', true);
1100
- continue;
1101
- }
1102
- // Pre-tool hook
1103
- const preHook = runPreToolHook(call.name, call.arguments || {}, options.agent || 'kernel');
1104
- if (preHook.blocked) {
1105
- results.push({ tool_call_id: call.id, result: `Blocked by hook: ${preHook.blockReason}`, error: true });
1106
- printToolResult(`Blocked by hook: ${preHook.blockReason}`, true);
1107
- continue;
1108
- }
1109
- const result = await executeTool(call);
1159
+ ui.onToolCallStart(call.name, call.arguments || {});
1160
+ // Execute through the middleware pipeline
1161
+ const ctx = {
1162
+ toolName: call.name,
1163
+ toolArgs: call.arguments || {},
1164
+ toolCallId: call.id,
1165
+ metadata: {},
1166
+ aborted: false,
1167
+ };
1168
+ await pipeline.execute(ctx);
1169
+ // Build ToolResult from pipeline context
1170
+ const result = {
1171
+ tool_call_id: call.id,
1172
+ result: ctx.error
1173
+ ? (ctx.aborted ? (ctx.abortReason || ctx.error) : `Tool error: ${ctx.error}`)
1174
+ : (ctx.result || ''),
1175
+ error: !!ctx.error || ctx.aborted,
1176
+ duration_ms: ctx.durationMs,
1177
+ };
1110
1178
  results.push(result);
1111
- printToolResult(result.result, result.error);
1179
+ ui.onToolCallEnd(call.name, result.result, result.error ? result.result : undefined, result.duration_ms);
1180
+ // ── Structured stream: tool result event ──
1181
+ if (options.responseStream) {
1182
+ options.responseStream.emit({
1183
+ type: 'tool_result',
1184
+ id: call.id,
1185
+ name: call.name,
1186
+ result: result.result,
1187
+ error: result.error ? result.result : undefined,
1188
+ });
1189
+ }
1112
1190
  // ── Feed Gödel detector with tool call data ──
1113
1191
  loopDetector.recordToolCall(call.name, JSON.stringify(call.arguments || {}), result.result.slice(0, 500));
1114
- // Post-tool hook
1115
- runPostToolHook(call.name, call.arguments || {}, result.result, options.agent || 'kernel');
1116
1192
  }
1117
1193
  // ── Maintain conversation context across tool iterations ──
1118
1194
  // 1. Include the assistant's response (its reasoning + which tools it chose)
@@ -1127,12 +1203,43 @@ Always quote file paths that contain spaces. Never reference internal system nam
1127
1203
  return `${r.tool_call_id} (${toolCalls.find(tc => tc.id === r.tool_call_id)?.name || 'unknown'}): ${status}${compressed}`;
1128
1204
  }).join('\n\n');
1129
1205
  loopMessages.push({ role: 'user', content: `Tool results:\n${toolResultSummary}` });
1206
+ // ── Checkpoint: save agent state after tool execution ──
1207
+ checkpointManager.save({
1208
+ id: crypto.randomUUID(),
1209
+ sessionId,
1210
+ timestamp: Date.now(),
1211
+ iteration: i,
1212
+ messages: [...loopMessages],
1213
+ toolSequenceLog: [...toolSequenceLog],
1214
+ toolCallCount,
1215
+ cumulativeCostUsd,
1216
+ agentId: options.agent || 'kernel',
1217
+ model: lastResponse?.model || 'unknown',
1218
+ systemPrompt: systemContext,
1219
+ status: 'in_progress',
1220
+ }).then(() => {
1221
+ telemetry.emit('checkpoint_save', { iteration: i, toolCallCount });
1222
+ }).catch(() => {
1223
+ // Checkpoint save is non-blocking and non-critical
1224
+ });
1130
1225
  }
1131
1226
  catch (err) {
1132
- spinner?.stop();
1227
+ spinnerHandle?.stop();
1228
+ // ── Telemetry: session failure ──
1229
+ telemetry.emit('session_end', { status: 'failed', error: String(err), toolCallCount });
1230
+ telemetry.destroy().catch(() => { });
1133
1231
  throw err;
1134
1232
  }
1135
1233
  }
1234
+ // ── Checkpoint & Telemetry: session ended (loop exhausted or early break) ──
1235
+ checkpointManager.markCompleted(sessionId).catch(() => { });
1236
+ telemetry.emit('session_end', {
1237
+ status: 'completed',
1238
+ toolCallCount,
1239
+ cumulativeCostUsd,
1240
+ reason: 'loop_exhausted',
1241
+ });
1242
+ telemetry.destroy().catch(() => { });
1136
1243
  const content = lastResponse?.content || 'Reached maximum tool iterations.';
1137
1244
  return {
1138
1245
  content,
@@ -1205,4 +1312,36 @@ export async function runAndPrint(message, options = {}) {
1205
1312
  process.exit(1);
1206
1313
  }
1207
1314
  }
1315
+ /**
1316
+ * Resume an agent session from a checkpoint.
1317
+ * Restores conversation messages and state, then continues execution.
1318
+ */
1319
+ export async function runAgentFromCheckpoint(checkpoint, options = {}) {
1320
+ const telemetryInstance = new TelemetryEmitter(checkpoint.sessionId);
1321
+ telemetryInstance.emit('checkpoint_resume', {
1322
+ checkpointId: checkpoint.id,
1323
+ iteration: checkpoint.iteration,
1324
+ toolCallCount: checkpoint.toolCallCount,
1325
+ cumulativeCostUsd: checkpoint.cumulativeCostUsd,
1326
+ });
1327
+ printInfo(`Resuming from checkpoint (iteration ${checkpoint.iteration}, ${checkpoint.toolCallCount} tool calls, $${checkpoint.cumulativeCostUsd.toFixed(4)})`);
1328
+ // Restore conversation history from checkpoint messages
1329
+ const { restoreHistory } = await import('./memory.js');
1330
+ const turns = checkpoint.messages.map((m) => ({
1331
+ role: m.role,
1332
+ content: m.content,
1333
+ }));
1334
+ restoreHistory(turns);
1335
+ // Extract the original user message from the first user message in checkpoint
1336
+ const firstUserMsg = checkpoint.messages.find((m) => m.role === 'user');
1337
+ const message = firstUserMsg?.content || 'continue';
1338
+ // Re-run the agent with the restored context
1339
+ const response = await runAgent(message, {
1340
+ ...options,
1341
+ agent: checkpoint.agentId || options.agent,
1342
+ skipPlanner: true, // Don't re-plan, just continue execution
1343
+ });
1344
+ await telemetryInstance.destroy();
1345
+ return response;
1346
+ }
1208
1347
  //# sourceMappingURL=agent.js.map