@charzhu/openjaw-agent 0.2.3 → 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");
@@ -19925,6 +19955,54 @@ var init_context = __esm({
19925
19955
  }
19926
19956
  });
19927
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
+
19928
20006
  // src/prompts/index.ts
19929
20007
  var prompts_exports = {};
19930
20008
  __export(prompts_exports, {
@@ -19932,14 +20010,14 @@ __export(prompts_exports, {
19932
20010
  clearPromptCache: () => clearPromptCache,
19933
20011
  getSystemPrompt: () => getSystemPrompt
19934
20012
  });
19935
- async function getSystemPrompt() {
19936
- const resolved = await resolveSystemPromptSections(sections);
20013
+ async function getSystemPrompt(opts = {}) {
20014
+ const resolved = await resolveSystemPromptSections(buildSections(opts));
19937
20015
  return resolved.filter((s) => s != null && s.length > 0);
19938
20016
  }
19939
20017
  function clearPromptCache() {
19940
20018
  clearSectionCache();
19941
20019
  }
19942
- var sections;
20020
+ var buildSections;
19943
20021
  var init_prompts = __esm({
19944
20022
  "src/prompts/index.ts"() {
19945
20023
  "use strict";
@@ -19952,8 +20030,9 @@ var init_prompts = __esm({
19952
20030
  init_memory2();
19953
20031
  init_skills();
19954
20032
  init_context();
20033
+ init_mcp();
19955
20034
  init_sections();
19956
- sections = [
20035
+ buildSections = /* @__PURE__ */ __name((opts = {}) => [
19957
20036
  // Static sections (cached across calls)
19958
20037
  systemPromptSection("identity", () => getIdentitySection()),
19959
20038
  systemPromptSection("reasoning", () => getReasoningSection()),
@@ -19965,8 +20044,9 @@ var init_prompts = __esm({
19965
20044
  DANGEROUS_uncachedSection("user", () => getUserSection(), "user prompt may be edited between calls"),
19966
20045
  DANGEROUS_uncachedSection("memory", () => getMemorySection(), "memory prefetch changes per query"),
19967
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"),
19968
20048
  DANGEROUS_uncachedSection("context", () => getContextSection(), "time and cwd change between calls")
19969
- ];
20049
+ ], "buildSections");
19970
20050
  __name(getSystemPrompt, "getSystemPrompt");
19971
20051
  __name(clearPromptCache, "clearPromptCache");
19972
20052
  }
@@ -20221,8 +20301,8 @@ var init_telegram = __esm({
20221
20301
  }, "updateStatus");
20222
20302
  const { setMemoryPrefetchQuery: setMemoryPrefetchQuery2 } = await Promise.resolve().then(() => (init_memory2(), memory_exports));
20223
20303
  setMemoryPrefetchQuery2(text);
20224
- const sections2 = await this.systemPromptFn();
20225
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
20304
+ const sections = await this.systemPromptFn();
20305
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
20226
20306
  let answer = "";
20227
20307
  let toolCalls = [];
20228
20308
  const typingInterval = setInterval(() => {
@@ -20364,8 +20444,8 @@ Options: ${chunk.choices.join(" | ")}` : "";
20364
20444
  await this.bot.sendMessage(chatId, `\u{1F4F7} Processing image...`);
20365
20445
  const { setMemoryPrefetchQuery: setMemoryPrefetchQuery2 } = await Promise.resolve().then(() => (init_memory2(), memory_exports));
20366
20446
  setMemoryPrefetchQuery2(text);
20367
- const sections2 = await this.systemPromptFn();
20368
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
20447
+ const sections = await this.systemPromptFn();
20448
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
20369
20449
  const typingInterval = setInterval(() => {
20370
20450
  this.bot.sendChatAction(chatId, "typing").catch(() => {
20371
20451
  });
@@ -20501,7 +20581,7 @@ __export(meta_exports, {
20501
20581
  function createMetaTool(registry) {
20502
20582
  return {
20503
20583
  name: "openjaw_load_tools",
20504
- 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(", ") + ".",
20505
20585
  parameters: {
20506
20586
  type: "object",
20507
20587
  properties: {
@@ -25304,8 +25384,8 @@ Type /resume <id> to resume.` });
25304
25384
  const { spawnFork: spawnFork2 } = await Promise.resolve().then(() => (init_fork(), fork_exports));
25305
25385
  const { loadAgentConfig: loadAgentConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
25306
25386
  const forkConfig = loadAgentConfig2();
25307
- const sections2 = await systemPromptFn();
25308
- const sysPrompt = sections2.filter(Boolean).join("\n\n");
25387
+ const sections = await systemPromptFn();
25388
+ const sysPrompt = sections.filter(Boolean).join("\n\n");
25309
25389
  const task = spawnFork2(
25310
25390
  forkPrompt,
25311
25391
  forkConfig,
@@ -25347,8 +25427,8 @@ ${list}` });
25347
25427
  addMessage({ type: "system", content: "Not enough history to compact (need >12 messages)." });
25348
25428
  return;
25349
25429
  }
25350
- const sections2 = await systemPromptFn();
25351
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
25430
+ const sections = await systemPromptFn();
25431
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
25352
25432
  const result = await agentLoop.compact(systemPrompt);
25353
25433
  if (result) {
25354
25434
  const afterTokens = contextManager.getEstimatedTokens();
@@ -25370,8 +25450,8 @@ ${list}` });
25370
25450
  addMessage({ type: "system", content: "Not enough history to compress (need >6 messages)." });
25371
25451
  return;
25372
25452
  }
25373
- const sections2 = await systemPromptFn();
25374
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
25453
+ const sections = await systemPromptFn();
25454
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
25375
25455
  const compressor = agentLoop.contextCompressor;
25376
25456
  if (compressor.isThrottled) {
25377
25457
  addMessage({ type: "system", content: "\u26A0 Compression throttled \u2014 recent attempts saved <10%. Try /compact or /clear instead." });
@@ -25670,8 +25750,8 @@ ${voices.length > 20 ? `... and ${voices.length - 20} more` : ""}` });
25670
25750
  try {
25671
25751
  const { setMemoryPrefetchQuery: setMemoryPrefetchQuery2 } = await Promise.resolve().then(() => (init_memory2(), memory_exports));
25672
25752
  setMemoryPrefetchQuery2(input);
25673
- const sections2 = await systemPromptFn();
25674
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
25753
+ const sections = await systemPromptFn();
25754
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
25675
25755
  let userMessage = image ? `${input}
25676
25756
 
25677
25757
  [User has attached a screenshot image (${image.width}\xD7${image.height}). Analyze the image carefully.]` : input;
@@ -26532,8 +26612,8 @@ var init_teams = __esm({
26532
26612
  }, "updateStatus");
26533
26613
  const { setMemoryPrefetchQuery: setMemoryPrefetchQuery2 } = await Promise.resolve().then(() => (init_memory2(), memory_exports));
26534
26614
  setMemoryPrefetchQuery2(text);
26535
- const sections2 = await this.systemPromptFn();
26536
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
26615
+ const sections = await this.systemPromptFn();
26616
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
26537
26617
  let answer = "";
26538
26618
  let thinkingBuffer = "";
26539
26619
  let toolCalls = [];
@@ -26811,8 +26891,8 @@ var init_feishu = __esm({
26811
26891
  }, "updateStatus");
26812
26892
  const { setMemoryPrefetchQuery: setMemoryPrefetchQuery2 } = await Promise.resolve().then(() => (init_memory2(), memory_exports));
26813
26893
  setMemoryPrefetchQuery2(text);
26814
- const sections2 = await this.systemPromptFn();
26815
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
26894
+ const sections = await this.systemPromptFn();
26895
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
26816
26896
  let answer = "";
26817
26897
  let thinkingBuffer = "";
26818
26898
  let toolCalls = [];
@@ -27589,8 +27669,8 @@ Scan URL manually: ${qrUrl}` });
27589
27669
  });
27590
27670
  const { setMemoryPrefetchQuery: setMemoryPrefetchQuery2 } = await Promise.resolve().then(() => (init_memory2(), memory_exports));
27591
27671
  setMemoryPrefetchQuery2(text);
27592
- const sections2 = await this.systemPromptFn();
27593
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
27672
+ const sections = await this.systemPromptFn();
27673
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
27594
27674
  let answer = "";
27595
27675
  let thinkingBuffer = "";
27596
27676
  let lastThinkingSegment = "";
@@ -28129,7 +28209,7 @@ async function startInkUI(resumeSessionId, bridges = []) {
28129
28209
  join36(homedir22(), ".openjaw", "memory", "MEMORY.md")
28130
28210
  ];
28131
28211
  const memoryStatus = existsSync28(memoryDbPath) ? "loaded" : memoryLegacyPaths.some((p) => existsSync28(p)) ? "legacy" : "empty";
28132
- const systemPromptFn = /* @__PURE__ */ __name(() => getSystemPrompt(), "systemPromptFn");
28212
+ const systemPromptFn = /* @__PURE__ */ __name(() => getSystemPrompt({ mcpManager }), "systemPromptFn");
28133
28213
  const { createSkillTool: createSkillTool2 } = await Promise.resolve().then(() => (init_skill_tool(), skill_tool_exports));
28134
28214
  toolRegistry.registerTool(createSkillTool2(agentConfig, toolRegistry, systemPromptFn));
28135
28215
  const totalToolCount = toolRegistry.listTools().length;
@@ -28916,7 +28996,7 @@ async function bootstrapAgent(opts = {}) {
28916
28996
  join38(homedir24(), ".openjaw", "memory", "MEMORY.md")
28917
28997
  ];
28918
28998
  const memoryStatus = existsSync30(memoryDbPath) ? "loaded" : memoryLegacyPaths.some((p) => existsSync30(p)) ? "legacy" : "empty";
28919
- const systemPromptFn = /* @__PURE__ */ __name(() => getSystemPrompt(), "systemPromptFn");
28999
+ const systemPromptFn = /* @__PURE__ */ __name(() => getSystemPrompt({ mcpManager }), "systemPromptFn");
28920
29000
  const { createSkillTool: createSkillTool2 } = await Promise.resolve().then(() => (init_skill_tool(), skill_tool_exports));
28921
29001
  toolRegistry.registerTool(createSkillTool2(agentConfig, toolRegistry, systemPromptFn));
28922
29002
  const totalToolCount = toolRegistry.listTools().length;
@@ -44239,7 +44319,7 @@ var init_details = __esm({
44239
44319
  resolveSections = /* @__PURE__ */ __name((raw) => raw && typeof raw === "object" && !Array.isArray(raw) ? Object.fromEntries(
44240
44320
  Object.entries(raw).map(([k, v]) => [k, parseDetailsMode(v)]).filter(([k, m]) => !!m && isSectionName(k))
44241
44321
  ) : {}, "resolveSections");
44242
- 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");
44243
44323
  nextDetailsMode = /* @__PURE__ */ __name((m) => MODES[(MODES.indexOf(m) + 1) % MODES.length], "nextDetailsMode");
44244
44324
  }
44245
44325
  });
@@ -44813,14 +44893,14 @@ var init_core = __esm({
44813
44893
  help: "list commands + hotkeys",
44814
44894
  name: "help",
44815
44895
  run: /* @__PURE__ */ __name((_arg, ctx) => {
44816
- const sections2 = (ctx.local.catalog?.categories ?? []).map((cat) => ({
44896
+ const sections = (ctx.local.catalog?.categories ?? []).map((cat) => ({
44817
44897
  rows: cat.pairs,
44818
44898
  title: cat.name
44819
44899
  }));
44820
44900
  if (ctx.local.catalog?.skillCount) {
44821
- 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` });
44822
44902
  }
44823
- sections2.push(
44903
+ sections.push(
44824
44904
  {
44825
44905
  rows: [
44826
44906
  ["/details [hidden|collapsed|expanded|cycle]", "set global agent detail visibility mode"],
@@ -44834,7 +44914,7 @@ var init_core = __esm({
44834
44914
  },
44835
44915
  { rows: HOTKEYS, title: "Hotkeys" }
44836
44916
  );
44837
- ctx.transcript.panel(ctx.ui.theme.brand.helpHeader, sections2);
44917
+ ctx.transcript.panel(ctx.ui.theme.brand.helpHeader, sections);
44838
44918
  }, "run")
44839
44919
  },
44840
44920
  {
@@ -44995,8 +45075,8 @@ var init_core = __esm({
44995
45075
  if (!next) {
44996
45076
  return transcript.sys(DETAILS_USAGE);
44997
45077
  }
44998
- const sections2 = Object.fromEntries(SECTION_NAMES.map((section) => [section, next]));
44999
- 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 });
45000
45080
  gateway.rpc("config.set", { key: "details_mode", value: next }).catch(() => {
45001
45081
  });
45002
45082
  transcript.sys(`details: ${next}`);
@@ -45831,7 +45911,7 @@ var init_openjaw = __esm({
45831
45911
  run: /* @__PURE__ */ __name((_arg, ctx) => {
45832
45912
  ctx.gateway.rpc("session.stats", { session_id: ctx.sid }).then(
45833
45913
  ctx.guarded((r) => {
45834
- const sections2 = [
45914
+ const sections = [
45835
45915
  {
45836
45916
  rows: [
45837
45917
  ["Session", r.session_id ?? ctx.sid ?? "(none)"],
@@ -45844,7 +45924,7 @@ var init_openjaw = __esm({
45844
45924
  ]
45845
45925
  }
45846
45926
  ];
45847
- ctx.transcript.panel("Session stats", sections2);
45927
+ ctx.transcript.panel("Session stats", sections);
45848
45928
  })
45849
45929
  ).catch(ctx.guardedErr);
45850
45930
  }, "run")
@@ -46345,11 +46425,11 @@ ${body}` : body;
46345
46425
  ["Category", String(info.category ?? "")],
46346
46426
  ["Path", String(info.path ?? "")]
46347
46427
  ];
46348
- const sections2 = [{ rows }];
46428
+ const sections = [{ rows }];
46349
46429
  if (info.description) {
46350
- sections2.push({ text: String(info.description) });
46430
+ sections.push({ text: String(info.description) });
46351
46431
  }
46352
- panel("Skill", sections2);
46432
+ panel("Skill", sections);
46353
46433
  })
46354
46434
  ).catch(ctx.guardedErr);
46355
46435
  return;
@@ -47104,14 +47184,14 @@ var init_session2 = __esm({
47104
47184
  if (cost) {
47105
47185
  rows.push(["Cost", cost]);
47106
47186
  }
47107
- const sections2 = [{ rows }];
47187
+ const sections = [{ rows }];
47108
47188
  if (r.context_max) {
47109
- 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}%)` });
47110
47190
  }
47111
47191
  if (r.compressions) {
47112
- sections2.push({ text: `Compressions: ${r.compressions}` });
47192
+ sections.push({ text: `Compressions: ${r.compressions}` });
47113
47193
  }
47114
- ctx.transcript.panel("Usage", sections2);
47194
+ ctx.transcript.panel("Usage", sections);
47115
47195
  });
47116
47196
  }, "run")
47117
47197
  }
@@ -48126,8 +48206,8 @@ ${helpMessage}` : field.label;
48126
48206
  if (!prompt) {
48127
48207
  return { id: 0, status: "error" };
48128
48208
  }
48129
- const sections2 = await systemPromptFn();
48130
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
48209
+ const sections = await systemPromptFn();
48210
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
48131
48211
  const task = spawnFork(prompt, loadAgentConfig(), toolRegistry, systemPrompt, (completed) => {
48132
48212
  const success = completed.status === "done";
48133
48213
  const text = `${success ? "\u2705" : "\u274C"} Fork #${completed.id} completed: ${completed.result || "(no result)"}`;
@@ -48334,8 +48414,8 @@ ${helpMessage}` : field.label;
48334
48414
  };
48335
48415
  }
48336
48416
  const before = agentLoop.history.length;
48337
- const sections2 = await systemPromptFn();
48338
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
48417
+ const sections = await systemPromptFn();
48418
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
48339
48419
  let ok = false;
48340
48420
  try {
48341
48421
  ok = await agentLoop.compact(systemPrompt);
@@ -48392,8 +48472,8 @@ ${helpMessage}` : field.label;
48392
48472
  bus.registerRpc("prompt.background", async (params) => {
48393
48473
  const text = String(params.text ?? "").trim();
48394
48474
  if (!text) return { task_id: "" };
48395
- const sections2 = await systemPromptFn();
48396
- const systemPrompt = sections2.filter(Boolean).join("\n\n");
48475
+ const sections = await systemPromptFn();
48476
+ const systemPrompt = sections.filter(Boolean).join("\n\n");
48397
48477
  const task = spawnFork(text, loadAgentConfig(), toolRegistry, systemPrompt, (completed) => {
48398
48478
  const success = completed.status === "done";
48399
48479
  const taskText = `${success ? "\u2705" : "\u274C"} bg ${completed.id} done: ${completed.result || "(no result)"}`;
@@ -53533,7 +53613,7 @@ function useMainApp(gw) {
53533
53613
  []
53534
53614
  );
53535
53615
  const panel = useCallback10(
53536
- (title, sections2) => appendMessage({ kind: "panel", panelData: { sections: sections2, title }, role: "system", text: "" }),
53616
+ (title, sections) => appendMessage({ kind: "panel", panelData: { sections, title }, role: "system", text: "" }),
53537
53617
  [appendMessage]
53538
53618
  );
53539
53619
  const maybeWarn = useCallback10(
@@ -57961,10 +58041,10 @@ function SessionPanel({ info, sid, t }) {
57961
58041
  ] })
57962
58042
  ] });
57963
58043
  }
57964
- function Panel({ sections: sections2, t, title }) {
58044
+ function Panel({ sections, t, title }) {
57965
58045
  return /* @__PURE__ */ jsxs18(Box_default, { borderColor: t.color.border, borderStyle: "round", flexDirection: "column", paddingX: 2, paddingY: 1, children: [
57966
58046
  /* @__PURE__ */ jsx30(Box_default, { justifyContent: "center", marginBottom: 1, children: /* @__PURE__ */ jsx30(Text9, { bold: true, color: t.color.primary, children: title }) }),
57967
- 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: [
57968
58048
  sec.title && /* @__PURE__ */ jsx30(Text9, { bold: true, color: t.color.accent, children: sec.title }),
57969
58049
  sec.rows?.map(([k, v], ri) => /* @__PURE__ */ jsxs18(Text9, { wrap: "truncate", children: [
57970
58050
  /* @__PURE__ */ jsx30(Text9, { color: t.color.muted, children: k.padEnd(20) }),
@@ -60126,9 +60206,9 @@ function SubagentAccordion({
60126
60206
  const noteRows = [...summary ? [summary] : [], ...item.notes];
60127
60207
  const hasNotes = noteRows.length > 0;
60128
60208
  const noteColor = statusTone === "error" ? t.color.error : statusTone === "warn" ? t.color.warn : t.color.muted;
60129
- const sections2 = [];
60209
+ const sections = [];
60130
60210
  if (hasThinking) {
60131
- sections2.push({
60211
+ sections.push({
60132
60212
  header: /* @__PURE__ */ jsx36(
60133
60213
  Chevron,
60134
60214
  {
@@ -60162,7 +60242,7 @@ function SubagentAccordion({
60162
60242
  });
60163
60243
  }
60164
60244
  if (hasTools) {
60165
- sections2.push({
60245
+ sections.push({
60166
60246
  header: /* @__PURE__ */ jsx36(
60167
60247
  Chevron,
60168
60248
  {
@@ -60198,7 +60278,7 @@ function SubagentAccordion({
60198
60278
  });
60199
60279
  }
60200
60280
  if (hasNotes) {
60201
- sections2.push({
60281
+ sections.push({
60202
60282
  header: /* @__PURE__ */ jsx36(
60203
60283
  Chevron,
60204
60284
  {
@@ -60233,7 +60313,7 @@ function SubagentAccordion({
60233
60313
  });
60234
60314
  }
60235
60315
  if (children.length > 0) {
60236
- sections2.push({
60316
+ sections.push({
60237
60317
  header: /* @__PURE__ */ jsx36(
60238
60318
  Chevron,
60239
60319
  {
@@ -60299,10 +60379,10 @@ function SubagentAccordion({
60299
60379
  stemColor: stem,
60300
60380
  stemDim: stem == null,
60301
60381
  t,
60302
- 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(
60303
60383
  TreeNode,
60304
60384
  {
60305
- branch: index === sections2.length - 1 ? "last" : "mid",
60385
+ branch: index === sections.length - 1 ? "last" : "mid",
60306
60386
  header: section.header,
60307
60387
  open: section.open,
60308
60388
  rails: childRails,
@@ -60374,7 +60454,7 @@ var init_thinking = __esm({
60374
60454
  reasoning = "",
60375
60455
  reasoningTokens,
60376
60456
  reasoningStreaming = false,
60377
- sections: sections2,
60457
+ sections,
60378
60458
  subagents = [],
60379
60459
  t,
60380
60460
  tools = [],
@@ -60384,12 +60464,12 @@ var init_thinking = __esm({
60384
60464
  }) {
60385
60465
  const visible = useMemo15(
60386
60466
  () => ({
60387
- thinking: sectionMode("thinking", detailsMode, sections2, commandOverride),
60388
- tools: sectionMode("tools", detailsMode, sections2, commandOverride),
60389
- subagents: sectionMode("subagents", detailsMode, sections2, commandOverride),
60390
- 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)
60391
60471
  }),
60392
- [commandOverride, detailsMode, sections2]
60472
+ [commandOverride, detailsMode, sections]
60393
60473
  );
60394
60474
  const [now2, setNow] = useState26(() => Date.now());
60395
60475
  const [openThinking, setOpenThinking] = useState26(visible.thinking === "expanded");
@@ -60848,13 +60928,13 @@ var init_messageLine = __esm({
60848
60928
  isStreaming = false,
60849
60929
  limitHistoryRender = false,
60850
60930
  msg,
60851
- sections: sections2,
60931
+ sections,
60852
60932
  t,
60853
60933
  tools = []
60854
60934
  }) {
60855
- const thinkingMode = sectionMode("thinking", detailsMode, sections2, detailsModeCommandOverride);
60856
- const toolsMode = sectionMode("tools", detailsMode, sections2, detailsModeCommandOverride);
60857
- 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);
60858
60938
  const thinking = msg.thinking?.trim() ?? "";
60859
60939
  const systemHasBoxArt = msg.role === "system" && (msg.text.match(/[\u2500-\u259F]/g)?.length ?? 0) >= 3;
60860
60940
  const systemIsLong = msg.role === "system" && msg.text.length > SYSTEM_COLLAPSE_CHARS && !systemHasBoxArt;
@@ -60878,7 +60958,7 @@ var init_messageLine = __esm({
60878
60958
  detailsMode,
60879
60959
  reasoning: thinking,
60880
60960
  reasoningTokens: msg.thinkingTokens,
60881
- sections: sections2,
60961
+ sections,
60882
60962
  t,
60883
60963
  tools,
60884
60964
  toolTokens: msg.toolTokens,
@@ -60950,7 +61030,7 @@ var init_messageLine = __esm({
60950
61030
  detailsMode,
60951
61031
  reasoning: thinking,
60952
61032
  reasoningTokens: msg.thinkingTokens,
60953
- sections: sections2,
61033
+ sections,
60954
61034
  t,
60955
61035
  toolTokens: msg.toolTokens,
60956
61036
  trail: msg.tools
@@ -61039,7 +61119,7 @@ var init_streamingAssistant = __esm({
61039
61119
  detailsMode,
61040
61120
  detailsModeCommandOverride,
61041
61121
  progress,
61042
- sections: sections2
61122
+ sections
61043
61123
  }) {
61044
61124
  const ui = useStore8($uiState);
61045
61125
  const streamSegments = useTurnSelector((state) => state.streamSegments);
@@ -61059,7 +61139,7 @@ var init_streamingAssistant = __esm({
61059
61139
  detailsMode,
61060
61140
  detailsModeCommandOverride,
61061
61141
  msg,
61062
- sections: sections2,
61142
+ sections,
61063
61143
  t: ui.theme
61064
61144
  },
61065
61145
  `seg:${i}`
@@ -61072,7 +61152,7 @@ var init_streamingAssistant = __esm({
61072
61152
  detailsMode,
61073
61153
  detailsModeCommandOverride,
61074
61154
  msg: { kind: "trail", role: "system", text: "" },
61075
- sections: sections2,
61155
+ sections,
61076
61156
  t: ui.theme,
61077
61157
  tools: activeTools
61078
61158
  }
@@ -61090,7 +61170,7 @@ var init_streamingAssistant = __esm({
61090
61170
  text: streaming,
61091
61171
  ...streamPendingTools.length && { tools: streamPendingTools }
61092
61172
  },
61093
- sections: sections2,
61173
+ sections,
61094
61174
  t: ui.theme
61095
61175
  }
61096
61176
  ),
@@ -61102,7 +61182,7 @@ var init_streamingAssistant = __esm({
61102
61182
  detailsMode,
61103
61183
  detailsModeCommandOverride,
61104
61184
  msg: { kind: "trail", role: "system", text: "", tools: streamPendingTools },
61105
- sections: sections2,
61185
+ sections,
61106
61186
  t: ui.theme
61107
61187
  }
61108
61188
  )
@@ -61698,7 +61778,7 @@ Memory: ~/.openjaw/memory/ (shared with MCP mode)
61698
61778
  token: agentConfig.telegram.token,
61699
61779
  allowedUsers: agentConfig.telegram.allowed_users,
61700
61780
  agentLoop,
61701
- systemPromptFn: /* @__PURE__ */ __name(() => getSystemPrompt2(), "systemPromptFn")
61781
+ systemPromptFn: /* @__PURE__ */ __name(() => getSystemPrompt2({ mcpManager }), "systemPromptFn")
61702
61782
  });
61703
61783
  try {
61704
61784
  await bridge.validate();