@charzhu/openjaw-agent 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -125,6 +125,7 @@ function loadAgentConfig() {
125
125
  use_responses_api: parsedLlm?.use_responses_api ?? DEFAULT_CONFIG.llm.use_responses_api,
126
126
  openai_tool_mode: parsedLlm?.openai_tool_mode ?? DEFAULT_CONFIG.llm.openai_tool_mode,
127
127
  openai_max_tools: parsedLlm?.openai_max_tools ?? DEFAULT_CONFIG.llm.openai_max_tools,
128
+ openai_mcp_max_tools: parsedLlm?.openai_mcp_max_tools,
128
129
  copilot_enterprise_url: parsedLlm?.copilot_enterprise_url,
129
130
  copilot_oauth_client_id: parsedLlm?.copilot_oauth_client_id ?? DEFAULT_CONFIG.llm.copilot_oauth_client_id,
130
131
  context_compression: parsedLlm?.context_compression,
@@ -214,7 +215,7 @@ var init_config = __esm({
214
215
  computer_use: true,
215
216
  // Enable computer use by default (auto-disabled for proxy)
216
217
  openai_tool_mode: "auto",
217
- openai_max_tools: 32,
218
+ openai_max_tools: 64,
218
219
  copilot_oauth_client_id: DEFAULT_COPILOT_OAUTH_CLIENT_ID
219
220
  }
220
221
  };
@@ -2432,9 +2433,9 @@ function systemPromptSection(name, compute) {
2432
2433
  function DANGEROUS_uncachedSection(name, compute, _reason) {
2433
2434
  return { name, compute, cacheBreak: true };
2434
2435
  }
2435
- async function resolveSystemPromptSections(sections2) {
2436
+ async function resolveSystemPromptSections(sections) {
2436
2437
  const results = [];
2437
- for (const section of sections2) {
2438
+ for (const section of sections) {
2438
2439
  if (!section.cacheBreak && sectionCache.has(section.name)) {
2439
2440
  results.push(sectionCache.get(section.name));
2440
2441
  continue;
@@ -2781,9 +2782,9 @@ var init_cache_monitor = __esm({
2781
2782
  /**
2782
2783
  * Take a snapshot of the current prompt state BEFORE an API call.
2783
2784
  */
2784
- snapshot(sections2, toolNames) {
2785
+ snapshot(sections, toolNames) {
2785
2786
  const sectionHashes = /* @__PURE__ */ new Map();
2786
- sections2.forEach((s, i) => {
2787
+ sections.forEach((s, i) => {
2787
2788
  sectionHashes.set(`section_${i}`, hash(s));
2788
2789
  });
2789
2790
  const toolsHash = hash(toolNames.sort().join("|"));
@@ -2793,14 +2794,14 @@ var init_cache_monitor = __esm({
2793
2794
  * Check for cache break AFTER an API call by comparing current state to previous snapshot.
2794
2795
  * Also uses cache_read_tokens from the API response to detect breaks.
2795
2796
  */
2796
- check(sections2, toolNames, cacheReadTokens, previousCacheReadTokens) {
2797
+ check(sections, toolNames, cacheReadTokens, previousCacheReadTokens) {
2797
2798
  this.totalTurns++;
2798
2799
  if (!this.lastSnapshot) {
2799
- this.snapshot(sections2, toolNames);
2800
+ this.snapshot(sections, toolNames);
2800
2801
  return null;
2801
2802
  }
2802
2803
  const changedSections = [];
2803
- sections2.forEach((s, i) => {
2804
+ sections.forEach((s, i) => {
2804
2805
  const key = `section_${i}`;
2805
2806
  const oldHash = this.lastSnapshot.sectionHashes.get(key);
2806
2807
  const newHash = hash(s);
@@ -2814,7 +2815,7 @@ var init_cache_monitor = __esm({
2814
2815
  }
2815
2816
  const tokenDrop = previousCacheReadTokens - cacheReadTokens;
2816
2817
  const hasTokenDrop = previousCacheReadTokens > 0 && tokenDrop > 1e3;
2817
- this.snapshot(sections2, toolNames);
2818
+ this.snapshot(sections, toolNames);
2818
2819
  if (changedSections.length > 0 || hasTokenDrop) {
2819
2820
  const event = {
2820
2821
  timestamp: Date.now(),
@@ -3394,6 +3395,13 @@ function resolveOpenAIMaxTools(config) {
3394
3395
  }
3395
3396
  return DEFAULT_OPENAI_MAX_TOOLS;
3396
3397
  }
3398
+ function resolveMcpMaxTools(config) {
3399
+ const configured = config.llm.openai_mcp_max_tools;
3400
+ if (typeof configured === "number" && Number.isFinite(configured) && configured >= 0) {
3401
+ return Math.floor(configured);
3402
+ }
3403
+ return null;
3404
+ }
3397
3405
  function rememberLoadedToolExposure(state, input, allTools) {
3398
3406
  const names = /* @__PURE__ */ new Set();
3399
3407
  const byName2 = new Map(allTools.map((tool) => [tool.name, tool]));
@@ -3425,36 +3433,54 @@ function rememberLoadedToolExposure(state, input, allTools) {
3425
3433
  }
3426
3434
  function selectToolsForRequest(params) {
3427
3435
  const { config, allTools, userMessage, state } = params;
3428
- const maxTools = resolveOpenAIMaxTools(config);
3436
+ const configuredMax = resolveOpenAIMaxTools(config);
3429
3437
  const compact = shouldCompactOpenAITools(config);
3438
+ const mcpTools = allTools.filter((tool) => categoryForTool(tool.name) === "mcp");
3430
3439
  if (!compact) {
3431
3440
  return {
3432
3441
  tools: allTools,
3433
3442
  mode: "full",
3434
3443
  reason: "full-mode",
3435
- maxTools,
3444
+ maxTools: configuredMax,
3436
3445
  fullToolCount: allTools.length,
3437
- exposedToolNames: allTools.map((tool) => tool.name)
3446
+ exposedToolNames: allTools.map((tool) => tool.name),
3447
+ mcpToolCount: mcpTools.length
3438
3448
  };
3439
3449
  }
3450
+ const mcpCap = resolveMcpMaxTools(config);
3451
+ const includedMcpTools = mcpCap !== null && mcpTools.length > mcpCap ? mcpTools.slice(0, mcpCap) : mcpTools;
3452
+ const maxTools = Math.min(
3453
+ MCP_AUTO_GROW_HARD_CAP,
3454
+ Math.max(configuredMax, FOUNDATION_TOOL_NAMES.length + includedMcpTools.length + BUILTIN_HEADROOM)
3455
+ );
3440
3456
  const selected = /* @__PURE__ */ new Map();
3441
3457
  const addByName = /* @__PURE__ */ __name((name) => {
3442
3458
  const tool = allTools.find((candidate) => candidate.name === name);
3443
3459
  if (tool) selected.set(tool.name, tool);
3444
3460
  }, "addByName");
3445
3461
  for (const name of FOUNDATION_TOOL_NAMES) addByName(name);
3446
- for (const name of state.exposedToolNames) addByName(name);
3462
+ for (const tool of includedMcpTools) {
3463
+ if (selected.size >= maxTools) break;
3464
+ selected.set(tool.name, tool);
3465
+ }
3466
+ for (const name of state.exposedToolNames) {
3467
+ if (selected.size >= maxTools) break;
3468
+ addByName(name);
3469
+ }
3447
3470
  const relevantCategories = categoriesForMessage(userMessage);
3448
3471
  for (const tool of allTools) {
3449
3472
  if (selected.size >= maxTools) break;
3450
- if (relevantCategories.has(categoryForTool(tool.name))) {
3473
+ const cat = categoryForTool(tool.name);
3474
+ if (cat === "mcp") continue;
3475
+ if (relevantCategories.has(cat)) {
3451
3476
  selected.set(tool.name, tool);
3452
3477
  }
3453
3478
  }
3454
3479
  if (selected.size < maxTools && relevantCategories.size === 0) {
3455
3480
  for (const tool of allTools) {
3456
3481
  if (selected.size >= maxTools) break;
3457
- if (categoryForTool(tool.name) === "memory" || categoryForTool(tool.name) === "system") {
3482
+ const cat = categoryForTool(tool.name);
3483
+ if (cat === "memory" || cat === "system") {
3458
3484
  selected.set(tool.name, tool);
3459
3485
  }
3460
3486
  }
@@ -3466,7 +3492,8 @@ function selectToolsForRequest(params) {
3466
3492
  reason: config.llm.provider === "github-copilot" ? "github-copilot" : isOpenAIProxyConfig(config) ? "openai-proxy" : "configured-compact",
3467
3493
  maxTools,
3468
3494
  fullToolCount: allTools.length,
3469
- exposedToolNames: tools.map((tool) => tool.name)
3495
+ exposedToolNames: tools.map((tool) => tool.name),
3496
+ mcpToolCount: includedMcpTools.length
3470
3497
  };
3471
3498
  }
3472
3499
  function categoriesForMessage(message) {
@@ -3498,11 +3525,13 @@ function categoryForTool(toolName) {
3498
3525
  if (toolName.startsWith("system_") || toolName.startsWith("clipboard_") || ["code_execute", "web_fetch", "web_search", "notify", "sleep", "ask_user", "config"].includes(toolName)) return "system";
3499
3526
  return "mcp";
3500
3527
  }
3501
- var DEFAULT_OPENAI_MAX_TOOLS, FOUNDATION_TOOL_NAMES, PROFILE_CATEGORIES, CATEGORY_KEYWORDS;
3528
+ var DEFAULT_OPENAI_MAX_TOOLS, MCP_AUTO_GROW_HARD_CAP, BUILTIN_HEADROOM, FOUNDATION_TOOL_NAMES, PROFILE_CATEGORIES, CATEGORY_KEYWORDS;
3502
3529
  var init_tool_exposure = __esm({
3503
3530
  "src/tool-exposure.ts"() {
3504
3531
  "use strict";
3505
- DEFAULT_OPENAI_MAX_TOOLS = 32;
3532
+ DEFAULT_OPENAI_MAX_TOOLS = 64;
3533
+ MCP_AUTO_GROW_HARD_CAP = 100;
3534
+ BUILTIN_HEADROOM = 8;
3506
3535
  FOUNDATION_TOOL_NAMES = [
3507
3536
  "openjaw_load_tools",
3508
3537
  "invoke_skill",
@@ -3530,6 +3559,7 @@ var init_tool_exposure = __esm({
3530
3559
  __name(isOpenAIProxyConfig, "isOpenAIProxyConfig");
3531
3560
  __name(shouldCompactOpenAITools, "shouldCompactOpenAITools");
3532
3561
  __name(resolveOpenAIMaxTools, "resolveOpenAIMaxTools");
3562
+ __name(resolveMcpMaxTools, "resolveMcpMaxTools");
3533
3563
  __name(rememberLoadedToolExposure, "rememberLoadedToolExposure");
3534
3564
  __name(selectToolsForRequest, "selectToolsForRequest");
3535
3565
  __name(categoriesForMessage, "categoriesForMessage");
@@ -19289,6 +19319,16 @@ var init_registry = __esm({
19289
19319
  this.registerTool(tool);
19290
19320
  }
19291
19321
  }
19322
+ /**
19323
+ * Remove a tool by name. Returns true if a tool was removed.
19324
+ *
19325
+ * Used to drop stale MCP tools when a server reconnects, is denied,
19326
+ * or is removed so the LLM does not try to call a tool whose backing
19327
+ * connection no longer exists.
19328
+ */
19329
+ unregisterTool(name) {
19330
+ return this.tools.delete(name);
19331
+ }
19292
19332
  getTool(name) {
19293
19333
  return this.tools.get(name);
19294
19334
  }
@@ -19915,6 +19955,54 @@ var init_context = __esm({
19915
19955
  }
19916
19956
  });
19917
19957
 
19958
+ // src/prompts/mcp.ts
19959
+ function getMcpSection(mcpManager) {
19960
+ if (!mcpManager) return null;
19961
+ const servers = mcpManager.getConnectedServers();
19962
+ if (servers.length === 0) return null;
19963
+ const lines = [
19964
+ "# Connected MCP Servers",
19965
+ "",
19966
+ "The following Model Context Protocol (MCP) servers are connected.",
19967
+ "Their tools are ALREADY registered and visible in your tool list \u2014",
19968
+ "you do NOT need to call `openjaw_load_tools` to use them.",
19969
+ ""
19970
+ ];
19971
+ const allTools = mcpManager.getTools();
19972
+ for (const server of servers) {
19973
+ const prefix = normalizeServerName(server);
19974
+ const toolsForServer = allTools.filter((t) => t.name.startsWith(`mcp__${prefix}__`));
19975
+ lines.push(`- **${server}** (${toolsForServer.length} tools) \u2014 tools start with \`mcp__${prefix}__\``);
19976
+ }
19977
+ lines.push(
19978
+ "",
19979
+ "## Tool Selection Rule",
19980
+ "",
19981
+ "When a user request maps to a domain covered by a connected MCP server,",
19982
+ "**call the `mcp__*` tool directly**. Do NOT call `openjaw_load_tools`",
19983
+ "first to load equivalent built-in openjaw tools. Only fall back to",
19984
+ "`openjaw_load_tools` and built-in `outlook_*` / `teams_*` / `office_*`",
19985
+ "tools when no connected MCP tool covers the task.",
19986
+ "",
19987
+ "Examples:",
19988
+ "- Work items, work-related Teams chats, project/task tracking \u2192 if a",
19989
+ " workiq-style MCP server is listed above, use its `mcp__*` tools.",
19990
+ "- Generic web search / file ops / shell \u2014 use the openjaw built-ins",
19991
+ " when no MCP server provides equivalent functionality."
19992
+ );
19993
+ return lines.join("\n");
19994
+ }
19995
+ function normalizeServerName(name) {
19996
+ return name.replace(/[^a-zA-Z0-9_-]/g, "_");
19997
+ }
19998
+ var init_mcp = __esm({
19999
+ "src/prompts/mcp.ts"() {
20000
+ "use strict";
20001
+ __name(getMcpSection, "getMcpSection");
20002
+ __name(normalizeServerName, "normalizeServerName");
20003
+ }
20004
+ });
20005
+
19918
20006
  // src/prompts/index.ts
19919
20007
  var prompts_exports = {};
19920
20008
  __export(prompts_exports, {
@@ -19922,14 +20010,14 @@ __export(prompts_exports, {
19922
20010
  clearPromptCache: () => clearPromptCache,
19923
20011
  getSystemPrompt: () => getSystemPrompt
19924
20012
  });
19925
- async function getSystemPrompt() {
19926
- const resolved = await resolveSystemPromptSections(sections);
20013
+ async function getSystemPrompt(opts = {}) {
20014
+ const resolved = await resolveSystemPromptSections(buildSections(opts));
19927
20015
  return resolved.filter((s) => s != null && s.length > 0);
19928
20016
  }
19929
20017
  function clearPromptCache() {
19930
20018
  clearSectionCache();
19931
20019
  }
19932
- var sections;
20020
+ var buildSections;
19933
20021
  var init_prompts = __esm({
19934
20022
  "src/prompts/index.ts"() {
19935
20023
  "use strict";
@@ -19942,8 +20030,9 @@ var init_prompts = __esm({
19942
20030
  init_memory2();
19943
20031
  init_skills();
19944
20032
  init_context();
20033
+ init_mcp();
19945
20034
  init_sections();
19946
- sections = [
20035
+ buildSections = /* @__PURE__ */ __name((opts = {}) => [
19947
20036
  // Static sections (cached across calls)
19948
20037
  systemPromptSection("identity", () => getIdentitySection()),
19949
20038
  systemPromptSection("reasoning", () => getReasoningSection()),
@@ -19955,8 +20044,9 @@ var init_prompts = __esm({
19955
20044
  DANGEROUS_uncachedSection("user", () => getUserSection(), "user prompt may be edited between calls"),
19956
20045
  DANGEROUS_uncachedSection("memory", () => getMemorySection(), "memory prefetch changes per query"),
19957
20046
  DANGEROUS_uncachedSection("skills", () => getSkillsSection(), "skill files may be added/edited between calls"),
20047
+ DANGEROUS_uncachedSection("mcp", () => getMcpSection(opts.mcpManager ?? null), "MCP servers can connect/disconnect mid-session via /mcp"),
19958
20048
  DANGEROUS_uncachedSection("context", () => getContextSection(), "time and cwd change between calls")
19959
- ];
20049
+ ], "buildSections");
19960
20050
  __name(getSystemPrompt, "getSystemPrompt");
19961
20051
  __name(clearPromptCache, "clearPromptCache");
19962
20052
  }
@@ -20211,8 +20301,8 @@ var init_telegram = __esm({
20211
20301
  }, "updateStatus");
20212
20302
  const { setMemoryPrefetchQuery: setMemoryPrefetchQuery2 } = await Promise.resolve().then(() => (init_memory2(), memory_exports));
20213
20303
  setMemoryPrefetchQuery2(text);
20214
- const sections2 = await this.systemPromptFn();
20215
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
20304
+ const sections = await this.systemPromptFn();
20305
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
20216
20306
  let answer = "";
20217
20307
  let toolCalls = [];
20218
20308
  const typingInterval = setInterval(() => {
@@ -20354,8 +20444,8 @@ Options: ${chunk.choices.join(" | ")}` : "";
20354
20444
  await this.bot.sendMessage(chatId, `\u{1F4F7} Processing image...`);
20355
20445
  const { setMemoryPrefetchQuery: setMemoryPrefetchQuery2 } = await Promise.resolve().then(() => (init_memory2(), memory_exports));
20356
20446
  setMemoryPrefetchQuery2(text);
20357
- const sections2 = await this.systemPromptFn();
20358
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
20447
+ const sections = await this.systemPromptFn();
20448
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
20359
20449
  const typingInterval = setInterval(() => {
20360
20450
  this.bot.sendChatAction(chatId, "typing").catch(() => {
20361
20451
  });
@@ -20491,7 +20581,7 @@ __export(meta_exports, {
20491
20581
  function createMetaTool(registry) {
20492
20582
  return {
20493
20583
  name: "openjaw_load_tools",
20494
- description: 'Dynamically load additional tools. PREFERRED: Load individual tools by name: tools: ["word_focus", "word_insert_text", "word_save"]. Only load specific tools you need for the task \u2014 avoid loading entire categories. Alternative: Load full category if many tools needed: categories: ["office"]. Available categories: ' + ALL_CATEGORIES.join(", ") + ".",
20584
+ description: 'Dynamically load additional openjaw built-in tools (file, browser, office, etc.). NOTE: MCP server tools (any tool whose name starts with `mcp__`) are ALWAYS loaded automatically \u2014 never call this tool to load them, and never assume you need it before invoking an `mcp__*` tool. Load individual openjaw built-in tools by name: tools: ["word_focus", "word_insert_text", "word_save"]. Only load specific tools you need for the task \u2014 avoid loading entire categories. Alternative: Load full category if many tools needed: categories: ["office"]. Available categories: ' + ALL_CATEGORIES.join(", ") + ".",
20495
20585
  parameters: {
20496
20586
  type: "object",
20497
20587
  properties: {
@@ -25294,8 +25384,8 @@ Type /resume <id> to resume.` });
25294
25384
  const { spawnFork: spawnFork2 } = await Promise.resolve().then(() => (init_fork(), fork_exports));
25295
25385
  const { loadAgentConfig: loadAgentConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
25296
25386
  const forkConfig = loadAgentConfig2();
25297
- const sections2 = await systemPromptFn();
25298
- const sysPrompt = sections2.filter(Boolean).join("\n\n");
25387
+ const sections = await systemPromptFn();
25388
+ const sysPrompt = sections.filter(Boolean).join("\n\n");
25299
25389
  const task = spawnFork2(
25300
25390
  forkPrompt,
25301
25391
  forkConfig,
@@ -25337,8 +25427,8 @@ ${list}` });
25337
25427
  addMessage({ type: "system", content: "Not enough history to compact (need >12 messages)." });
25338
25428
  return;
25339
25429
  }
25340
- const sections2 = await systemPromptFn();
25341
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
25430
+ const sections = await systemPromptFn();
25431
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
25342
25432
  const result = await agentLoop.compact(systemPrompt);
25343
25433
  if (result) {
25344
25434
  const afterTokens = contextManager.getEstimatedTokens();
@@ -25360,8 +25450,8 @@ ${list}` });
25360
25450
  addMessage({ type: "system", content: "Not enough history to compress (need >6 messages)." });
25361
25451
  return;
25362
25452
  }
25363
- const sections2 = await systemPromptFn();
25364
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
25453
+ const sections = await systemPromptFn();
25454
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
25365
25455
  const compressor = agentLoop.contextCompressor;
25366
25456
  if (compressor.isThrottled) {
25367
25457
  addMessage({ type: "system", content: "\u26A0 Compression throttled \u2014 recent attempts saved <10%. Try /compact or /clear instead." });
@@ -25660,8 +25750,8 @@ ${voices.length > 20 ? `... and ${voices.length - 20} more` : ""}` });
25660
25750
  try {
25661
25751
  const { setMemoryPrefetchQuery: setMemoryPrefetchQuery2 } = await Promise.resolve().then(() => (init_memory2(), memory_exports));
25662
25752
  setMemoryPrefetchQuery2(input);
25663
- const sections2 = await systemPromptFn();
25664
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
25753
+ const sections = await systemPromptFn();
25754
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
25665
25755
  let userMessage = image ? `${input}
25666
25756
 
25667
25757
  [User has attached a screenshot image (${image.width}\xD7${image.height}). Analyze the image carefully.]` : input;
@@ -26522,8 +26612,8 @@ var init_teams = __esm({
26522
26612
  }, "updateStatus");
26523
26613
  const { setMemoryPrefetchQuery: setMemoryPrefetchQuery2 } = await Promise.resolve().then(() => (init_memory2(), memory_exports));
26524
26614
  setMemoryPrefetchQuery2(text);
26525
- const sections2 = await this.systemPromptFn();
26526
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
26615
+ const sections = await this.systemPromptFn();
26616
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
26527
26617
  let answer = "";
26528
26618
  let thinkingBuffer = "";
26529
26619
  let toolCalls = [];
@@ -26801,8 +26891,8 @@ var init_feishu = __esm({
26801
26891
  }, "updateStatus");
26802
26892
  const { setMemoryPrefetchQuery: setMemoryPrefetchQuery2 } = await Promise.resolve().then(() => (init_memory2(), memory_exports));
26803
26893
  setMemoryPrefetchQuery2(text);
26804
- const sections2 = await this.systemPromptFn();
26805
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
26894
+ const sections = await this.systemPromptFn();
26895
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
26806
26896
  let answer = "";
26807
26897
  let thinkingBuffer = "";
26808
26898
  let toolCalls = [];
@@ -27579,8 +27669,8 @@ Scan URL manually: ${qrUrl}` });
27579
27669
  });
27580
27670
  const { setMemoryPrefetchQuery: setMemoryPrefetchQuery2 } = await Promise.resolve().then(() => (init_memory2(), memory_exports));
27581
27671
  setMemoryPrefetchQuery2(text);
27582
- const sections2 = await this.systemPromptFn();
27583
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
27672
+ const sections = await this.systemPromptFn();
27673
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
27584
27674
  let answer = "";
27585
27675
  let thinkingBuffer = "";
27586
27676
  let lastThinkingSegment = "";
@@ -28119,7 +28209,7 @@ async function startInkUI(resumeSessionId, bridges = []) {
28119
28209
  join36(homedir22(), ".openjaw", "memory", "MEMORY.md")
28120
28210
  ];
28121
28211
  const memoryStatus = existsSync28(memoryDbPath) ? "loaded" : memoryLegacyPaths.some((p) => existsSync28(p)) ? "legacy" : "empty";
28122
- const systemPromptFn = /* @__PURE__ */ __name(() => getSystemPrompt(), "systemPromptFn");
28212
+ const systemPromptFn = /* @__PURE__ */ __name(() => getSystemPrompt({ mcpManager }), "systemPromptFn");
28123
28213
  const { createSkillTool: createSkillTool2 } = await Promise.resolve().then(() => (init_skill_tool(), skill_tool_exports));
28124
28214
  toolRegistry.registerTool(createSkillTool2(agentConfig, toolRegistry, systemPromptFn));
28125
28215
  const totalToolCount = toolRegistry.listTools().length;
@@ -28906,7 +28996,7 @@ async function bootstrapAgent(opts = {}) {
28906
28996
  join38(homedir24(), ".openjaw", "memory", "MEMORY.md")
28907
28997
  ];
28908
28998
  const memoryStatus = existsSync30(memoryDbPath) ? "loaded" : memoryLegacyPaths.some((p) => existsSync30(p)) ? "legacy" : "empty";
28909
- const systemPromptFn = /* @__PURE__ */ __name(() => getSystemPrompt(), "systemPromptFn");
28999
+ const systemPromptFn = /* @__PURE__ */ __name(() => getSystemPrompt({ mcpManager }), "systemPromptFn");
28910
29000
  const { createSkillTool: createSkillTool2 } = await Promise.resolve().then(() => (init_skill_tool(), skill_tool_exports));
28911
29001
  toolRegistry.registerTool(createSkillTool2(agentConfig, toolRegistry, systemPromptFn));
28912
29002
  const totalToolCount = toolRegistry.listTools().length;
@@ -44229,7 +44319,7 @@ var init_details = __esm({
44229
44319
  resolveSections = /* @__PURE__ */ __name((raw) => raw && typeof raw === "object" && !Array.isArray(raw) ? Object.fromEntries(
44230
44320
  Object.entries(raw).map(([k, v]) => [k, parseDetailsMode(v)]).filter(([k, m]) => !!m && isSectionName(k))
44231
44321
  ) : {}, "resolveSections");
44232
- sectionMode = /* @__PURE__ */ __name((name, global, sections2, commandOverride = false) => sections2?.[name] ?? (commandOverride ? global : SECTION_DEFAULTS[name] ?? global), "sectionMode");
44322
+ sectionMode = /* @__PURE__ */ __name((name, global, sections, commandOverride = false) => sections?.[name] ?? (commandOverride ? global : SECTION_DEFAULTS[name] ?? global), "sectionMode");
44233
44323
  nextDetailsMode = /* @__PURE__ */ __name((m) => MODES[(MODES.indexOf(m) + 1) % MODES.length], "nextDetailsMode");
44234
44324
  }
44235
44325
  });
@@ -44803,14 +44893,14 @@ var init_core = __esm({
44803
44893
  help: "list commands + hotkeys",
44804
44894
  name: "help",
44805
44895
  run: /* @__PURE__ */ __name((_arg, ctx) => {
44806
- const sections2 = (ctx.local.catalog?.categories ?? []).map((cat) => ({
44896
+ const sections = (ctx.local.catalog?.categories ?? []).map((cat) => ({
44807
44897
  rows: cat.pairs,
44808
44898
  title: cat.name
44809
44899
  }));
44810
44900
  if (ctx.local.catalog?.skillCount) {
44811
- sections2.push({ text: `${ctx.local.catalog.skillCount} skill commands available \u2014 /skills to browse` });
44901
+ sections.push({ text: `${ctx.local.catalog.skillCount} skill commands available \u2014 /skills to browse` });
44812
44902
  }
44813
- sections2.push(
44903
+ sections.push(
44814
44904
  {
44815
44905
  rows: [
44816
44906
  ["/details [hidden|collapsed|expanded|cycle]", "set global agent detail visibility mode"],
@@ -44824,7 +44914,7 @@ var init_core = __esm({
44824
44914
  },
44825
44915
  { rows: HOTKEYS, title: "Hotkeys" }
44826
44916
  );
44827
- ctx.transcript.panel(ctx.ui.theme.brand.helpHeader, sections2);
44917
+ ctx.transcript.panel(ctx.ui.theme.brand.helpHeader, sections);
44828
44918
  }, "run")
44829
44919
  },
44830
44920
  {
@@ -44985,8 +45075,8 @@ var init_core = __esm({
44985
45075
  if (!next) {
44986
45076
  return transcript.sys(DETAILS_USAGE);
44987
45077
  }
44988
- const sections2 = Object.fromEntries(SECTION_NAMES.map((section) => [section, next]));
44989
- patchUiState({ detailsMode: next, detailsModeCommandOverride: true, sections: sections2 });
45078
+ const sections = Object.fromEntries(SECTION_NAMES.map((section) => [section, next]));
45079
+ patchUiState({ detailsMode: next, detailsModeCommandOverride: true, sections });
44990
45080
  gateway.rpc("config.set", { key: "details_mode", value: next }).catch(() => {
44991
45081
  });
44992
45082
  transcript.sys(`details: ${next}`);
@@ -45821,7 +45911,7 @@ var init_openjaw = __esm({
45821
45911
  run: /* @__PURE__ */ __name((_arg, ctx) => {
45822
45912
  ctx.gateway.rpc("session.stats", { session_id: ctx.sid }).then(
45823
45913
  ctx.guarded((r) => {
45824
- const sections2 = [
45914
+ const sections = [
45825
45915
  {
45826
45916
  rows: [
45827
45917
  ["Session", r.session_id ?? ctx.sid ?? "(none)"],
@@ -45834,7 +45924,7 @@ var init_openjaw = __esm({
45834
45924
  ]
45835
45925
  }
45836
45926
  ];
45837
- ctx.transcript.panel("Session stats", sections2);
45927
+ ctx.transcript.panel("Session stats", sections);
45838
45928
  })
45839
45929
  ).catch(ctx.guardedErr);
45840
45930
  }, "run")
@@ -46335,11 +46425,11 @@ ${body}` : body;
46335
46425
  ["Category", String(info.category ?? "")],
46336
46426
  ["Path", String(info.path ?? "")]
46337
46427
  ];
46338
- const sections2 = [{ rows }];
46428
+ const sections = [{ rows }];
46339
46429
  if (info.description) {
46340
- sections2.push({ text: String(info.description) });
46430
+ sections.push({ text: String(info.description) });
46341
46431
  }
46342
- panel("Skill", sections2);
46432
+ panel("Skill", sections);
46343
46433
  })
46344
46434
  ).catch(ctx.guardedErr);
46345
46435
  return;
@@ -47094,14 +47184,14 @@ var init_session2 = __esm({
47094
47184
  if (cost) {
47095
47185
  rows.push(["Cost", cost]);
47096
47186
  }
47097
- const sections2 = [{ rows }];
47187
+ const sections = [{ rows }];
47098
47188
  if (r.context_max) {
47099
- sections2.push({ text: `Context: ${f(r.context_used)} / ${f(r.context_max)} (${r.context_percent}%)` });
47189
+ sections.push({ text: `Context: ${f(r.context_used)} / ${f(r.context_max)} (${r.context_percent}%)` });
47100
47190
  }
47101
47191
  if (r.compressions) {
47102
- sections2.push({ text: `Compressions: ${r.compressions}` });
47192
+ sections.push({ text: `Compressions: ${r.compressions}` });
47103
47193
  }
47104
- ctx.transcript.panel("Usage", sections2);
47194
+ ctx.transcript.panel("Usage", sections);
47105
47195
  });
47106
47196
  }, "run")
47107
47197
  }
@@ -48116,8 +48206,8 @@ ${helpMessage}` : field.label;
48116
48206
  if (!prompt) {
48117
48207
  return { id: 0, status: "error" };
48118
48208
  }
48119
- const sections2 = await systemPromptFn();
48120
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
48209
+ const sections = await systemPromptFn();
48210
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
48121
48211
  const task = spawnFork(prompt, loadAgentConfig(), toolRegistry, systemPrompt, (completed) => {
48122
48212
  const success = completed.status === "done";
48123
48213
  const text = `${success ? "\u2705" : "\u274C"} Fork #${completed.id} completed: ${completed.result || "(no result)"}`;
@@ -48324,8 +48414,8 @@ ${helpMessage}` : field.label;
48324
48414
  };
48325
48415
  }
48326
48416
  const before = agentLoop.history.length;
48327
- const sections2 = await systemPromptFn();
48328
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
48417
+ const sections = await systemPromptFn();
48418
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
48329
48419
  let ok = false;
48330
48420
  try {
48331
48421
  ok = await agentLoop.compact(systemPrompt);
@@ -48382,8 +48472,8 @@ ${helpMessage}` : field.label;
48382
48472
  bus.registerRpc("prompt.background", async (params) => {
48383
48473
  const text = String(params.text ?? "").trim();
48384
48474
  if (!text) return { task_id: "" };
48385
- const sections2 = await systemPromptFn();
48386
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
48475
+ const sections = await systemPromptFn();
48476
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
48387
48477
  const task = spawnFork(text, loadAgentConfig(), toolRegistry, systemPrompt, (completed) => {
48388
48478
  const success = completed.status === "done";
48389
48479
  const taskText = `${success ? "\u2705" : "\u274C"} bg ${completed.id} done: ${completed.result || "(no result)"}`;
@@ -48558,12 +48648,14 @@ ${helpMessage}` : field.label;
48558
48648
  bus.registerRpc("delegation.status", () => ({ delegated: [], paused: false }));
48559
48649
  bus.registerRpc("delegation.pause", (params) => ({ paused: Boolean(params.paused) }));
48560
48650
  bus.registerRpc("subagent.interrupt", () => ({ ok: true }));
48651
+ syncMcpTools(mcpManager, toolRegistry);
48561
48652
  bus.emitEvent({
48562
48653
  payload: sessionInfoSnapshot(agentLoop, toolRegistry),
48563
48654
  session_id: agentLoop.sessionId,
48564
48655
  type: "session.info"
48565
48656
  });
48566
48657
  const onToolsChanged = /* @__PURE__ */ __name(() => {
48658
+ syncMcpTools(mcpManager, toolRegistry);
48567
48659
  bus.emitEvent({
48568
48660
  payload: sessionInfoSnapshot(agentLoop, toolRegistry),
48569
48661
  session_id: agentLoop.sessionId,
@@ -48580,7 +48672,7 @@ ${helpMessage}` : field.label;
48580
48672
  }
48581
48673
  };
48582
48674
  }
48583
- var PROVIDERS2, PROVIDER_LABELS, PROVIDER_AUTH, isProviderAuthenticated, buildProviderOption, fetchLiveModels, BRIDGE_SOURCES, isBridgeSource, inferBridgeSource, bridgeLabels, bridgeEventLabels, stripBridgeGlyph, firstLogLine, bridgeUser, formatBridgeText, runProcess, contentToText, sessionMessageToMarkdown, usageSnapshot, sessionInfoSnapshot;
48675
+ var PROVIDERS2, PROVIDER_LABELS, PROVIDER_AUTH, isProviderAuthenticated, buildProviderOption, fetchLiveModels, BRIDGE_SOURCES, isBridgeSource, inferBridgeSource, bridgeLabels, bridgeEventLabels, stripBridgeGlyph, firstLogLine, bridgeUser, formatBridgeText, runProcess, contentToText, sessionMessageToMarkdown, usageSnapshot, sessionInfoSnapshot, MCP_TOOL_PREFIX, MAX_TOTAL_TOOLS, syncMcpTools;
48584
48676
  var init_rpcHandlers = __esm({
48585
48677
  "src/rpcHandlers.ts"() {
48586
48678
  "use strict";
@@ -48809,6 +48901,23 @@ ${raw}`;
48809
48901
  tools: { [agentLoop.providerName]: toolRegistry.listTools().map((t) => t.name) },
48810
48902
  usage: usageSnapshot(agentLoop)
48811
48903
  }), "sessionInfoSnapshot");
48904
+ MCP_TOOL_PREFIX = "mcp__";
48905
+ MAX_TOTAL_TOOLS = 200;
48906
+ syncMcpTools = /* @__PURE__ */ __name((mcpManager, toolRegistry) => {
48907
+ let removed = 0;
48908
+ for (const tool of toolRegistry.listTools()) {
48909
+ if (tool.name.startsWith(MCP_TOOL_PREFIX)) {
48910
+ if (toolRegistry.unregisterTool(tool.name)) removed++;
48911
+ }
48912
+ }
48913
+ const nonMcpCount = toolRegistry.listTools().length;
48914
+ const budget = Math.max(0, MAX_TOTAL_TOOLS - nonMcpCount);
48915
+ const mcpTools = mcpManager.getTools(budget);
48916
+ for (const tool of mcpTools) {
48917
+ toolRegistry.registerTool(tool);
48918
+ }
48919
+ return { added: mcpTools.length, removed };
48920
+ }, "syncMcpTools");
48812
48921
  __name(registerRpcHandlers, "registerRpcHandlers");
48813
48922
  }
48814
48923
  });
@@ -53504,7 +53613,7 @@ function useMainApp(gw) {
53504
53613
  []
53505
53614
  );
53506
53615
  const panel = useCallback10(
53507
- (title, sections2) => appendMessage({ kind: "panel", panelData: { sections: sections2, title }, role: "system", text: "" }),
53616
+ (title, sections) => appendMessage({ kind: "panel", panelData: { sections, title }, role: "system", text: "" }),
53508
53617
  [appendMessage]
53509
53618
  );
53510
53619
  const maybeWarn = useCallback10(
@@ -57932,10 +58041,10 @@ function SessionPanel({ info, sid, t }) {
57932
58041
  ] })
57933
58042
  ] });
57934
58043
  }
57935
- function Panel({ sections: sections2, t, title }) {
58044
+ function Panel({ sections, t, title }) {
57936
58045
  return /* @__PURE__ */ jsxs18(Box_default, { borderColor: t.color.border, borderStyle: "round", flexDirection: "column", paddingX: 2, paddingY: 1, children: [
57937
58046
  /* @__PURE__ */ jsx30(Box_default, { justifyContent: "center", marginBottom: 1, children: /* @__PURE__ */ jsx30(Text9, { bold: true, color: t.color.primary, children: title }) }),
57938
- sections2.map((sec, si) => /* @__PURE__ */ jsxs18(Box_default, { flexDirection: "column", marginTop: si > 0 ? 1 : 0, children: [
58047
+ sections.map((sec, si) => /* @__PURE__ */ jsxs18(Box_default, { flexDirection: "column", marginTop: si > 0 ? 1 : 0, children: [
57939
58048
  sec.title && /* @__PURE__ */ jsx30(Text9, { bold: true, color: t.color.accent, children: sec.title }),
57940
58049
  sec.rows?.map(([k, v], ri) => /* @__PURE__ */ jsxs18(Text9, { wrap: "truncate", children: [
57941
58050
  /* @__PURE__ */ jsx30(Text9, { color: t.color.muted, children: k.padEnd(20) }),
@@ -60097,9 +60206,9 @@ function SubagentAccordion({
60097
60206
  const noteRows = [...summary ? [summary] : [], ...item.notes];
60098
60207
  const hasNotes = noteRows.length > 0;
60099
60208
  const noteColor = statusTone === "error" ? t.color.error : statusTone === "warn" ? t.color.warn : t.color.muted;
60100
- const sections2 = [];
60209
+ const sections = [];
60101
60210
  if (hasThinking) {
60102
- sections2.push({
60211
+ sections.push({
60103
60212
  header: /* @__PURE__ */ jsx36(
60104
60213
  Chevron,
60105
60214
  {
@@ -60133,7 +60242,7 @@ function SubagentAccordion({
60133
60242
  });
60134
60243
  }
60135
60244
  if (hasTools) {
60136
- sections2.push({
60245
+ sections.push({
60137
60246
  header: /* @__PURE__ */ jsx36(
60138
60247
  Chevron,
60139
60248
  {
@@ -60169,7 +60278,7 @@ function SubagentAccordion({
60169
60278
  });
60170
60279
  }
60171
60280
  if (hasNotes) {
60172
- sections2.push({
60281
+ sections.push({
60173
60282
  header: /* @__PURE__ */ jsx36(
60174
60283
  Chevron,
60175
60284
  {
@@ -60204,7 +60313,7 @@ function SubagentAccordion({
60204
60313
  });
60205
60314
  }
60206
60315
  if (children.length > 0) {
60207
- sections2.push({
60316
+ sections.push({
60208
60317
  header: /* @__PURE__ */ jsx36(
60209
60318
  Chevron,
60210
60319
  {
@@ -60270,10 +60379,10 @@ function SubagentAccordion({
60270
60379
  stemColor: stem,
60271
60380
  stemDim: stem == null,
60272
60381
  t,
60273
- children: (childRails) => /* @__PURE__ */ jsx36(Box_default, { flexDirection: "column", children: sections2.map((section, index) => /* @__PURE__ */ jsx36(
60382
+ children: (childRails) => /* @__PURE__ */ jsx36(Box_default, { flexDirection: "column", children: sections.map((section, index) => /* @__PURE__ */ jsx36(
60274
60383
  TreeNode,
60275
60384
  {
60276
- branch: index === sections2.length - 1 ? "last" : "mid",
60385
+ branch: index === sections.length - 1 ? "last" : "mid",
60277
60386
  header: section.header,
60278
60387
  open: section.open,
60279
60388
  rails: childRails,
@@ -60345,7 +60454,7 @@ var init_thinking = __esm({
60345
60454
  reasoning = "",
60346
60455
  reasoningTokens,
60347
60456
  reasoningStreaming = false,
60348
- sections: sections2,
60457
+ sections,
60349
60458
  subagents = [],
60350
60459
  t,
60351
60460
  tools = [],
@@ -60355,12 +60464,12 @@ var init_thinking = __esm({
60355
60464
  }) {
60356
60465
  const visible = useMemo15(
60357
60466
  () => ({
60358
- thinking: sectionMode("thinking", detailsMode, sections2, commandOverride),
60359
- tools: sectionMode("tools", detailsMode, sections2, commandOverride),
60360
- subagents: sectionMode("subagents", detailsMode, sections2, commandOverride),
60361
- activity: sectionMode("activity", detailsMode, sections2, commandOverride)
60467
+ thinking: sectionMode("thinking", detailsMode, sections, commandOverride),
60468
+ tools: sectionMode("tools", detailsMode, sections, commandOverride),
60469
+ subagents: sectionMode("subagents", detailsMode, sections, commandOverride),
60470
+ activity: sectionMode("activity", detailsMode, sections, commandOverride)
60362
60471
  }),
60363
- [commandOverride, detailsMode, sections2]
60472
+ [commandOverride, detailsMode, sections]
60364
60473
  );
60365
60474
  const [now2, setNow] = useState26(() => Date.now());
60366
60475
  const [openThinking, setOpenThinking] = useState26(visible.thinking === "expanded");
@@ -60819,13 +60928,13 @@ var init_messageLine = __esm({
60819
60928
  isStreaming = false,
60820
60929
  limitHistoryRender = false,
60821
60930
  msg,
60822
- sections: sections2,
60931
+ sections,
60823
60932
  t,
60824
60933
  tools = []
60825
60934
  }) {
60826
- const thinkingMode = sectionMode("thinking", detailsMode, sections2, detailsModeCommandOverride);
60827
- const toolsMode = sectionMode("tools", detailsMode, sections2, detailsModeCommandOverride);
60828
- const activityMode = sectionMode("activity", detailsMode, sections2, detailsModeCommandOverride);
60935
+ const thinkingMode = sectionMode("thinking", detailsMode, sections, detailsModeCommandOverride);
60936
+ const toolsMode = sectionMode("tools", detailsMode, sections, detailsModeCommandOverride);
60937
+ const activityMode = sectionMode("activity", detailsMode, sections, detailsModeCommandOverride);
60829
60938
  const thinking = msg.thinking?.trim() ?? "";
60830
60939
  const systemHasBoxArt = msg.role === "system" && (msg.text.match(/[\u2500-\u259F]/g)?.length ?? 0) >= 3;
60831
60940
  const systemIsLong = msg.role === "system" && msg.text.length > SYSTEM_COLLAPSE_CHARS && !systemHasBoxArt;
@@ -60849,7 +60958,7 @@ var init_messageLine = __esm({
60849
60958
  detailsMode,
60850
60959
  reasoning: thinking,
60851
60960
  reasoningTokens: msg.thinkingTokens,
60852
- sections: sections2,
60961
+ sections,
60853
60962
  t,
60854
60963
  tools,
60855
60964
  toolTokens: msg.toolTokens,
@@ -60921,7 +61030,7 @@ var init_messageLine = __esm({
60921
61030
  detailsMode,
60922
61031
  reasoning: thinking,
60923
61032
  reasoningTokens: msg.thinkingTokens,
60924
- sections: sections2,
61033
+ sections,
60925
61034
  t,
60926
61035
  toolTokens: msg.toolTokens,
60927
61036
  trail: msg.tools
@@ -61010,7 +61119,7 @@ var init_streamingAssistant = __esm({
61010
61119
  detailsMode,
61011
61120
  detailsModeCommandOverride,
61012
61121
  progress,
61013
- sections: sections2
61122
+ sections
61014
61123
  }) {
61015
61124
  const ui = useStore8($uiState);
61016
61125
  const streamSegments = useTurnSelector((state) => state.streamSegments);
@@ -61030,7 +61139,7 @@ var init_streamingAssistant = __esm({
61030
61139
  detailsMode,
61031
61140
  detailsModeCommandOverride,
61032
61141
  msg,
61033
- sections: sections2,
61142
+ sections,
61034
61143
  t: ui.theme
61035
61144
  },
61036
61145
  `seg:${i}`
@@ -61043,7 +61152,7 @@ var init_streamingAssistant = __esm({
61043
61152
  detailsMode,
61044
61153
  detailsModeCommandOverride,
61045
61154
  msg: { kind: "trail", role: "system", text: "" },
61046
- sections: sections2,
61155
+ sections,
61047
61156
  t: ui.theme,
61048
61157
  tools: activeTools
61049
61158
  }
@@ -61061,7 +61170,7 @@ var init_streamingAssistant = __esm({
61061
61170
  text: streaming,
61062
61171
  ...streamPendingTools.length && { tools: streamPendingTools }
61063
61172
  },
61064
- sections: sections2,
61173
+ sections,
61065
61174
  t: ui.theme
61066
61175
  }
61067
61176
  ),
@@ -61073,7 +61182,7 @@ var init_streamingAssistant = __esm({
61073
61182
  detailsMode,
61074
61183
  detailsModeCommandOverride,
61075
61184
  msg: { kind: "trail", role: "system", text: "", tools: streamPendingTools },
61076
- sections: sections2,
61185
+ sections,
61077
61186
  t: ui.theme
61078
61187
  }
61079
61188
  )
@@ -61669,7 +61778,7 @@ Memory: ~/.openjaw/memory/ (shared with MCP mode)
61669
61778
  token: agentConfig.telegram.token,
61670
61779
  allowedUsers: agentConfig.telegram.allowed_users,
61671
61780
  agentLoop,
61672
- systemPromptFn: /* @__PURE__ */ __name(() => getSystemPrompt2(), "systemPromptFn")
61781
+ systemPromptFn: /* @__PURE__ */ __name(() => getSystemPrompt2({ mcpManager }), "systemPromptFn")
61673
61782
  });
61674
61783
  try {
61675
61784
  await bridge.validate();