@kernel.chat/kbot 3.9.0 → 3.11.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 (49) hide show
  1. package/dist/a2a-client.d.ts +162 -0
  2. package/dist/a2a-client.d.ts.map +1 -0
  3. package/dist/a2a-client.js +376 -0
  4. package/dist/a2a-client.js.map +1 -0
  5. package/dist/agent.d.ts.map +1 -1
  6. package/dist/agent.js +96 -5
  7. package/dist/agent.js.map +1 -1
  8. package/dist/cli.js +295 -3
  9. package/dist/cli.js.map +1 -1
  10. package/dist/mcp-apps.d.ts +90 -0
  11. package/dist/mcp-apps.d.ts.map +1 -0
  12. package/dist/mcp-apps.js +497 -0
  13. package/dist/mcp-apps.js.map +1 -0
  14. package/dist/memory-synthesis.d.ts +67 -0
  15. package/dist/memory-synthesis.d.ts.map +1 -0
  16. package/dist/memory-synthesis.js +557 -0
  17. package/dist/memory-synthesis.js.map +1 -0
  18. package/dist/prompt-evolution.d.ts +92 -0
  19. package/dist/prompt-evolution.d.ts.map +1 -0
  20. package/dist/prompt-evolution.js +371 -0
  21. package/dist/prompt-evolution.js.map +1 -0
  22. package/dist/reflection.d.ts +33 -0
  23. package/dist/reflection.d.ts.map +1 -0
  24. package/dist/reflection.js +368 -0
  25. package/dist/reflection.js.map +1 -0
  26. package/dist/serve.d.ts.map +1 -1
  27. package/dist/serve.js +56 -6
  28. package/dist/serve.js.map +1 -1
  29. package/dist/skill-library.d.ts +60 -0
  30. package/dist/skill-library.d.ts.map +1 -0
  31. package/dist/skill-library.js +475 -0
  32. package/dist/skill-library.js.map +1 -0
  33. package/dist/spec.d.ts +23 -0
  34. package/dist/spec.d.ts.map +1 -0
  35. package/dist/spec.js +177 -0
  36. package/dist/spec.js.map +1 -0
  37. package/dist/tool-pipeline.d.ts +7 -0
  38. package/dist/tool-pipeline.d.ts.map +1 -1
  39. package/dist/tool-pipeline.js +32 -0
  40. package/dist/tool-pipeline.js.map +1 -1
  41. package/dist/tools/index.d.ts +1 -1
  42. package/dist/tools/index.d.ts.map +1 -1
  43. package/dist/tools/index.js +2 -1
  44. package/dist/tools/index.js.map +1 -1
  45. package/dist/tree-planner.d.ts +63 -0
  46. package/dist/tree-planner.d.ts.map +1 -0
  47. package/dist/tree-planner.js +818 -0
  48. package/dist/tree-planner.js.map +1 -0
  49. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -20,6 +20,7 @@ import { clearHistory, clearMemory, compactHistory, restoreHistory } from './mem
20
20
  import { saveSession, loadSession, listSessions, deleteSession, formatSessionList, } from './sessions.js';
21
21
  import { createAgent, removeAgent, getAgent, formatAgentList, formatAgentDetail, PRESETS, getMatrixAgentIds, activateMimic, listMimicProfiles, getMimicProfile, registerBuiltinAgents, formatBuiltinAgentList, formatBuiltinAgentDetail, } from './matrix.js';
22
22
  import { getExtendedStats, incrementSessions, learnFact, selfTrain, getTrainingLog, flushPendingWrites } from './learning.js';
23
+ import { maybeSynthesize, getSynthesisStats } from './memory-synthesis.js';
23
24
  import { banner, bannerCompact, bannerAuth, prompt as kbotPrompt, printError, printSuccess, printInfo, printWarn, printResponse, printHelp, printGoodbye, setQuiet, } from './ui.js';
24
25
  import { checkForUpdate, selfUpdate } from './updater.js';
25
26
  import { runTutorial } from './tutorial.js';
@@ -48,6 +49,7 @@ async function main() {
48
49
  .option('--self-eval', 'Enable self-evaluation loop (score and retry low-quality responses)')
49
50
  .option('--plan', 'Plan mode — read-only exploration, no changes')
50
51
  .option('--architect', 'Architect mode — plan-review-implement with dual agents')
52
+ .option('--tree', 'Tree planning mode — LATS branching search instead of linear plans')
51
53
  .option('--safe', 'Confirm destructive operations')
52
54
  .option('--strict', 'Confirm ALL operations')
53
55
  .argument('[prompt...]', 'One-shot prompt')
@@ -1024,6 +1026,250 @@ async function main() {
1024
1026
  computerUse: opts.computerUse,
1025
1027
  });
1026
1028
  });
1029
+ program
1030
+ .command('apps [tool]')
1031
+ .description('MCP Apps — list app-capable tools, or run one and render its HTML output')
1032
+ .option('--render', 'Run the tool and render its MCP App output in the browser')
1033
+ .option('--inline', 'Return inline HTML instead of opening browser')
1034
+ .option('--args <json>', 'JSON arguments for the tool (use with --render)')
1035
+ .action(async (tool, opts) => {
1036
+ const { registerMcpAppTools, listAppCapableTools, getAppConfig, renderMcpApp, extractMcpAppFromText } = await import('./mcp-apps.js');
1037
+ const { ensureLazyToolsLoaded, executeTool: execTool } = await import('./tools/index.js');
1038
+ // Ensure tools are loaded (including MCP App tools)
1039
+ await ensureLazyToolsLoaded();
1040
+ if (!tool) {
1041
+ // List all app-capable tools
1042
+ const appTools = listAppCapableTools();
1043
+ if (appTools.length === 0) {
1044
+ printInfo('No MCP App-capable tools registered.');
1045
+ return;
1046
+ }
1047
+ process.stderr.write(`\n ${chalk.hex('#6B5B95')('MCP App Tools')} (${appTools.length}):\n\n`);
1048
+ for (const t of appTools) {
1049
+ process.stderr.write(` ${chalk.bold(t.name)} — ${t.description}\n`);
1050
+ }
1051
+ process.stderr.write(`\n ${chalk.dim('Run a tool:')} kbot apps --render <tool> --args \'{"key":"value"}\'\n`);
1052
+ process.stderr.write(` ${chalk.dim('Config:')} ~/.kbot/config.json → mcpApps.renderMode (browser|inline|disabled)\n\n`);
1053
+ return;
1054
+ }
1055
+ if (opts?.render) {
1056
+ // Parse args
1057
+ let toolArgs = {};
1058
+ if (opts.args) {
1059
+ try {
1060
+ toolArgs = JSON.parse(opts.args);
1061
+ }
1062
+ catch {
1063
+ printError('Invalid JSON for --args. Example: --args \'{"type":"bar","labels":["A","B"],"datasets":[{"data":[1,2]}]}\'');
1064
+ return;
1065
+ }
1066
+ }
1067
+ // Execute the tool
1068
+ printInfo(`Running ${tool}...`);
1069
+ const result = await execTool({
1070
+ id: `apps_${Date.now()}`,
1071
+ name: tool,
1072
+ arguments: toolArgs,
1073
+ });
1074
+ if (result.error) {
1075
+ printError(`Tool error: ${result.result}`);
1076
+ return;
1077
+ }
1078
+ // Try to extract MCP App from result
1079
+ const appResult = extractMcpAppFromText(result.result);
1080
+ if (!appResult) {
1081
+ printWarn(`Tool "${tool}" did not return MCP App content. Text output:`);
1082
+ console.log(result.result);
1083
+ return;
1084
+ }
1085
+ // Render the app
1086
+ const config = getAppConfig();
1087
+ if (opts.inline) {
1088
+ config.renderMode = 'inline';
1089
+ }
1090
+ const rendered = await renderMcpApp(appResult, config);
1091
+ printSuccess(`MCP App: ${appResult.title ?? tool}`);
1092
+ if (rendered.path) {
1093
+ printInfo(`Opened in browser: ${rendered.path}`);
1094
+ }
1095
+ if (rendered.rendered && opts.inline) {
1096
+ console.log(rendered.rendered);
1097
+ }
1098
+ console.log(rendered.text);
1099
+ }
1100
+ else {
1101
+ // Show info about the specific tool
1102
+ const appTools = listAppCapableTools();
1103
+ const match = appTools.find(t => t.name === tool);
1104
+ if (match) {
1105
+ printInfo(`${match.name} — ${match.description}`);
1106
+ printInfo(`\nRun with: kbot apps --render ${match.name} --args '{...}'`);
1107
+ }
1108
+ else {
1109
+ printError(`Tool "${tool}" is not registered as MCP App-capable.`);
1110
+ printInfo('Run `kbot apps` to see available tools.');
1111
+ }
1112
+ }
1113
+ });
1114
+ // ── A2A (Agent-to-Agent) subcommands ──
1115
+ const a2aCmd = program
1116
+ .command('a2a')
1117
+ .description('Agent-to-Agent protocol — discover agents, send tasks, collaborate');
1118
+ a2aCmd
1119
+ .command('card')
1120
+ .description('Print this kbot instance\'s Agent Card JSON')
1121
+ .option('--url <url>', 'Override the endpoint URL in the card')
1122
+ .action(async (opts) => {
1123
+ const { generateAgentCard } = await import('./a2a-client.js');
1124
+ const card = generateAgentCard(opts.url);
1125
+ console.log(JSON.stringify(card, null, 2));
1126
+ });
1127
+ a2aCmd
1128
+ .command('discover <url>')
1129
+ .description('Discover a remote A2A agent at the given URL')
1130
+ .action(async (url) => {
1131
+ const { discoverAgent } = await import('./a2a-client.js');
1132
+ printInfo(`Discovering agent at ${url}...`);
1133
+ const card = await discoverAgent(url);
1134
+ if (!card) {
1135
+ printError(`No A2A agent found at ${url}`);
1136
+ printInfo('Make sure the agent serves /.well-known/agent.json');
1137
+ process.exit(1);
1138
+ }
1139
+ printSuccess(`Discovered: ${card.name} (v${card.version})`);
1140
+ printInfo(` ${card.description}`);
1141
+ printInfo(` Skills: ${card.skills.length}`);
1142
+ for (const skill of card.skills) {
1143
+ printInfo(` • ${skill.name} — ${skill.description}`);
1144
+ }
1145
+ printInfo(` Streaming: ${card.capabilities.streaming}`);
1146
+ printInfo(` Agent saved to local registry.`);
1147
+ });
1148
+ a2aCmd
1149
+ .command('send <url> <prompt...>')
1150
+ .description('Send a task to a remote A2A agent')
1151
+ .option('--agent <agent>', 'Hint which specialist should handle the task')
1152
+ .option('--token <token>', 'Auth token for the remote agent')
1153
+ .option('--async', 'Submit asynchronously (don\'t wait for completion)')
1154
+ .action(async (url, promptParts, opts) => {
1155
+ const { sendTask } = await import('./a2a-client.js');
1156
+ const prompt = promptParts.join(' ');
1157
+ const sync = !opts.async;
1158
+ printInfo(`Sending task to ${url}${sync ? ' (waiting for result)' : ' (async)'}...`);
1159
+ try {
1160
+ const result = await sendTask(url, prompt, {
1161
+ agent: opts.agent,
1162
+ token: opts.token,
1163
+ sync,
1164
+ });
1165
+ if (result.status === 'completed' && result.text) {
1166
+ printSuccess(`Task ${result.id} completed`);
1167
+ console.log();
1168
+ console.log(result.text);
1169
+ }
1170
+ else if (result.status === 'submitted') {
1171
+ printInfo(`Task submitted: ${result.id}`);
1172
+ printInfo(`Check status: kbot a2a status ${url} ${result.id}`);
1173
+ }
1174
+ else {
1175
+ printWarn(`Task ${result.id} — status: ${result.status}`);
1176
+ if (result.text)
1177
+ console.log(result.text);
1178
+ }
1179
+ }
1180
+ catch (err) {
1181
+ printError(`Task failed: ${err instanceof Error ? err.message : String(err)}`);
1182
+ process.exit(1);
1183
+ }
1184
+ });
1185
+ a2aCmd
1186
+ .command('status <url> <taskId>')
1187
+ .description('Check the status of a task on a remote agent')
1188
+ .option('--token <token>', 'Auth token for the remote agent')
1189
+ .action(async (url, taskId, opts) => {
1190
+ const { getTaskStatus } = await import('./a2a-client.js');
1191
+ try {
1192
+ const result = await getTaskStatus(url, taskId, { token: opts.token });
1193
+ printInfo(`Task ${result.id}: ${result.status}`);
1194
+ if (result.message)
1195
+ printInfo(` Message: ${result.message}`);
1196
+ if (result.text) {
1197
+ console.log();
1198
+ console.log(result.text);
1199
+ }
1200
+ }
1201
+ catch (err) {
1202
+ printError(`Status check failed: ${err instanceof Error ? err.message : String(err)}`);
1203
+ process.exit(1);
1204
+ }
1205
+ });
1206
+ a2aCmd
1207
+ .command('cancel <url> <taskId>')
1208
+ .description('Cancel a running task on a remote agent')
1209
+ .option('--token <token>', 'Auth token for the remote agent')
1210
+ .action(async (url, taskId, opts) => {
1211
+ const { cancelTask } = await import('./a2a-client.js');
1212
+ try {
1213
+ const canceled = await cancelTask(url, taskId, { token: opts.token });
1214
+ if (canceled) {
1215
+ printSuccess(`Task ${taskId} canceled.`);
1216
+ }
1217
+ else {
1218
+ printWarn(`Task ${taskId} is already in a terminal state (completed or failed).`);
1219
+ }
1220
+ }
1221
+ catch (err) {
1222
+ printError(`Cancel failed: ${err instanceof Error ? err.message : String(err)}`);
1223
+ process.exit(1);
1224
+ }
1225
+ });
1226
+ a2aCmd
1227
+ .command('agents')
1228
+ .description('List all discovered remote agents in the local registry')
1229
+ .action(async () => {
1230
+ const { listRemoteAgents, removeRemoteAgent } = await import('./a2a-client.js');
1231
+ const agents = listRemoteAgents();
1232
+ if (agents.length === 0) {
1233
+ printInfo('No remote agents discovered yet.');
1234
+ printInfo('Discover one: kbot a2a discover <url>');
1235
+ return;
1236
+ }
1237
+ printInfo(`${agents.length} discovered agent${agents.length === 1 ? '' : 's'}:`);
1238
+ for (const agent of agents) {
1239
+ printInfo(` • ${agent.card.name} (v${agent.card.version})`);
1240
+ printInfo(` URL: ${agent.url}`);
1241
+ printInfo(` Skills: ${agent.card.skills.length}`);
1242
+ printInfo(` Discovered: ${agent.discoveredAt}`);
1243
+ if (agent.lastContactedAt) {
1244
+ printInfo(` Last contact: ${agent.lastContactedAt}`);
1245
+ }
1246
+ }
1247
+ });
1248
+ a2aCmd
1249
+ .command('history')
1250
+ .description('Show local task history (tasks sent to remote agents)')
1251
+ .option('--clear', 'Clear the task history')
1252
+ .action(async (opts) => {
1253
+ const { getTaskHistory, clearTaskHistory } = await import('./a2a-client.js');
1254
+ if (opts.clear) {
1255
+ clearTaskHistory();
1256
+ printSuccess('Task history cleared.');
1257
+ return;
1258
+ }
1259
+ const history = getTaskHistory();
1260
+ if (history.length === 0) {
1261
+ printInfo('No task history yet.');
1262
+ return;
1263
+ }
1264
+ printInfo(`${history.length} task${history.length === 1 ? '' : 's'} in history:`);
1265
+ for (const entry of history.slice(-20).reverse()) {
1266
+ const status = entry.status === 'completed' ? chalk.green(entry.status)
1267
+ : entry.status === 'failed' ? chalk.red(entry.status)
1268
+ : chalk.yellow(entry.status);
1269
+ printInfo(` ${entry.id.slice(0, 8)} ${status} ${entry.agentUrl}`);
1270
+ printInfo(` ${chalk.dim(entry.prompt.slice(0, 80))}`);
1271
+ }
1272
+ });
1027
1273
  program
1028
1274
  .command('kbot-local')
1029
1275
  .description('Use kbot local gateway as AI provider')
@@ -1406,6 +1652,33 @@ async function main() {
1406
1652
  await new Promise(() => { });
1407
1653
  }
1408
1654
  });
1655
+ program
1656
+ .command('spec <description>')
1657
+ .description('Generate a formal specification with requirements + acceptance criteria before coding')
1658
+ .option('--implement', 'Generate spec then pass to coder agent for implementation')
1659
+ .option('--agent <name>', 'Override the default architect agent', 'architect')
1660
+ .option('--output <path>', 'Custom output path for the spec file')
1661
+ .action(async (description, specOpts) => {
1662
+ const { generateSpec } = await import('./spec.js');
1663
+ const parentOpts = program.opts();
1664
+ try {
1665
+ await generateSpec(description, {
1666
+ agent: specOpts.agent,
1667
+ output: specOpts.output,
1668
+ implement: specOpts.implement,
1669
+ agentOpts: {
1670
+ model: parentOpts.model,
1671
+ stream: parentOpts.stream ?? true,
1672
+ thinking: parentOpts.thinking || false,
1673
+ thinkingBudget: parentOpts.thinkingBudget ? (parseInt(parentOpts.thinkingBudget, 10) || 10000) : undefined,
1674
+ },
1675
+ });
1676
+ }
1677
+ catch (err) {
1678
+ printError(err instanceof Error ? err.message : String(err));
1679
+ process.exit(1);
1680
+ }
1681
+ });
1409
1682
  program
1410
1683
  .command('completions')
1411
1684
  .description('Generate shell tab-completion script (bash, zsh, fish)')
@@ -1426,7 +1699,7 @@ async function main() {
1426
1699
  if (opts.quiet)
1427
1700
  setQuiet(true);
1428
1701
  // If a sub-command was run, we're done
1429
- if (['byok', 'auth', 'ide', 'local', 'ollama', 'kbot-local', 'pull', 'doctor', 'serve', 'agents', 'watch', 'voice', 'export', 'plugins', 'changelog', 'completions', 'automate', 'status'].includes(program.args[0]))
1702
+ if (['byok', 'auth', 'ide', 'local', 'ollama', 'kbot-local', 'pull', 'doctor', 'serve', 'agents', 'watch', 'voice', 'export', 'plugins', 'changelog', 'completions', 'automate', 'status', 'spec', 'a2a'].includes(program.args[0]))
1430
1703
  return;
1431
1704
  // Check for API key (BYOK or local provider)
1432
1705
  let byokActive = isByokEnabled();
@@ -1599,7 +1872,7 @@ async function main() {
1599
1872
  printInfo(syncMsg);
1600
1873
  // Determine if we're in one-shot or REPL mode
1601
1874
  const isOneShot = promptArgs.length > 0 &&
1602
- !['byok', 'auth', 'ide', 'ollama', 'kbot-local', 'pull', 'doctor'].includes(promptArgs[0]);
1875
+ !['byok', 'auth', 'ide', 'ollama', 'kbot-local', 'pull', 'doctor', 'spec'].includes(promptArgs[0]);
1603
1876
  const isStdinOnly = !process.stdin.isTTY && promptArgs.length === 0;
1604
1877
  if (isOneShot || isStdinOnly || opts.pipe) {
1605
1878
  // One-shot / pipe mode: start lazy tools in background (non-blocking).
@@ -1740,7 +2013,7 @@ async function main() {
1740
2013
  stdinContent = Buffer.concat(chunks).toString('utf-8').trim();
1741
2014
  }
1742
2015
  // One-shot mode: kbot "fix the bug" — always stream for speed
1743
- if (promptArgs.length > 0 && !['byok', 'auth', 'ide', 'ollama', 'kbot-local', 'pull', 'doctor'].includes(promptArgs[0])) {
2016
+ if (promptArgs.length > 0 && !['byok', 'auth', 'ide', 'ollama', 'kbot-local', 'pull', 'doctor', 'spec'].includes(promptArgs[0])) {
1744
2017
  if (!opts.pipe)
1745
2018
  console.log(bannerCompact());
1746
2019
  let message = promptArgs.join(' ');
@@ -1767,6 +2040,12 @@ async function main() {
1767
2040
  await runArchitectMode(message, agentOpts);
1768
2041
  return;
1769
2042
  }
2043
+ // Tree planning mode: LATS branching search
2044
+ if (opts.tree) {
2045
+ const { executeTreePlan } = await import('./tree-planner.js');
2046
+ await executeTreePlan(message, agentOpts);
2047
+ return;
2048
+ }
1770
2049
  agentOpts.stream = true; // Force streaming for faster one-shot
1771
2050
  await runAndPrint(message, agentOpts);
1772
2051
  return;
@@ -2089,9 +2368,20 @@ async function startRepl(agentOpts, context, tier, byokActive = false, localActi
2089
2368
  printInfo(`${p.name}`);
2090
2369
  }
2091
2370
  const sessionCount = incrementSessions();
2371
+ // Three-tier memory: run synthesis on every 10th session (or first time with enough data)
2372
+ if (sessionCount % 10 === 0 || sessionCount === 1) {
2373
+ try {
2374
+ const insightCount = maybeSynthesize();
2375
+ if (insightCount > 0) {
2376
+ printInfo(`Memory synthesis: ${insightCount} insights generated`);
2377
+ }
2378
+ }
2379
+ catch { /* synthesis is non-critical */ }
2380
+ }
2092
2381
  // Return-visit greeting — show kbot's growth
2093
2382
  if (sessionCount > 1) {
2094
2383
  const stats = getExtendedStats();
2384
+ const synthStats = getSynthesisStats();
2095
2385
  const parts = [];
2096
2386
  if (stats.patternsCount > 0)
2097
2387
  parts.push(`${stats.patternsCount} patterns learned`);
@@ -2099,6 +2389,8 @@ async function startRepl(agentOpts, context, tier, byokActive = false, localActi
2099
2389
  parts.push(`${stats.solutionsCount} solutions cached`);
2100
2390
  if (stats.knowledgeCount > 0)
2101
2391
  parts.push(`${stats.knowledgeCount} facts remembered`);
2392
+ if (synthStats.insightCount > 0)
2393
+ parts.push(`${synthStats.insightCount} insights synthesized`);
2102
2394
  if (parts.length > 0) {
2103
2395
  printInfo(`Session ${stats.sessions} · ${parts.join(' · ')}`);
2104
2396
  }