reasonix 0.48.0 → 0.48.1

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 (106) hide show
  1. package/README.md +9 -0
  2. package/dist/cli/{acp-4ROCGYNH.js → acp-QJGGHQLA.js} +52 -135
  3. package/dist/cli/acp-QJGGHQLA.js.map +1 -0
  4. package/dist/cli/{chat-GZNB5625.js → chat-ZXF227MP.js} +23 -23
  5. package/dist/cli/{chunk-JMDE6IO3.js → chunk-3FULTFRB.js} +2 -2
  6. package/dist/cli/{chunk-7M4YYMKW.js → chunk-43ROGEX2.js} +47 -55
  7. package/dist/cli/{chunk-7M4YYMKW.js.map → chunk-43ROGEX2.js.map} +1 -1
  8. package/dist/cli/{chunk-3WGTGXO4.js → chunk-4E2BHJU4.js} +4 -4
  9. package/dist/cli/chunk-4E2BHJU4.js.map +1 -0
  10. package/dist/cli/{chunk-HR5NBKEM.js → chunk-5AW6NIHU.js} +2 -2
  11. package/dist/cli/{chunk-TE5UIIFL.js → chunk-5U5LMMFF.js} +2 -2
  12. package/dist/cli/{chunk-WZGNXR6E.js → chunk-6FRNXWDZ.js} +320 -75
  13. package/dist/cli/chunk-6FRNXWDZ.js.map +1 -0
  14. package/dist/cli/{chunk-6MZTZO7A.js → chunk-DABAOQSV.js} +1826 -1793
  15. package/dist/cli/chunk-DABAOQSV.js.map +1 -0
  16. package/dist/cli/{chunk-EMMENC4O.js → chunk-EO6RPTJG.js} +5 -5
  17. package/dist/cli/{chunk-2QSTA2QV.js → chunk-FD7SNDWW.js} +3 -3
  18. package/dist/cli/{chunk-CDVSFSAK.js → chunk-FPME5QOO.js} +19 -4
  19. package/dist/cli/chunk-FPME5QOO.js.map +1 -0
  20. package/dist/cli/{chunk-5OHHAQ4W.js → chunk-H2F4LDNH.js} +2 -2
  21. package/dist/cli/{chunk-OG5JANQ4.js → chunk-IKSYVBBZ.js} +2 -2
  22. package/dist/cli/{chunk-OPYALNTT.js → chunk-JFBGSWQB.js} +77 -38
  23. package/dist/cli/chunk-JFBGSWQB.js.map +1 -0
  24. package/dist/cli/{chunk-RUDBUHO4.js → chunk-KH5JIJJD.js} +2 -2
  25. package/dist/cli/{chunk-H4CCXMDD.js → chunk-NQZ5U37J.js} +2 -2
  26. package/dist/cli/{chunk-I4M5QJNL.js → chunk-O3AGYTG2.js} +2 -2
  27. package/dist/cli/{chunk-YW63N3ZR.js → chunk-PIC5HJRD.js} +124 -18
  28. package/dist/cli/chunk-PIC5HJRD.js.map +1 -0
  29. package/dist/cli/{chunk-NMQSUNLB.js → chunk-PJIQIYTV.js} +6 -3
  30. package/dist/cli/chunk-PJIQIYTV.js.map +1 -0
  31. package/dist/cli/{chunk-V4Y732RQ.js → chunk-R7U44O3Y.js} +2 -2
  32. package/dist/cli/{chunk-OB4BUJBL.js → chunk-RCLS63KE.js} +5 -2
  33. package/dist/cli/chunk-RCLS63KE.js.map +1 -0
  34. package/dist/cli/{chunk-FY4S7TJZ.js → chunk-SLAFMXAZ.js} +10 -2
  35. package/dist/cli/chunk-SLAFMXAZ.js.map +1 -0
  36. package/dist/cli/{chunk-DOWEOA6E.js → chunk-SWUMD2LX.js} +2 -2
  37. package/dist/cli/{chunk-S2RMQULY.js → chunk-TIJ4ZHD6.js} +7 -7
  38. package/dist/cli/{chunk-MOJYKO2A.js → chunk-WKOXKCF3.js} +3 -3
  39. package/dist/cli/{chunk-MRZG4GBF.js → chunk-XMR2VCGT.js} +3 -3
  40. package/dist/cli/{code-PMPJWXEO.js → code-OKA5YPOH.js} +26 -26
  41. package/dist/cli/{commands-QS6TG4G3.js → commands-3U6PUVSS.js} +4 -4
  42. package/dist/cli/{commit-XPRSKUBF.js → commit-HFB7SRBU.js} +3 -3
  43. package/dist/cli/{desktop-562OPWIU.js → desktop-G7UMW3CJ.js} +60 -23
  44. package/dist/cli/desktop-G7UMW3CJ.js.map +1 -0
  45. package/dist/cli/{diff-I6W4AUWJ.js → diff-PGXW4YZD.js} +8 -8
  46. package/dist/cli/{doctor-6XVZKT4U.js → doctor-WWJFLVCB.js} +8 -8
  47. package/dist/cli/index.js +33 -33
  48. package/dist/cli/{mcp-7W7ANO2Y.js → mcp-Y3VKIK3T.js} +2 -2
  49. package/dist/cli/{mcp-browse-LA4I4YIZ.js → mcp-browse-4IN2QIFR.js} +2 -2
  50. package/dist/cli/{mcp-inspect-LWXXU7BY.js → mcp-inspect-BUXFXDHB.js} +2 -2
  51. package/dist/cli/{prompt-RKZD4X6Y.js → prompt-US57R6BA.js} +5 -4
  52. package/dist/cli/{replay-2X7MVXOI.js → replay-EQJMZRB3.js} +8 -8
  53. package/dist/cli/{run-TPKXIJ27.js → run-KVEI66TR.js} +14 -14
  54. package/dist/cli/{server-NHQ3QXOZ.js → server-D6C4GHWN.js} +13 -11
  55. package/dist/cli/server-D6C4GHWN.js.map +1 -0
  56. package/dist/cli/{sessions-2A4DGSHA.js → sessions-TGVS2RQZ.js} +13 -13
  57. package/dist/cli/{setup-GOLP7J4C.js → setup-WLKX6GGG.js} +5 -5
  58. package/dist/cli/{stats-CGDAFDKI.js → stats-TCD7Q6MB.js} +6 -6
  59. package/dist/cli/{version-FIL4ZFOS.js → version-BCWWS2OU.js} +13 -13
  60. package/dist/index.d.ts +8 -2
  61. package/dist/index.js +200 -46
  62. package/dist/index.js.map +1 -1
  63. package/package.json +5 -2
  64. package/dist/cli/acp-4ROCGYNH.js.map +0 -1
  65. package/dist/cli/chunk-3WGTGXO4.js.map +0 -1
  66. package/dist/cli/chunk-6MZTZO7A.js.map +0 -1
  67. package/dist/cli/chunk-CDVSFSAK.js.map +0 -1
  68. package/dist/cli/chunk-FY4S7TJZ.js.map +0 -1
  69. package/dist/cli/chunk-NMQSUNLB.js.map +0 -1
  70. package/dist/cli/chunk-OB4BUJBL.js.map +0 -1
  71. package/dist/cli/chunk-OPYALNTT.js.map +0 -1
  72. package/dist/cli/chunk-WZGNXR6E.js.map +0 -1
  73. package/dist/cli/chunk-YW63N3ZR.js.map +0 -1
  74. package/dist/cli/desktop-562OPWIU.js.map +0 -1
  75. package/dist/cli/server-NHQ3QXOZ.js.map +0 -1
  76. /package/dist/cli/{chat-GZNB5625.js.map → chat-ZXF227MP.js.map} +0 -0
  77. /package/dist/cli/{chunk-JMDE6IO3.js.map → chunk-3FULTFRB.js.map} +0 -0
  78. /package/dist/cli/{chunk-HR5NBKEM.js.map → chunk-5AW6NIHU.js.map} +0 -0
  79. /package/dist/cli/{chunk-TE5UIIFL.js.map → chunk-5U5LMMFF.js.map} +0 -0
  80. /package/dist/cli/{chunk-EMMENC4O.js.map → chunk-EO6RPTJG.js.map} +0 -0
  81. /package/dist/cli/{chunk-2QSTA2QV.js.map → chunk-FD7SNDWW.js.map} +0 -0
  82. /package/dist/cli/{chunk-5OHHAQ4W.js.map → chunk-H2F4LDNH.js.map} +0 -0
  83. /package/dist/cli/{chunk-OG5JANQ4.js.map → chunk-IKSYVBBZ.js.map} +0 -0
  84. /package/dist/cli/{chunk-RUDBUHO4.js.map → chunk-KH5JIJJD.js.map} +0 -0
  85. /package/dist/cli/{chunk-H4CCXMDD.js.map → chunk-NQZ5U37J.js.map} +0 -0
  86. /package/dist/cli/{chunk-I4M5QJNL.js.map → chunk-O3AGYTG2.js.map} +0 -0
  87. /package/dist/cli/{chunk-V4Y732RQ.js.map → chunk-R7U44O3Y.js.map} +0 -0
  88. /package/dist/cli/{chunk-DOWEOA6E.js.map → chunk-SWUMD2LX.js.map} +0 -0
  89. /package/dist/cli/{chunk-S2RMQULY.js.map → chunk-TIJ4ZHD6.js.map} +0 -0
  90. /package/dist/cli/{chunk-MOJYKO2A.js.map → chunk-WKOXKCF3.js.map} +0 -0
  91. /package/dist/cli/{chunk-MRZG4GBF.js.map → chunk-XMR2VCGT.js.map} +0 -0
  92. /package/dist/cli/{code-PMPJWXEO.js.map → code-OKA5YPOH.js.map} +0 -0
  93. /package/dist/cli/{commands-QS6TG4G3.js.map → commands-3U6PUVSS.js.map} +0 -0
  94. /package/dist/cli/{commit-XPRSKUBF.js.map → commit-HFB7SRBU.js.map} +0 -0
  95. /package/dist/cli/{diff-I6W4AUWJ.js.map → diff-PGXW4YZD.js.map} +0 -0
  96. /package/dist/cli/{doctor-6XVZKT4U.js.map → doctor-WWJFLVCB.js.map} +0 -0
  97. /package/dist/cli/{mcp-7W7ANO2Y.js.map → mcp-Y3VKIK3T.js.map} +0 -0
  98. /package/dist/cli/{mcp-browse-LA4I4YIZ.js.map → mcp-browse-4IN2QIFR.js.map} +0 -0
  99. /package/dist/cli/{mcp-inspect-LWXXU7BY.js.map → mcp-inspect-BUXFXDHB.js.map} +0 -0
  100. /package/dist/cli/{prompt-RKZD4X6Y.js.map → prompt-US57R6BA.js.map} +0 -0
  101. /package/dist/cli/{replay-2X7MVXOI.js.map → replay-EQJMZRB3.js.map} +0 -0
  102. /package/dist/cli/{run-TPKXIJ27.js.map → run-KVEI66TR.js.map} +0 -0
  103. /package/dist/cli/{sessions-2A4DGSHA.js.map → sessions-TGVS2RQZ.js.map} +0 -0
  104. /package/dist/cli/{setup-GOLP7J4C.js.map → setup-WLKX6GGG.js.map} +0 -0
  105. /package/dist/cli/{stats-CGDAFDKI.js.map → stats-TCD7Q6MB.js.map} +0 -0
  106. /package/dist/cli/{version-FIL4ZFOS.js.map → version-BCWWS2OU.js.map} +0 -0
package/dist/index.js CHANGED
@@ -466,12 +466,11 @@ function memoryTypeDefaults(typeName, cfg = readConfig()) {
466
466
  if (found.expires) out.expires = found.expires;
467
467
  return out;
468
468
  }
469
- var DEFAULT_METASO_API_KEY = "mk-E384C1DD5E8501BB7EFE27C949AFDE5B";
470
469
  function loadMetasoApiKey(path2 = defaultConfigPath()) {
471
- if (process.env.METASO_API_KEY) return process.env.METASO_API_KEY;
470
+ if (process.env.METASO_API_KEY) return process.env.METASO_API_KEY.trim();
472
471
  const cfg = readConfig(path2).metasoApiKey;
473
472
  if (cfg && typeof cfg === "string" && cfg.trim()) return cfg.trim();
474
- return DEFAULT_METASO_API_KEY;
473
+ return void 0;
475
474
  }
476
475
  function loadTavilyApiKey(path2 = defaultConfigPath()) {
477
476
  if (process.env.TAVILY_API_KEY) return process.env.TAVILY_API_KEY.trim();
@@ -669,6 +668,11 @@ function addProjectShellAllowed(rootDir, prefix, path2 = defaultConfigPath()) {
669
668
  cfg.projects[key].shellAllowed = [...existing, trimmed];
670
669
  writeConfig(cfg, path2);
671
670
  }
671
+ function projectHooksTrusted(rootDir, path2 = defaultConfigPath()) {
672
+ const cfg = readConfig(path2);
673
+ const key = findProjectKey(cfg, rootDir);
674
+ return key !== void 0 && cfg.projects?.[key]?.hooksTrusted === true;
675
+ }
672
676
  function loadProjectPathAllowed(rootDir, path2 = defaultConfigPath()) {
673
677
  const cfg = readConfig(path2);
674
678
  const key = findProjectKey(cfg, rootDir);
@@ -1766,7 +1770,9 @@ var EN = {
1766
1770
  editHistoryHelpShow: "/show <id> \u2192 per-file summary \xB7 /show <id> <path> \u2192 full diff of one file",
1767
1771
  editHistoryHelpUndo: "/undo \u2192 newest non-undone \xB7 /undo <id> [path] \u2192 target a specific batch or file",
1768
1772
  editHistoryAlreadyReverted: "(already reverted \u2014 /history shows the batch-level status)",
1769
- editHistoryRevertFile: "/undo {id} {path} \u2192 revert just this file"
1773
+ editHistoryRevertFile: "/undo {id} {path} \u2192 revert just this file",
1774
+ mcpFailed: "MCP {name} failed",
1775
+ mcpWarn: "MCP {name} warn"
1770
1776
  },
1771
1777
  hooks: {
1772
1778
  head: "hook {tag} `{cmd}` {decision}{truncTag}",
@@ -1789,9 +1795,9 @@ var EN = {
1789
1795
  abortedAtIter: "aborted at iter {iter} \u2014 stopped without producing a summary (press \u2191 + Enter or /retry to resume)",
1790
1796
  toolUploadStatus: "tool result uploaded \xB7 model thinking before next response\u2026",
1791
1797
  preflightTruncateStatus: "preflight: context near full, truncating oldest history\u2026",
1792
- preflightTruncated: "preflight: request ~{estimate}/{ctxMax} tokens ({pct}%) \u2014 truncated {beforeMessages} messages \u2192 {afterMessages}. Sending.",
1793
- preflightTruncatedStillFull: "preflight: request still ~{estimate}/{ctxMax} tokens ({pct}%) after truncating {beforeMessages} messages \u2192 {afterMessages}. DeepSeek will likely 400. Run /clear or /new to start fresh.",
1794
- preflightNoFold: "preflight: request ~{estimate}/{ctxMax} tokens ({pct}%) and nothing left to truncate \u2014 DeepSeek will likely 400. Run /clear or /new to start fresh.",
1798
+ preflightTruncated: "preflight: request ~{estimate}/{ctxMax} tokens ({pct}%) \xB7 body {bodyKB} KB \u2014 truncated {beforeMessages} messages \u2192 {afterMessages}. Sending.",
1799
+ preflightTruncatedStillFull: "preflight: request still ~{estimate}/{ctxMax} tokens ({pct}%) \xB7 body {bodyKB} KB after truncating {beforeMessages} messages \u2192 {afterMessages}. DeepSeek will likely 400. Run /clear or /new to start fresh.",
1800
+ preflightNoFold: "preflight: request ~{estimate}/{ctxMax} tokens ({pct}%) \xB7 body {bodyKB} KB and nothing left to truncate \u2014 DeepSeek will likely 400. Run /clear or /new to start fresh.",
1795
1801
  flashEscalation: "\u21E7 flash requested escalation \u2014 retrying this turn on {model}{reasonSuffix}",
1796
1802
  harvestStatus: "extracting plan state from reasoning\u2026",
1797
1803
  repeatToolCallWarning: "Caught a repeated tool call \u2014 let the model see the issue and retry with a different approach.",
@@ -2078,6 +2084,12 @@ var EN = {
2078
2084
  statusMcp: " mcp {servers} server(s), {tools} tool(s) in registry",
2079
2085
  statusEdits: " edits {count} pending (/apply to commit, /discard to drop)",
2080
2086
  statusPlan: " plan ON \u2014 writes gated (submit_plan + approval)",
2087
+ statusLifecycle: " lifecycle {mode}/{state} \xB7 {progress}{evidence}",
2088
+ lifecycleNoPlan: "no plan",
2089
+ lifecycleEvidencePending: "evidence pending",
2090
+ lifecycleRejected: "lifecycle: {tool} blocked in {state} \u2014 next: {next}",
2091
+ lifecycleEvidenceRejected: "lifecycle: step {stepId} needs evidence \u2014 next: {next}",
2092
+ lifecycleRepeatedRejected: "lifecycle: repeated {tool} rejection \u2014 do not retry identical args",
2081
2093
  statusModeYolo: " mode YOLO \u2014 edits + shell auto-run with no prompt (/undo still rolls back \xB7 Shift+Tab to flip)",
2082
2094
  statusModeAuto: " mode AUTO \u2014 edits apply immediately (u to undo within 5s \xB7 Shift+Tab to flip)",
2083
2095
  statusModeReview: " mode review \u2014 edits queue for /apply or y (Shift+Tab to flip)",
@@ -2248,7 +2260,8 @@ var EN = {
2248
2260
  evt: " evt",
2249
2261
  editsLabel: "edits:",
2250
2262
  mcpLoading: "MCP",
2251
- ctx: "ctx"
2263
+ ctx: "ctx",
2264
+ shortcutsHint: "Ctrl+P shortcuts"
2252
2265
  },
2253
2266
  editMode: {
2254
2267
  plan: "PLAN MODE",
@@ -2470,7 +2483,8 @@ var EN = {
2470
2483
  endpointMustBeHttp: "web_search: SearXNG endpoint must be http(s), got {protocol} \u2014 try: set a valid URL with /search-endpoint http://host:port",
2471
2484
  cannotReach: "web_search: Cannot reach SearXNG server at {endpoint} \u2014 try: install and start SearXNG (https://github.com/searxng/searxng, e.g. `docker run -d -p 8080:8080 searxng/searxng`), or switch to another engine with /search-engine mojeek|searxng|metaso|tavily|perplexity|exa",
2472
2485
  searxngNoResults: "web_search: 0 results but SearXNG response doesn't look like an empty results page ({chars} chars) \u2014 try: rephrase the query with simpler terms, or switch engine with /search-engine mojeek|searxng|metaso|tavily|perplexity|exa",
2473
- metasoDailyLimit: "web_search: daily search limit reached for the default API key \u2014 set your own METASO_API_KEY env var or get one at https://metaso.cn/search-api/playground",
2486
+ metasoMissingKey: "web_search: Metaso requires an API key \u2014 set METASO_API_KEY or configure one with /search-engine metaso <key>. Get one at https://metaso.cn/search-api/playground",
2487
+ metasoDailyLimit: "web_search: Metaso daily search limit reached \u2014 set METASO_API_KEY or get a key at https://metaso.cn/search-api/playground",
2474
2488
  metasoUnauthorized: "web_search: Metaso API key rejected \u2014 check METASO_API_KEY or get one at https://metaso.cn/search-api/playground",
2475
2489
  metasoRateLimit: "web_search: Metaso rate-limited \u2014 wait and retry, or get your own API key at https://metaso.cn/search-api/playground",
2476
2490
  metasoServerError: "web_search: Metaso server error ({status}) \u2014 try again later, or switch engine with /search-engine mojeek|searxng|metaso|tavily|perplexity|exa",
@@ -2662,7 +2676,13 @@ var EN = {
2662
2676
  noResources: "No resources on any connected MCP server (or no servers connected). `/mcp` shows the current set.",
2663
2677
  readOne: "Read one: `/resource <uri>` \u2014 or use Tab in the picker.",
2664
2678
  noPrompts: "No prompts on any connected MCP server (or no servers connected). `/mcp` shows the current set.",
2665
- fetchOne: "Fetch one: `/prompt <name>` \u2014 args are not supported yet; prompts with required args will surface an error from the server."
2679
+ fetchOne: "Fetch one: `/prompt <name>` \u2014 args are not supported yet; prompts with required args will surface an error from the server.",
2680
+ noServerForResource: 'no server exposes resource "{name}"',
2681
+ resourceHint: "`/resource` with no arg lists what's available.",
2682
+ readFailed: "readResource failed",
2683
+ noServerForPrompt: 'no server exposes prompt "{name}"',
2684
+ promptHint: "`/prompt` with no arg lists what's available.",
2685
+ fetchFailed: "getPrompt failed"
2666
2686
  },
2667
2687
  mcpLifecycle: {
2668
2688
  handshake: "handshake\u2026",
@@ -2675,7 +2695,9 @@ var EN = {
2675
2695
  disabledDetail: "via /mcp disable {name}",
2676
2696
  failedSetupHint: "\u2192 run `reasonix setup` to remove this entry, or fix the underlying issue (missing npm package, network, etc.).",
2677
2697
  failedSetupConfigHint: "\u2192 run `reasonix setup` to remove broken entries from your saved config.",
2678
- abortedHint: "MCP startup aborted \u2014 {count} server(s) skipped. Run /mcp to retry once you've fixed the underlying issue."
2698
+ abortedHint: "MCP startup aborted \u2014 {count} server(s) skipped. Run /mcp to retry once you've fixed the underlying issue.",
2699
+ toolsReady: "tools ready",
2700
+ warnLabel: "warn"
2679
2701
  },
2680
2702
  checkpointPicker: {
2681
2703
  title: "restore a checkpoint \u2014 {workspace}",
@@ -2721,6 +2743,41 @@ var EN = {
2721
2743
  noRecords: "no records",
2722
2744
  untracked: "(untracked)",
2723
2745
  churned: "(churned \xD7{count})"
2746
+ },
2747
+ builtinSkills: {
2748
+ explore: "Explore the codebase in an isolated subagent \u2014 wide-net read-only investigation that returns one distilled answer. Best for: 'find all places that\u2026', 'how does X work across the project', 'survey the code for Y'.",
2749
+ research: "Research a question by combining web search + code reading in an isolated subagent. Best for: 'is X feature supported by lib Y', 'what\u2019s the canonical way to do Z', 'compare our impl against the spec'.",
2750
+ review: "Review the pending changes (current branch diff by default) in an isolated subagent \u2014 flags correctness, security, missing tests, hidden behavior changes; reports verdict + per-issue file:line. Read-only; the parent decides what to act on.",
2751
+ securityReview: "Security-focused review of the current branch diff in an isolated subagent \u2014 flags injection/authz/secrets/deserialization/path-traversal/crypto issues, severity-tagged. Read-only. Use when shipping changes that touch auth, input parsing, file IO, or external requests.",
2752
+ test: "Run the project\u2019s test suite, diagnose failures, propose SEARCH/REPLACE fixes, re-run until green (or stop after 2 fix attempts on the same failure). Inlined \u2014 runs in the parent loop so you see the edit blocks and can /apply them. Detects npm/pnpm/yarn/pytest/go/cargo."
2753
+ },
2754
+ shortcutsHelp: {
2755
+ title: "Shortcuts",
2756
+ groupInput: "Input",
2757
+ groupNavigation: "Navigation",
2758
+ groupSession: "Session",
2759
+ groupSystem: "System",
2760
+ descEnter: "Send message",
2761
+ descShiftEnter: "New line",
2762
+ descCtrlU: "Clear input",
2763
+ descCtrlW: "Delete word",
2764
+ descCtrlP: "Toggle shortcut panel",
2765
+ descCtrlX: "Open in editor",
2766
+ descArrows: "Input history",
2767
+ descPgUpDown: "Scroll page",
2768
+ descCtrlL: "Clear screen",
2769
+ descCtrlB: "Toggle sidebar",
2770
+ descNewSession: "New session",
2771
+ descListSessions: "List sessions",
2772
+ descSwitchModel: "Switch model",
2773
+ descSwitchPreset: "Switch preset",
2774
+ descSwitchTheme: "Switch theme",
2775
+ descCtrlC: "Quit",
2776
+ descEsc: "Stop / Cancel",
2777
+ descCtrlR: "Toggle verbose",
2778
+ descCtrlO: "Expand stream",
2779
+ descHelp: "Show all commands",
2780
+ descShiftTab: "Switch edit mode"
2724
2781
  }
2725
2782
  };
2726
2783
 
@@ -3324,7 +3381,9 @@ var zhCN = {
3324
3381
  editHistoryHelpShow: "/show <id> \u2192 \u6587\u4EF6\u6458\u8981 \xB7 /show <id> <path> \u2192 \u67D0\u4E2A\u6587\u4EF6\u7684\u5B8C\u6574 diff",
3325
3382
  editHistoryHelpUndo: "/undo \u2192 \u6700\u65B0\u7684\u672A\u64A4\u9500\u9879 \xB7 /undo <id> [path] \u2192 \u6307\u5B9A\u6279\u6B21\u6216\u6587\u4EF6",
3326
3383
  editHistoryAlreadyReverted: "\uFF08\u5DF2\u64A4\u9500 \u2014 /history \u663E\u793A\u6279\u6B21\u7EA7\u72B6\u6001\uFF09",
3327
- editHistoryRevertFile: "/undo {id} {path} \u2192 \u4EC5\u8FD8\u539F\u6B64\u6587\u4EF6"
3384
+ editHistoryRevertFile: "/undo {id} {path} \u2192 \u4EC5\u8FD8\u539F\u6B64\u6587\u4EF6",
3385
+ mcpFailed: "MCP {name} \u5931\u8D25",
3386
+ mcpWarn: "MCP {name} \u8B66\u544A"
3328
3387
  },
3329
3388
  hooks: {
3330
3389
  head: "\u94A9\u5B50 {tag} `{cmd}` {decision}{truncTag}",
@@ -3347,9 +3406,9 @@ var zhCN = {
3347
3406
  abortedAtIter: "\u5728\u7B2C {iter} \u6B21\u5DE5\u5177\u8C03\u7528\u5904\u4E2D\u65AD \u2014 \u672A\u751F\u6210\u603B\u7ED3\u5373\u505C\u6B62\uFF08\u6309 \u2191 + Enter \u6216 /retry \u6062\u590D\uFF09",
3348
3407
  toolUploadStatus: "\u5DE5\u5177\u7ED3\u679C\u5DF2\u4E0A\u4F20 \xB7 \u6A21\u578B\u5728\u751F\u6210\u4E0B\u4E00\u6761\u54CD\u5E94\u524D\u601D\u8003\u4E2D\u2026",
3349
3408
  preflightTruncateStatus: "\u9884\u68C0\uFF1A\u4E0A\u4E0B\u6587\u63A5\u8FD1\u4E0A\u9650\uFF0C\u6B63\u5728\u88C1\u526A\u6700\u65E9\u5386\u53F2\u2026",
3350
- preflightTruncated: "\u9884\u68C0\uFF1A\u8BF7\u6C42\u7EA6 {estimate}/{ctxMax} tokens\uFF08{pct}%\uFF09\u2014 \u5DF2\u88C1\u526A {beforeMessages} \u6761\u6D88\u606F \u2192 {afterMessages}\u3002\u53D1\u9001\u4E2D\u3002",
3351
- preflightTruncatedStillFull: "\u9884\u68C0\uFF1A\u88C1\u526A {beforeMessages} \u6761\u6D88\u606F \u2192 {afterMessages} \u540E\uFF0C\u8BF7\u6C42\u4ECD\u7EA6 {estimate}/{ctxMax} tokens\uFF08{pct}%\uFF09\u2014 DeepSeek \u5927\u6982\u7387\u4F1A\u8FD4\u56DE 400\u3002\u8BF7\u8FD0\u884C /clear \u6216 /new \u91CD\u65B0\u5F00\u59CB\u3002",
3352
- preflightNoFold: "\u9884\u68C0\uFF1A\u8BF7\u6C42\u7EA6 {estimate}/{ctxMax} tokens\uFF08{pct}%\uFF09\u4E14\u6CA1\u6709\u53EF\u88C1\u526A\u7684\u5185\u5BB9 \u2014 DeepSeek \u5927\u6982\u7387\u4F1A\u8FD4\u56DE 400\u3002\u8BF7\u8FD0\u884C /clear \u6216 /new \u91CD\u65B0\u5F00\u59CB\u3002",
3409
+ preflightTruncated: "\u9884\u68C0\uFF1A\u8BF7\u6C42\u7EA6 {estimate}/{ctxMax} tokens\uFF08{pct}%\uFF09\xB7 body {bodyKB} KB \u2014 \u5DF2\u88C1\u526A {beforeMessages} \u6761\u6D88\u606F \u2192 {afterMessages}\u3002\u53D1\u9001\u4E2D\u3002",
3410
+ preflightTruncatedStillFull: "\u9884\u68C0\uFF1A\u88C1\u526A {beforeMessages} \u6761\u6D88\u606F \u2192 {afterMessages} \u540E\uFF0C\u8BF7\u6C42\u4ECD\u7EA6 {estimate}/{ctxMax} tokens\uFF08{pct}%\uFF09\xB7 body {bodyKB} KB \u2014 DeepSeek \u5927\u6982\u7387\u4F1A\u8FD4\u56DE 400\u3002\u8BF7\u8FD0\u884C /clear \u6216 /new \u91CD\u65B0\u5F00\u59CB\u3002",
3411
+ preflightNoFold: "\u9884\u68C0\uFF1A\u8BF7\u6C42\u7EA6 {estimate}/{ctxMax} tokens\uFF08{pct}%\uFF09\xB7 body {bodyKB} KB \u4E14\u6CA1\u6709\u53EF\u88C1\u526A\u7684\u5185\u5BB9 \u2014 DeepSeek \u5927\u6982\u7387\u4F1A\u8FD4\u56DE 400\u3002\u8BF7\u8FD0\u884C /clear \u6216 /new \u91CD\u65B0\u5F00\u59CB\u3002",
3353
3412
  flashEscalation: "\u21E7 flash \u8BF7\u6C42\u5347\u7EA7 \u2014 \u672C\u8F6E\u6539\u7528 {model}{reasonSuffix}",
3354
3413
  harvestStatus: "\u6B63\u5728\u4ECE\u63A8\u7406\u8FC7\u7A0B\u63D0\u53D6\u8BA1\u5212\u72B6\u6001\u2026",
3355
3414
  repeatToolCallWarning: "\u62E6\u622A\u5230\u91CD\u590D\u5DE5\u5177\u8C03\u7528 \u2014 \u8BA9\u6A21\u578B\u5BDF\u89C9\u95EE\u9898\u5E76\u6362\u79CD\u65B9\u5F0F\u91CD\u8BD5\u3002",
@@ -3636,6 +3695,12 @@ var zhCN = {
3636
3695
  statusMcp: " MCP {servers} \u4E2A\u670D\u52A1\u5668\uFF0C\u6CE8\u518C\u8868\u4E2D {tools} \u4E2A\u5DE5\u5177",
3637
3696
  statusEdits: " \u7F16\u8F91 {count} \u4E2A\u5F85\u5904\u7406\uFF08/apply \u63D0\u4EA4\uFF0C/discard \u4E22\u5F03\uFF09",
3638
3697
  statusPlan: " \u8BA1\u5212 \u5F00\u542F \u2014 \u5199\u5165\u53D7\u9650\uFF08submit_plan + \u5BA1\u6279\uFF09",
3698
+ statusLifecycle: " \u751F\u547D\u5468\u671F {mode}/{state} \xB7 {progress}{evidence}",
3699
+ lifecycleNoPlan: "\u6682\u65E0\u8BA1\u5212",
3700
+ lifecycleEvidencePending: "\u7B49\u5F85 evidence",
3701
+ lifecycleRejected: "lifecycle\uFF1A{tool} \u5728 {state} \u72B6\u6001\u88AB\u62E6\u622A \u2014 \u4E0B\u4E00\u6B65\uFF1A{next}",
3702
+ lifecycleEvidenceRejected: "lifecycle\uFF1A\u6B65\u9AA4 {stepId} \u9700\u8981 evidence \u2014 \u4E0B\u4E00\u6B65\uFF1A{next}",
3703
+ lifecycleRepeatedRejected: "lifecycle\uFF1A{tool} \u88AB\u91CD\u590D\u62E6\u622A \u2014 \u4E0D\u8981\u7528\u76F8\u540C\u53C2\u6570\u53CD\u590D\u91CD\u8BD5",
3639
3704
  statusModeYolo: " \u6A21\u5F0F YOLO \u2014 \u7F16\u8F91 + shell \u81EA\u52A8\u8FD0\u884C\uFF0C\u65E0\u63D0\u793A\uFF08/undo \u4ECD\u53EF\u56DE\u6EDA \xB7 Shift+Tab \u5207\u6362\uFF09",
3640
3705
  statusModeAuto: " \u6A21\u5F0F AUTO \u2014 \u7F16\u8F91\u7ACB\u5373\u5E94\u7528\uFF085 \u79D2\u5185\u6309 u \u64A4\u6D88 \xB7 Shift+Tab \u5207\u6362\uFF09",
3641
3706
  statusModeReview: " \u6A21\u5F0F review \u2014 \u7F16\u8F91\u6392\u961F\u7B49\u5F85 /apply \u6216 y\uFF08Shift+Tab \u5207\u6362\uFF09",
@@ -3806,7 +3871,8 @@ var zhCN = {
3806
3871
  evt: " \u4E8B\u4EF6",
3807
3872
  editsLabel: "\u7F16\u8F91:",
3808
3873
  mcpLoading: "MCP",
3809
- ctx: "\u4E0A\u4E0B\u6587"
3874
+ ctx: "\u4E0A\u4E0B\u6587",
3875
+ shortcutsHint: "Ctrl+P \u5FEB\u6377\u952E"
3810
3876
  },
3811
3877
  editMode: {
3812
3878
  plan: "\u8BA1\u5212",
@@ -4028,7 +4094,8 @@ var zhCN = {
4028
4094
  endpointMustBeHttp: "web_search: SearXNG \u7AEF\u70B9\u5FC5\u987B\u662F http(s) \u534F\u8BAE\uFF0C\u5F53\u524D\u4E3A {protocol} \u2014 try: \u4F7F\u7528 /search-endpoint http://host:port \u8BBE\u7F6E\u6709\u6548\u7684 URL",
4029
4095
  cannotReach: "web_search: \u65E0\u6CD5\u8BBF\u95EE SearXNG \u670D\u52A1\u5668 {endpoint} \u2014 try: \u5B89\u88C5\u5E76\u542F\u52A8 SearXNG\uFF08https://github.com/searxng/searxng\uFF0C\u4F8B\u5982 `docker run -d -p 8080:8080 searxng/searxng`\uFF09\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
4030
4096
  searxngNoResults: "web_search: \u8FD4\u56DE 0 \u6761\u7ED3\u679C\u4F46 SearXNG \u54CD\u5E94\u770B\u8D77\u6765\u4E0D\u662F\u6B63\u5E38\u7A7A\u7ED3\u679C\u9875\uFF08{chars} \u5B57\u7B26\uFF09\u2014 try: \u4F7F\u7528\u66F4\u7B80\u5355\u7684\u5173\u952E\u8BCD\u6539\u5199\u67E5\u8BE2\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
4031
- metasoDailyLimit: "web_search: \u9ED8\u8BA4 API \u5BC6\u94A5\u7684\u6BCF\u65E5\u641C\u7D22\u6B21\u6570\u5DF2\u8FBE\u4E0A\u9650 \u2014 \u8BBE\u7F6E METASO_API_KEY \u73AF\u5883\u53D8\u91CF\uFF0C\u6216\u5728 https://metaso.cn/search-api/playground \u83B7\u53D6\u81EA\u5DF1\u7684\u5BC6\u94A5",
4097
+ metasoMissingKey: "web_search: Metaso \u9700\u8981 API \u5BC6\u94A5 \u2014 \u8BBE\u7F6E METASO_API_KEY\uFF0C\u6216\u4F7F\u7528 /search-engine metaso <key> \u914D\u7F6E\uFF1B\u53EF\u5728 https://metaso.cn/search-api/playground \u83B7\u53D6\u5BC6\u94A5",
4098
+ metasoDailyLimit: "web_search: Metaso \u6BCF\u65E5\u641C\u7D22\u6B21\u6570\u5DF2\u8FBE\u4E0A\u9650 \u2014 \u8BBE\u7F6E METASO_API_KEY\uFF0C\u6216\u5728 https://metaso.cn/search-api/playground \u83B7\u53D6\u5BC6\u94A5",
4032
4099
  metasoUnauthorized: "web_search: Metaso API \u5BC6\u94A5\u88AB\u62D2\u7EDD \u2014 \u68C0\u67E5 METASO_API_KEY\uFF0C\u6216\u5728 https://metaso.cn/search-api/playground \u83B7\u53D6\u5BC6\u94A5",
4033
4100
  metasoRateLimit: "web_search: Metaso \u8BF7\u6C42\u9891\u7387\u9650\u5236 \u2014 \u7B49\u5F85\u540E\u91CD\u8BD5\uFF0C\u6216\u5728 https://metaso.cn/search-api/playground \u83B7\u53D6\u81EA\u5DF1\u7684\u5BC6\u94A5",
4034
4101
  metasoServerError: "web_search: Metaso \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
@@ -4220,7 +4287,13 @@ var zhCN = {
4220
4287
  noResources: "\u6CA1\u6709\u4EFB\u4F55\u5DF2\u8FDE\u63A5 MCP \u670D\u52A1\u5668\u4E0A\u7684\u8D44\u6E90\uFF08\u6216\u65E0\u670D\u52A1\u5668\u8FDE\u63A5\uFF09\u3002`/mcp` \u663E\u793A\u5F53\u524D\u5217\u8868\u3002",
4221
4288
  readOne: "\u8BFB\u53D6\uFF1A`/resource <uri>` \u2014 \u6216\u5728\u9009\u62E9\u5668\u4E2D\u4F7F\u7528 Tab \u952E\u3002",
4222
4289
  noPrompts: "\u6CA1\u6709\u4EFB\u4F55\u5DF2\u8FDE\u63A5 MCP \u670D\u52A1\u5668\u4E0A\u7684\u63D0\u793A\uFF08\u6216\u65E0\u670D\u52A1\u5668\u8FDE\u63A5\uFF09\u3002`/mcp` \u663E\u793A\u5F53\u524D\u5217\u8868\u3002",
4223
- fetchOne: "\u83B7\u53D6\uFF1A`/prompt <name>` \u2014 \u6682\u4E0D\u652F\u6301\u53C2\u6570\uFF1B\u5E26\u5FC5\u9700\u53C2\u6570\u7684\u63D0\u793A\u5C06\u8FD4\u56DE\u670D\u52A1\u5668\u9519\u8BEF\u3002"
4290
+ fetchOne: "\u83B7\u53D6\uFF1A`/prompt <name>` \u2014 \u6682\u4E0D\u652F\u6301\u53C2\u6570\uFF1B\u5E26\u5FC5\u9700\u53C2\u6570\u7684\u63D0\u793A\u5C06\u8FD4\u56DE\u670D\u52A1\u5668\u9519\u8BEF\u3002",
4291
+ noServerForResource: '\u6CA1\u6709\u670D\u52A1\u5668\u66B4\u9732\u8D44\u6E90 "{name}"',
4292
+ resourceHint: "`/resource` \u4E0D\u5E26\u53C2\u6570\u53EF\u67E5\u770B\u53EF\u7528\u5217\u8868\u3002",
4293
+ readFailed: "\u8BFB\u53D6\u8D44\u6E90\u5931\u8D25",
4294
+ noServerForPrompt: '\u6CA1\u6709\u670D\u52A1\u5668\u66B4\u9732 prompt "{name}"',
4295
+ promptHint: "`/prompt` \u4E0D\u5E26\u53C2\u6570\u53EF\u67E5\u770B\u53EF\u7528\u5217\u8868\u3002",
4296
+ fetchFailed: "\u83B7\u53D6 prompt \u5931\u8D25"
4224
4297
  },
4225
4298
  mcpLifecycle: {
4226
4299
  handshake: "\u63E1\u624B\u4E2D\u2026",
@@ -4233,7 +4306,9 @@ var zhCN = {
4233
4306
  disabledDetail: "\u901A\u8FC7 /mcp disable {name}",
4234
4307
  failedSetupHint: "\u2192 \u8FD0\u884C `reasonix setup` \u79FB\u9664\u6B64\u6761\u76EE\uFF0C\u6216\u4FEE\u590D\u5E95\u5C42\u95EE\u9898\uFF08\u7F3A\u5C11 npm \u5305\u3001\u7F51\u7EDC\u7B49\uFF09\u3002",
4235
4308
  failedSetupConfigHint: "\u2192 \u8FD0\u884C `reasonix setup` \u4ECE\u5DF2\u4FDD\u5B58\u914D\u7F6E\u4E2D\u79FB\u9664\u635F\u574F\u7684\u6761\u76EE\u3002",
4236
- abortedHint: "\u5DF2\u4E2D\u65AD MCP \u542F\u52A8 \u2014 \u8DF3\u8FC7 {count} \u4E2A\u670D\u52A1\u5668\u3002\u95EE\u9898\u4FEE\u590D\u540E\u7528 /mcp \u91CD\u65B0\u8FDE\u63A5\u3002"
4309
+ abortedHint: "\u5DF2\u4E2D\u65AD MCP \u542F\u52A8 \u2014 \u8DF3\u8FC7 {count} \u4E2A\u670D\u52A1\u5668\u3002\u95EE\u9898\u4FEE\u590D\u540E\u7528 /mcp \u91CD\u65B0\u8FDE\u63A5\u3002",
4310
+ toolsReady: "\u5DE5\u5177\u5C31\u7EEA",
4311
+ warnLabel: "\u8B66\u544A"
4237
4312
  },
4238
4313
  checkpointPicker: {
4239
4314
  title: "\u6062\u590D\u68C0\u67E5\u70B9 \u2014 {workspace}",
@@ -4279,6 +4354,41 @@ var zhCN = {
4279
4354
  noRecords: "\u65E0\u8BB0\u5F55",
4280
4355
  untracked: "\uFF08\u672A\u8FFD\u8E2A\uFF09",
4281
4356
  churned: "\uFF08\u5DF2\u53D8\u66F4 \xD7{count}\uFF09"
4357
+ },
4358
+ builtinSkills: {
4359
+ explore: "\u5728\u9694\u79BB\u5B50 agent \u4E2D\u63A2\u7D22\u4EE3\u7801\u5E93 \u2014 \u53EA\u8BFB\u5BBD\u7F51\u8C03\u67E5\uFF0C\u8FD4\u56DE\u4E00\u4E2A\u7CBE\u70BC\u7ED3\u8BBA",
4360
+ research: "\u7ED3\u5408\u4EE3\u7801\u9605\u8BFB\u4E0E\u7F51\u7EDC\u641C\u7D22\u8FDB\u884C\u8C03\u7814 \u2014 \u5728\u9694\u79BB\u5B50 agent \u4E2D\u7EFC\u5408\u4FE1\u606F\u5E76\u8FD4\u56DE\u7ED3\u8BBA",
4361
+ review: "\u5BA1\u67E5\u5F53\u524D\u5206\u652F\u53D8\u66F4 \u2014 \u68C0\u67E5\u6B63\u786E\u6027\u3001\u5B89\u5168\u6027\u3001\u7F3A\u5931\u6D4B\u8BD5\u3001\u9690\u85CF\u884C\u4E3A\u53D8\u66F4",
4362
+ securityReview: "\u5B89\u5168\u4E13\u9879\u5BA1\u67E5 \u2014 \u6807\u8BB0\u6CE8\u5165/\u8BA4\u8BC1/\u5BC6\u94A5/\u53CD\u5E8F\u5217\u5316/\u8DEF\u5F84\u7A7F\u8D8A/\u52A0\u5BC6\u95EE\u9898",
4363
+ test: "\u8FD0\u884C\u6D4B\u8BD5\u5957\u4EF6\u5E76\u8BCA\u65AD\u5931\u8D25 \u2014 \u81EA\u52A8\u8BC6\u522B\u6D4B\u8BD5\u6846\u67B6\uFF0C\u4FEE\u590D\u540E\u91CD\u8DD1\u76F4\u81F3\u901A\u8FC7"
4364
+ },
4365
+ shortcutsHelp: {
4366
+ title: "\u5FEB\u6377\u952E",
4367
+ groupInput: "\u8F93\u5165",
4368
+ groupNavigation: "\u5BFC\u822A",
4369
+ groupSession: "\u4F1A\u8BDD",
4370
+ groupSystem: "\u7CFB\u7EDF",
4371
+ descEnter: "\u53D1\u9001\u6D88\u606F",
4372
+ descShiftEnter: "\u6362\u884C",
4373
+ descCtrlU: "\u6E05\u7A7A\u8F93\u5165",
4374
+ descCtrlW: "\u5220\u9664\u5355\u8BCD",
4375
+ descCtrlP: "\u6253\u5F00/\u5173\u95ED\u5FEB\u6377\u952E\u9762\u677F",
4376
+ descCtrlX: "\u5728\u7F16\u8F91\u5668\u4E2D\u6253\u5F00",
4377
+ descArrows: "\u6D4F\u89C8\u8F93\u5165\u5386\u53F2",
4378
+ descPgUpDown: "\u7FFB\u9875",
4379
+ descCtrlL: "\u6E05\u5C4F",
4380
+ descCtrlB: "\u5207\u6362\u4FA7\u8FB9\u680F",
4381
+ descNewSession: "\u65B0\u5EFA\u4F1A\u8BDD",
4382
+ descListSessions: "\u5217\u51FA\u4F1A\u8BDD",
4383
+ descSwitchModel: "\u5207\u6362\u6A21\u578B",
4384
+ descSwitchPreset: "\u5207\u6362\u9884\u8BBE",
4385
+ descSwitchTheme: "\u5207\u6362\u4E3B\u9898",
4386
+ descCtrlC: "\u9000\u51FA",
4387
+ descEsc: "\u505C\u6B62/\u53D6\u6D88",
4388
+ descCtrlR: "\u5207\u6362\u8BE6\u7EC6\u6A21\u5F0F",
4389
+ descCtrlO: "\u5C55\u5F00\u6D41\u5F0F\u8F93\u51FA",
4390
+ descHelp: "\u663E\u793A\u6240\u6709\u547D\u4EE4",
4391
+ descShiftTab: "\u5207\u6362\u7F16\u8F91\u6A21\u5F0F"
4282
4392
  }
4283
4393
  };
4284
4394
 
@@ -4354,7 +4464,7 @@ function readSettingsFile(path2) {
4354
4464
  }
4355
4465
  function loadHooks(opts = {}) {
4356
4466
  const out = [];
4357
- if (opts.projectRoot) {
4467
+ if (opts.projectRoot && (opts.trustProjectHooks === true || projectHooksTrusted(opts.projectRoot, opts.configPath))) {
4358
4468
  const projPath = projectSettingsPath(opts.projectRoot);
4359
4469
  const settings2 = readSettingsFile(projPath);
4360
4470
  if (settings2) appendResolved(out, settings2, "project", projPath);
@@ -5916,14 +6026,16 @@ function round(n, digits) {
5916
6026
  }
5917
6027
 
5918
6028
  // src/context-manager.ts
5919
- var HISTORY_FOLD_THRESHOLD = 0.5;
6029
+ var HISTORY_FOLD_THRESHOLD = 0.75;
5920
6030
  var HISTORY_FOLD_TAIL_FRACTION = 0.2;
5921
- var HISTORY_FOLD_AGGRESSIVE_THRESHOLD = 0.7;
6031
+ var HISTORY_FOLD_AGGRESSIVE_THRESHOLD = 0.78;
5922
6032
  var HISTORY_FOLD_AGGRESSIVE_TAIL_FRACTION = 0.1;
5923
6033
  var HISTORY_FOLD_MIN_SAVINGS_FRACTION = 0.3;
5924
6034
  var FORCE_SUMMARY_THRESHOLD = 0.8;
5925
6035
  var PREFLIGHT_EMERGENCY_THRESHOLD = 0.95;
5926
6036
  var PREFLIGHT_MECHANICAL_TARGET_FRACTION = 0.7;
6037
+ var MAX_BODY_BYTES = 7e5;
6038
+ var MAX_BODY_BYTES_TARGET = 5e5;
5927
6039
  var HISTORY_FOLD_SUMMARY_TIMEOUT_MS = 15e3;
5928
6040
  var HISTORY_FOLD_MARKER = "[CONVERSATION HISTORY SUMMARY \u2014 earlier turns folded for context efficiency]\n\n";
5929
6041
  var SKILL_PIN_MEMO_HEADER = "[Active skill memos \u2014 preserved verbatim across the fold:]";
@@ -5990,14 +6102,25 @@ var ContextManager = class {
5990
6102
  }
5991
6103
  return { kind: "none", ...base };
5992
6104
  }
5993
- /** Local-side preflight before sending a request — catches oversized payloads early. */
6105
+ /** Local-side preflight before sending a request — catches oversized payloads early.
6106
+ * Two independent signals trip mechanical truncate: token estimate above the context-window
6107
+ * fraction, OR JSON body bytes above the gateway limit (see `MAX_BODY_BYTES`). */
5994
6108
  decidePreflight(messages, toolSpecs, model) {
5995
6109
  const ctxMax = DEEPSEEK_CONTEXT_TOKENS[model] ?? DEFAULT_CONTEXT_TOKENS;
5996
6110
  const estimate = estimateRequestTokens(messages, toolSpecs ?? null, true);
6111
+ const estimateBytes = Buffer.byteLength(JSON.stringify(messages), "utf8");
6112
+ const tokensOver = estimate / ctxMax > PREFLIGHT_EMERGENCY_THRESHOLD;
6113
+ const bytesOver = estimateBytes > MAX_BODY_BYTES;
6114
+ let trigger = "none";
6115
+ if (tokensOver && bytesOver) trigger = "both";
6116
+ else if (tokensOver) trigger = "tokens";
6117
+ else if (bytesOver) trigger = "bytes";
5997
6118
  return {
5998
- needsAction: estimate / ctxMax > PREFLIGHT_EMERGENCY_THRESHOLD,
6119
+ needsAction: tokensOver || bytesOver,
5999
6120
  estimateTokens: estimate,
6000
- ctxMax
6121
+ estimateBytes,
6122
+ ctxMax,
6123
+ trigger
6001
6124
  };
6002
6125
  }
6003
6126
  /** Replace older turns with one summary message; keep tail within keepRecentTokens budget. */
@@ -6050,10 +6173,13 @@ ${pinnedBodies.join("\n\n")}` : "";
6050
6173
  summaryChars: summary.content.length
6051
6174
  };
6052
6175
  }
6053
- /** Pure local emergency compaction for preflight: drop oldest log entries and keep a valid tail. */
6176
+ /** Pure local emergency compaction for preflight: drop oldest log entries and keep a valid tail.
6177
+ * Bounded by tokens AND bytes — bytes matter because DeepSeek's gateway 400s on bodies past
6178
+ * `MAX_BODY_BYTES` even when the token budget is far from exhausted. */
6054
6179
  mechanicalTruncate(model, opts) {
6055
6180
  const ctxMax = DEEPSEEK_CONTEXT_TOKENS[model] ?? DEFAULT_CONTEXT_TOKENS;
6056
6181
  const targetTokens = opts?.targetTokens ?? Math.floor(ctxMax * PREFLIGHT_MECHANICAL_TARGET_FRACTION);
6182
+ const targetBytes = opts?.targetBytes ?? MAX_BODY_BYTES_TARGET;
6057
6183
  const all = this.deps.log.toMessages();
6058
6184
  const noop = {
6059
6185
  folded: false,
@@ -6063,6 +6189,7 @@ ${pinnedBodies.join("\n\n")}` : "";
6063
6189
  };
6064
6190
  if (all.length === 0) return noop;
6065
6191
  const tokenCounts = all.map((m) => estimateConversationTokens([m], true));
6192
+ const byteCounts = all.map((m) => Buffer.byteLength(JSON.stringify(m), "utf8"));
6066
6193
  let latestUserBoundary = -1;
6067
6194
  for (let i = all.length - 1; i >= 0; i--) {
6068
6195
  if (all[i].role === "user") {
@@ -6071,12 +6198,15 @@ ${pinnedBodies.join("\n\n")}` : "";
6071
6198
  }
6072
6199
  }
6073
6200
  let cumTokens = 0;
6201
+ let cumBytes = 0;
6074
6202
  let boundary = all.length;
6075
6203
  let foundSafeBoundary = false;
6076
6204
  for (let i = all.length - 1; i >= 0; i--) {
6077
- const next = cumTokens + tokenCounts[i];
6078
- if (next > targetTokens) break;
6079
- cumTokens = next;
6205
+ const nextTokens = cumTokens + tokenCounts[i];
6206
+ const nextBytes = cumBytes + byteCounts[i];
6207
+ if (nextTokens > targetTokens || nextBytes > targetBytes) break;
6208
+ cumTokens = nextTokens;
6209
+ cumBytes = nextBytes;
6080
6210
  if (all[i].role === "user") {
6081
6211
  boundary = i;
6082
6212
  foundSafeBoundary = true;
@@ -7419,7 +7549,7 @@ ${reason}`
7419
7549
  {
7420
7550
  const decision2 = this.context.decidePreflight(messages, this.prefix.toolSpecs, this.model);
7421
7551
  if (decision2.needsAction) {
7422
- const { estimateTokens: estimate, ctxMax } = decision2;
7552
+ const { estimateTokens: estimate, estimateBytes, ctxMax } = decision2;
7423
7553
  yield {
7424
7554
  turn: this._turn,
7425
7555
  role: "status",
@@ -7441,6 +7571,7 @@ ${reason}`
7441
7571
  estimate: after.estimateTokens.toLocaleString(),
7442
7572
  ctxMax: after.ctxMax.toLocaleString(),
7443
7573
  pct: Math.round(after.estimateTokens / after.ctxMax * 100),
7574
+ bodyKB: Math.round(after.estimateBytes / 1024).toLocaleString(),
7444
7575
  beforeMessages: result.beforeMessages,
7445
7576
  afterMessages: result.afterMessages
7446
7577
  }
@@ -7453,7 +7584,8 @@ ${reason}`
7453
7584
  content: t("loop.preflightNoFold", {
7454
7585
  estimate: estimate.toLocaleString(),
7455
7586
  ctxMax: ctxMax.toLocaleString(),
7456
- pct: Math.round(estimate / ctxMax * 100)
7587
+ pct: Math.round(estimate / ctxMax * 100),
7588
+ bodyKB: Math.round(estimateBytes / 1024).toLocaleString()
7457
7589
  })
7458
7590
  };
7459
7591
  }
@@ -8671,8 +8803,13 @@ Tips:
8671
8803
  - Add \`allowed-tools: read_file, search_content\` to scope a subagent's tools
8672
8804
  `;
8673
8805
  }
8806
+ function skillDescription(s) {
8807
+ if (s.scope !== "builtin") return s.description;
8808
+ const key = s.name === "security-review" ? "securityReview" : s.name;
8809
+ return t(`builtinSkills.${key}`);
8810
+ }
8674
8811
  function skillIndexLine(s) {
8675
- const safeDesc = s.description.replace(/\n/g, " ").trim();
8812
+ const safeDesc = skillDescription(s).replace(/\n/g, " ").trim();
8676
8813
  const tag = s.runAs === "subagent" ? " [\u{1F9EC} subagent]" : "";
8677
8814
  const max = 130 - s.name.length - tag.length;
8678
8815
  const clipped = safeDesc.length > max ? `${safeDesc.slice(0, Math.max(1, max - 1))}\u2026` : safeDesc;
@@ -10015,8 +10152,8 @@ async function searchContent(ctx, startAbs, args) {
10015
10152
  for (let i = realStart; i <= winEnd; i++) {
10016
10153
  const line = lines[i];
10017
10154
  const display = line.length > 200 ? `${line.slice(0, 200)}\u2026` : line;
10018
- const sep3 = hitSet.has(i) ? ":" : "-";
10019
- if (!pushLine(`${rel}:${i + 1}${sep3} ${display}`)) return;
10155
+ const sep2 = hitSet.has(i) ? ":" : "-";
10156
+ if (!pushLine(`${rel}:${i + 1}${sep2} ${display}`)) return;
10020
10157
  }
10021
10158
  prevWindowEnd = winEnd;
10022
10159
  }
@@ -13654,6 +13791,7 @@ async function searchSearxng(query, opts = {}) {
13654
13791
  async function searchMetaso(query, opts = {}) {
13655
13792
  const topK = Math.max(1, Math.min(100, opts.topK ?? DEFAULT_TOPK));
13656
13793
  const apiKey = loadMetasoApiKey();
13794
+ if (!apiKey) throw new Error(t("webErrors.metasoMissingKey"));
13657
13795
  let resp;
13658
13796
  try {
13659
13797
  resp = await fetch(`${METASO_ENDPOINT}/search`, {
@@ -15448,7 +15586,7 @@ import {
15448
15586
  writeFileSync as writeFileSync6,
15449
15587
  writeSync
15450
15588
  } from "fs";
15451
- import { dirname as dirname8, resolve as resolve12 } from "path";
15589
+ import { dirname as dirname8, isAbsolute as isAbsolute7, relative as relative8, resolve as resolve12 } from "path";
15452
15590
  var BLOCK_RE = /^(\S[^\n]*)\n<{7} SEARCH\n([\s\S]*?)\n?={7}\n([\s\S]*?)\n?>{7} REPLACE/gm;
15453
15591
  function parseEditBlocks(text) {
15454
15592
  const out = [];
@@ -15465,10 +15603,30 @@ function parseEditBlocks(text) {
15465
15603
  }
15466
15604
  return out;
15467
15605
  }
15606
+ function resolveEditPath(rootDir, rawPath) {
15607
+ const absRoot = resolve12(rootDir);
15608
+ if (/^[A-Za-z]:[\\/]/.test(rawPath) || looksLikeAbsoluteSystemPath2(rawPath)) {
15609
+ return resolve12(rawPath);
15610
+ }
15611
+ let rooted = rawPath;
15612
+ while (rooted.startsWith("/") || rooted.startsWith("\\")) {
15613
+ rooted = rooted.slice(1);
15614
+ }
15615
+ return resolve12(absRoot, rooted || ".");
15616
+ }
15617
+ function looksLikeAbsoluteSystemPath2(rawPath) {
15618
+ return /^\/(?:home|Users|etc|var|opt|tmp|usr|mnt|Library|Volumes|proc|sys|dev|run|srv|media|Applications|System|root|boot|private)(?:[/\\]|$)/.test(
15619
+ rawPath
15620
+ );
15621
+ }
15622
+ function pathIsUnder2(child, parent) {
15623
+ const rel = relative8(parent, child);
15624
+ return rel === "" || !rel.startsWith("..") && !isAbsolute7(rel);
15625
+ }
15468
15626
  function applyEditBlock(block, rootDir) {
15469
15627
  const absRoot = resolve12(rootDir);
15470
- const absTarget = resolve12(absRoot, block.path);
15471
- if (absTarget !== absRoot && !absTarget.startsWith(`${absRoot}${sep2()}`)) {
15628
+ const absTarget = resolveEditPath(rootDir, block.path);
15629
+ if (!pathIsUnder2(absTarget, absRoot)) {
15472
15630
  return {
15473
15631
  path: block.path,
15474
15632
  status: "path-escape",
@@ -15562,13 +15720,12 @@ function applyEditBlocks(blocks, rootDir) {
15562
15720
  return blocks.map((b) => applyEditBlock(b, rootDir));
15563
15721
  }
15564
15722
  function snapshotBeforeEdits(blocks, rootDir) {
15565
- const absRoot = resolve12(rootDir);
15566
15723
  const seen = /* @__PURE__ */ new Set();
15567
15724
  const snapshots = [];
15568
15725
  for (const b of blocks) {
15569
- if (seen.has(b.path)) continue;
15570
- seen.add(b.path);
15571
- const abs = resolve12(absRoot, b.path);
15726
+ const abs = resolveEditPath(rootDir, b.path);
15727
+ if (seen.has(abs)) continue;
15728
+ seen.add(abs);
15572
15729
  if (!existsSync11(abs)) {
15573
15730
  snapshots.push({ path: b.path, prevContent: null });
15574
15731
  continue;
@@ -15584,8 +15741,8 @@ function snapshotBeforeEdits(blocks, rootDir) {
15584
15741
  function restoreSnapshots(snapshots, rootDir) {
15585
15742
  const absRoot = resolve12(rootDir);
15586
15743
  return snapshots.map((snap) => {
15587
- const abs = resolve12(absRoot, snap.path);
15588
- if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep2()}`)) {
15744
+ const abs = resolveEditPath(rootDir, snap.path);
15745
+ if (!pathIsUnder2(abs, absRoot)) {
15589
15746
  return {
15590
15747
  path: snap.path,
15591
15748
  status: "path-escape",
@@ -15612,9 +15769,6 @@ function restoreSnapshots(snapshots, rootDir) {
15612
15769
  }
15613
15770
  });
15614
15771
  }
15615
- function sep2() {
15616
- return process.platform === "win32" ? "\\" : "/";
15617
- }
15618
15772
  function lineEndingOf(text) {
15619
15773
  return text.includes("\r\n") ? "\r\n" : "\n";
15620
15774
  }