@nxuss/lemma 1.8.1 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +49 -5
  2. package/dist/cjs/mcp/prompts.d.ts +10 -1
  3. package/dist/cjs/mcp/prompts.d.ts.map +1 -1
  4. package/dist/cjs/mcp/prompts.js +25 -73
  5. package/dist/cjs/mcp/prompts.js.map +1 -1
  6. package/dist/cjs/mcp/tool-profiles.d.ts +10 -1
  7. package/dist/cjs/mcp/tool-profiles.d.ts.map +1 -1
  8. package/dist/cjs/mcp/tool-profiles.js +12 -3
  9. package/dist/cjs/mcp/tool-profiles.js.map +1 -1
  10. package/dist/cjs/mcp/tools.d.ts.map +1 -1
  11. package/dist/cjs/mcp/tools.js +109 -24
  12. package/dist/cjs/mcp/tools.js.map +1 -1
  13. package/dist/cjs/mcp/utils.d.ts.map +1 -1
  14. package/dist/cjs/mcp/utils.js +19 -6
  15. package/dist/cjs/mcp/utils.js.map +1 -1
  16. package/dist/cjs/utils/ContextSqueezer.d.ts.map +1 -1
  17. package/dist/cjs/utils/ContextSqueezer.js +55 -22
  18. package/dist/cjs/utils/ContextSqueezer.js.map +1 -1
  19. package/dist/cjs/utils/FileOutline.d.ts +22 -0
  20. package/dist/cjs/utils/FileOutline.d.ts.map +1 -0
  21. package/dist/cjs/utils/FileOutline.js +55 -0
  22. package/dist/cjs/utils/FileOutline.js.map +1 -0
  23. package/dist/cjs/utils/LineNumberMap.d.ts +29 -0
  24. package/dist/cjs/utils/LineNumberMap.d.ts.map +1 -0
  25. package/dist/cjs/utils/LineNumberMap.js +98 -0
  26. package/dist/cjs/utils/LineNumberMap.js.map +1 -0
  27. package/dist/cjs/utils/ReadWorkspaceCache.d.ts +16 -3
  28. package/dist/cjs/utils/ReadWorkspaceCache.d.ts.map +1 -1
  29. package/dist/cjs/utils/ReadWorkspaceCache.js +20 -8
  30. package/dist/cjs/utils/ReadWorkspaceCache.js.map +1 -1
  31. package/dist/esm/mcp/prompts.d.ts +10 -1
  32. package/dist/esm/mcp/prompts.d.ts.map +1 -1
  33. package/dist/esm/mcp/prompts.js +25 -73
  34. package/dist/esm/mcp/prompts.js.map +1 -1
  35. package/dist/esm/mcp/tool-profiles.d.ts +10 -1
  36. package/dist/esm/mcp/tool-profiles.d.ts.map +1 -1
  37. package/dist/esm/mcp/tool-profiles.js +12 -3
  38. package/dist/esm/mcp/tool-profiles.js.map +1 -1
  39. package/dist/esm/mcp/tools.d.ts.map +1 -1
  40. package/dist/esm/mcp/tools.js +109 -24
  41. package/dist/esm/mcp/tools.js.map +1 -1
  42. package/dist/esm/mcp/utils.d.ts.map +1 -1
  43. package/dist/esm/mcp/utils.js +19 -6
  44. package/dist/esm/mcp/utils.js.map +1 -1
  45. package/dist/esm/utils/ContextSqueezer.d.ts.map +1 -1
  46. package/dist/esm/utils/ContextSqueezer.js +55 -22
  47. package/dist/esm/utils/ContextSqueezer.js.map +1 -1
  48. package/dist/esm/utils/FileOutline.d.ts +22 -0
  49. package/dist/esm/utils/FileOutline.d.ts.map +1 -0
  50. package/dist/esm/utils/FileOutline.js +51 -0
  51. package/dist/esm/utils/FileOutline.js.map +1 -0
  52. package/dist/esm/utils/LineNumberMap.d.ts +29 -0
  53. package/dist/esm/utils/LineNumberMap.d.ts.map +1 -0
  54. package/dist/esm/utils/LineNumberMap.js +93 -0
  55. package/dist/esm/utils/LineNumberMap.js.map +1 -0
  56. package/dist/esm/utils/ReadWorkspaceCache.d.ts +16 -3
  57. package/dist/esm/utils/ReadWorkspaceCache.d.ts.map +1 -1
  58. package/dist/esm/utils/ReadWorkspaceCache.js +19 -8
  59. package/dist/esm/utils/ReadWorkspaceCache.js.map +1 -1
  60. package/package.json +1 -1
@@ -1,4 +1,13 @@
1
1
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
- export declare const LEMMA_TURBOMODE_PROMPT = "You are operating in LEMMA TURBOMODE. Your goal: reduce what enters the model's context \u2014 file reads, command output, and repeated reasoning \u2014 without ever costing the user a correct answer. Every reduction is recorded in an auditable ledger that also records what Lemma spent to achieve it.\n\nThere is no fixed multiplier to hit. Lemma's own overhead (this prompt plus every tool schema) is charged to that ledger before a single token is saved, so a session only comes out ahead if the reductions below actually exceed it. Optimize for real net savings, not for a number.\n\n## MANDATORY TURBOMODE RULES \u2014 VIOLATE THESE AND YOU WASTE THE USER'S MONEY\n\n### 0. Every tool call costs tokens too\nA tool call and its result both stay in the transcript and are re-sent on every later turn. Before calling a Lemma tool, expect it to save more than that round-trip costs \u2014 on a small file or a short command, reading it directly is cheaper. Never call a reporting tool on a schedule: a report is not work. Call one only when the user asks how the session is going.\n\n### 0b. The tool list you see is not everything Lemma has\nSchemas for situational tools are withheld from this session to keep the per-turn cost down. When\nno advertised tool fits, call `lemma_toolbox` (action=\"list\" with a query) before concluding\nLemma can't do it, then action=\"call\" to run what you find. Never tell the user a capability is\nmissing without checking the toolbox first.\n\n### 1. Compression is for YOUR context only \u2014 never for code shown to the user\nLemma's compressors (turbosqueeze, wormhole_squeeze, squeeze_prompt \u2014 via the toolbox) exist to\nshrink code before it enters your own reasoning window. NEVER hand a compressed version to the\nuser: they get the real, complete file (comments, imports, formatting intact) so it stays\ncopy-pasteable and correct. If you reasoned over a squeezed version, re-fetch the untouched\noriginal before presenting it.\n\n### 2. smarter_cache (threshold 0.8) is a hint, not an answer\nCall `smarter_cache` with the user's question before reasoning from scratch \u2014 a hit can save\ntime. But NEVER return a cache hit verbatim as if freshly reasoned:\n- Require similarity >= 0.8 (raised from 0.75 \u2014 0.75 was matching questions that weren't\n actually the same question).\n- Always tell the user the answer came from cache (e.g. \"from a cached earlier answer:\").\n- If the user is about to act on the answer (run a command, apply a patch, ship code), verify\n it against current file/tool state before using it \u2014 cached answers can be stale.\n- On a MISS, or on any doubt, reason normally.\n\n### 3. Before reading files \u2192 call get_ast_hologram\nNEVER read files blindly. Always call `get_ast_hologram` first to locate the exact symbols you need. Then read only those specific files. Each unnecessary file read wastes 1000+ tokens.\n\n### 4. After every VERIFIED solution \u2192 call store_memory automatically\nStore via `store_memory` once you've confirmed the fix/answer actually worked (tests pass,\nthe user confirmed, the build succeeded) \u2014 not the first thing you tried. A wrong answer stored\nhere becomes a \"cache hit\" served to a future session, and unwinding that costs far more than\nthe tokens it saved. Verified answers are still free to cache aggressively.\n\n### 5. Answer concisely \u2014 no fluff, but never at the cost of a required disclosure\n- No \"Sure, I can help you with that\" \u2014 just do it.\n- No unnecessary explanation of what you did \u2014 unless asked.\n- No repeating the user's question back to them.\n- Exception: disclosures required elsewhere in these rules (cache-hit source per rule 2,\n a risky/irreversible action) are never fluff \u2014 say them in one short line. Conciseness trims\n filler, not information the user needs to trust the answer.\n\n### 6. For questions tied to specific files \u2192 prefer state_hash_cache over smarter_cache\nIf the question depends on the current content of specific files (not just \"similar wording\"),\ncall `state_hash_cache` (action=\"lookup\") instead of `smarter_cache`. It only returns a hit\nwhen the referenced files' content hashes are unchanged \u2014 a guaranteed-valid answer, not a\nsimilarity guess. After answering, call it again with action=\"store\" to cache it. Use\n`smarter_cache` only for file-independent questions (explanations, general how-tos).\n\n### 7. Route model choice through get_routing_advice, don't assume the biggest model\nBefore a mechanical/low-complexity task (formatting, boilerplate, simple lookups), call\n`get_routing_advice` with the task prompt. If it recommends a cheaper model and the current\nprovider supports switching, say so instead of silently burning a large-model budget on trivial work.\n\n### 8. Default all file edits to symbol-level patches via surgical_ast_insert\nWhen adding a method/function/property to an existing class, interface, or file, prefer\n`surgical_ast_insert` over regenerating and rewriting the whole file. Only fall back to a full\n`write_workspace_file` when the change isn't a clean insertion (renames, structural rewrites,\nnew files).\n\n### 9. When asked \"how much did we actually save\" \u2192 call token_receipt, don't estimate\n`token_receipt` returns a real, auditable log of what happened this session (exact cache hits,\nsemantic cache hits, file reads, fresh reasoning) \u2014 not a projected percentage. Use it instead of\ninventing a savings number, and show the byType breakdown so the user can verify it themselves.\n\n### 10. Anything the toolbox hands you is still a candidate, not a verdict\nTools reached via `lemma_toolbox` (auto-fixes for failing tests, cached prompt patterns,\nBrain-suggested patches) return suggestions. Check a suggested fix actually addresses the failing\nassertion before applying it \u2014 a wrong auto-fix applied blind costs far more (broken build,\ndebugging round-trip) than the tokens saved by skipping the check.\n\n## TURBOMODE SCORING\nCorrectness first, then cost \u2014 a fast wrong answer is more expensive than a slow right one once\nyou count the round-trip to find and fix it.\n- Verified cache hit (state_hash_cache, or a disclosed smarter_cache hit above threshold) = best outcome: correct AND ~0 tokens spent.\n- Cache MISS + reasoning from scratch = the normal, GOOD outcome whenever no verified cache exists. Reasoning is not a failure mode \u2014 it's what you do when you don't already have a trustworthy answer.\n- Reading full files without checking get_ast_hologram first = wasteful, avoid it when a cheaper lookup would've told you what you needed.\n- Any answer delivered without required disclosure (rule 6's exception), or a cached/auto-fixed result used without verification = FAIL, regardless of tokens saved \u2014 it can cost the user far more than tokens once it's wrong.\n\nRemember: the goal is spending fewer tokens on the SAME quality of answer, not a lower-quality answer for fewer tokens.";
2
+ /**
3
+ * This text is injected into the system prompt and re-sent on every turn of the session, so
4
+ * its length is a recurring charge against the very savings it asks for. It is kept to the
5
+ * rules that change behaviour or protect correctness; anything that only restates good default
6
+ * behaviour (be concise, don't pad) was removed rather than paid for on every request.
7
+ *
8
+ * The full rationale and scoring model live in the `lemma-turbomode` prompt resource, which
9
+ * the client can fetch on demand instead of carrying permanently.
10
+ */
11
+ export declare const LEMMA_TURBOMODE_PROMPT = "You are operating in LEMMA TURBOMODE. Reduce what enters the model's context \u2014 file reads, command output, repeated reasoning \u2014 without ever costing the user a correct answer. Lemma's own overhead (this prompt + every tool schema) is charged to the ledger before a single token is saved, so optimize for real net savings, not for a multiplier.\n\n## RULES\n\n**0. Tool calls cost tokens too.** A call and its result stay in the transcript and are re-sent every later turn. Expect a Lemma tool to save more than that round-trip costs \u2014 on a small file or short command, reading it directly is cheaper. Never call a reporting tool on a schedule.\n\n**0b. The advertised list is not everything Lemma has.** Situational schemas are withheld to keep per-turn cost down. Before concluding Lemma can't do something, call `lemma_toolbox` (action=\"list\" with a query), then action=\"call\".\n\n**1. Compression is for YOUR context only.** NEVER hand a compressed version to the user \u2014 they get the real, complete file, comments and imports intact. If you reasoned over a squeezed version, re-fetch the original before presenting it.\n\n**2. A cache hit is a hint, not an answer.** `smarter_cache` requires similarity >= 0.8. Always tell the user the answer came from cache. If they're about to act on it (run a command, apply a patch, ship code), verify against current state first \u2014 cached answers go stale. On a MISS or any doubt, reason normally.\n\n**3. Locate before reading only when locating is cheaper.** Don't know which file? `get_ast_hologram` / `search_workspace` beats reading candidates one by one. Already know the file? Read it \u2014 a lookup round-trip on a small file costs more than the file. `read_workspace_file` carries real line numbers and takes offset/limit, so prefer a targeted range on large files.\n\n**4. store_memory only after verification.** Store once the fix actually worked (tests pass, build succeeded, user confirmed) \u2014 not your first attempt. A wrong answer stored here becomes a false cache hit for a future session, and unwinding that costs far more than it saved.\n\n**5. Never trade a required disclosure for brevity.** A cache-hit source or a risky/irreversible action is not filler. One short line.\n\n**6. File-dependent questions \u2192 state_hash_cache, not smarter_cache.** It only hits when the referenced files' hashes are unchanged: a guaranteed-valid answer instead of a similarity guess. Look up before, store after. Use `smarter_cache` only for file-independent questions.\n\n**7. Edit in place.** `apply_workspace_patch` or `surgical_ast_insert` over rewriting a file with `write_workspace_file`; reserve the full write for renames, structural rewrites and new files. For mechanical work, `get_routing_advice` says whether a cheaper model suffices.\n\n**8. \"How much did we save\" \u2192 token_receipt, don't estimate.** It returns a real auditable log. Show the byType breakdown so the user can verify it.\n\n**9. What the toolbox hands you is a candidate, not a verdict.** Auto-fixes, cached patterns and Brain-suggested patches are suggestions. Check a fix addresses the actual failing assertion before applying it.\n\n## SCORING\nCorrectness first, then cost \u2014 a fast wrong answer costs more than a slow right one once you count the round-trip to fix it. A verified cache hit is best; a MISS followed by reasoning from scratch is the normal GOOD outcome, not a failure. A cached or auto-fixed result delivered without disclosure or verification is a FAIL regardless of tokens saved.\n\nFewer tokens for the SAME quality of answer \u2014 never a worse answer for fewer tokens.";
3
12
  export declare function setupPromptsHandlers(server: Server): void;
4
13
  //# sourceMappingURL=prompts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../src/mcp/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAGnE,eAAO,MAAM,sBAAsB,23NAsFqF,CAAC;AAEzH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CA8BzD"}
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../../src/mcp/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAGnE;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,slHA6BkD,CAAC;AAEtF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CA8BzD"}
@@ -1,91 +1,43 @@
1
1
  import { GetPromptRequestSchema, ListPromptsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
2
- export const LEMMA_TURBOMODE_PROMPT = `You are operating in LEMMA TURBOMODE. Your goal: reduce what enters the model's context — file reads, command output, and repeated reasoning — without ever costing the user a correct answer. Every reduction is recorded in an auditable ledger that also records what Lemma spent to achieve it.
2
+ /**
3
+ * This text is injected into the system prompt and re-sent on every turn of the session, so
4
+ * its length is a recurring charge against the very savings it asks for. It is kept to the
5
+ * rules that change behaviour or protect correctness; anything that only restates good default
6
+ * behaviour (be concise, don't pad) was removed rather than paid for on every request.
7
+ *
8
+ * The full rationale and scoring model live in the `lemma-turbomode` prompt resource, which
9
+ * the client can fetch on demand instead of carrying permanently.
10
+ */
11
+ export const LEMMA_TURBOMODE_PROMPT = `You are operating in LEMMA TURBOMODE. Reduce what enters the model's context — file reads, command output, repeated reasoning — without ever costing the user a correct answer. Lemma's own overhead (this prompt + every tool schema) is charged to the ledger before a single token is saved, so optimize for real net savings, not for a multiplier.
3
12
 
4
- There is no fixed multiplier to hit. Lemma's own overhead (this prompt plus every tool schema) is charged to that ledger before a single token is saved, so a session only comes out ahead if the reductions below actually exceed it. Optimize for real net savings, not for a number.
13
+ ## RULES
5
14
 
6
- ## MANDATORY TURBOMODE RULESVIOLATE THESE AND YOU WASTE THE USER'S MONEY
15
+ **0. Tool calls cost tokens too.** A call and its result stay in the transcript and are re-sent every later turn. Expect a Lemma tool to save more than that round-trip costs on a small file or short command, reading it directly is cheaper. Never call a reporting tool on a schedule.
7
16
 
8
- ### 0. Every tool call costs tokens too
9
- A tool call and its result both stay in the transcript and are re-sent on every later turn. Before calling a Lemma tool, expect it to save more than that round-trip costs — on a small file or a short command, reading it directly is cheaper. Never call a reporting tool on a schedule: a report is not work. Call one only when the user asks how the session is going.
17
+ **0b. The advertised list is not everything Lemma has.** Situational schemas are withheld to keep per-turn cost down. Before concluding Lemma can't do something, call \`lemma_toolbox\` (action="list" with a query), then action="call".
10
18
 
11
- ### 0b. The tool list you see is not everything Lemma has
12
- Schemas for situational tools are withheld from this session to keep the per-turn cost down. When
13
- no advertised tool fits, call \`lemma_toolbox\` (action="list" with a query) before concluding
14
- Lemma can't do it, then action="call" to run what you find. Never tell the user a capability is
15
- missing without checking the toolbox first.
19
+ **1. Compression is for YOUR context only.** NEVER hand a compressed version to the user — they get the real, complete file, comments and imports intact. If you reasoned over a squeezed version, re-fetch the original before presenting it.
16
20
 
17
- ### 1. Compression is for YOUR context only never for code shown to the user
18
- Lemma's compressors (turbosqueeze, wormhole_squeeze, squeeze_prompt — via the toolbox) exist to
19
- shrink code before it enters your own reasoning window. NEVER hand a compressed version to the
20
- user: they get the real, complete file (comments, imports, formatting intact) so it stays
21
- copy-pasteable and correct. If you reasoned over a squeezed version, re-fetch the untouched
22
- original before presenting it.
21
+ **2. A cache hit is a hint, not an answer.** \`smarter_cache\` requires similarity >= 0.8. Always tell the user the answer came from cache. If they're about to act on it (run a command, apply a patch, ship code), verify against current state first — cached answers go stale. On a MISS or any doubt, reason normally.
23
22
 
24
- ### 2. smarter_cache (threshold 0.8) is a hint, not an answer
25
- Call \`smarter_cache\` with the user's question before reasoning from scratch — a hit can save
26
- time. But NEVER return a cache hit verbatim as if freshly reasoned:
27
- - Require similarity >= 0.8 (raised from 0.75 — 0.75 was matching questions that weren't
28
- actually the same question).
29
- - Always tell the user the answer came from cache (e.g. "from a cached earlier answer:").
30
- - If the user is about to act on the answer (run a command, apply a patch, ship code), verify
31
- it against current file/tool state before using it — cached answers can be stale.
32
- - On a MISS, or on any doubt, reason normally.
23
+ **3. Locate before reading only when locating is cheaper.** Don't know which file? \`get_ast_hologram\` / \`search_workspace\` beats reading candidates one by one. Already know the file? Read it — a lookup round-trip on a small file costs more than the file. \`read_workspace_file\` carries real line numbers and takes offset/limit, so prefer a targeted range on large files.
33
24
 
34
- ### 3. Before reading files call get_ast_hologram
35
- NEVER read files blindly. Always call \`get_ast_hologram\` first to locate the exact symbols you need. Then read only those specific files. Each unnecessary file read wastes 1000+ tokens.
25
+ **4. store_memory only after verification.** Store once the fix actually worked (tests pass, build succeeded, user confirmed) — not your first attempt. A wrong answer stored here becomes a false cache hit for a future session, and unwinding that costs far more than it saved.
36
26
 
37
- ### 4. After every VERIFIED solution call store_memory automatically
38
- Store via \`store_memory\` once you've confirmed the fix/answer actually worked (tests pass,
39
- the user confirmed, the build succeeded) — not the first thing you tried. A wrong answer stored
40
- here becomes a "cache hit" served to a future session, and unwinding that costs far more than
41
- the tokens it saved. Verified answers are still free to cache aggressively.
27
+ **5. Never trade a required disclosure for brevity.** A cache-hit source or a risky/irreversible action is not filler. One short line.
42
28
 
43
- ### 5. Answer concisely no fluff, but never at the cost of a required disclosure
44
- - No "Sure, I can help you with that" — just do it.
45
- - No unnecessary explanation of what you did — unless asked.
46
- - No repeating the user's question back to them.
47
- - Exception: disclosures required elsewhere in these rules (cache-hit source per rule 2,
48
- a risky/irreversible action) are never fluff — say them in one short line. Conciseness trims
49
- filler, not information the user needs to trust the answer.
29
+ **6. File-dependent questions state_hash_cache, not smarter_cache.** It only hits when the referenced files' hashes are unchanged: a guaranteed-valid answer instead of a similarity guess. Look up before, store after. Use \`smarter_cache\` only for file-independent questions.
50
30
 
51
- ### 6. For questions tied to specific files prefer state_hash_cache over smarter_cache
52
- If the question depends on the current content of specific files (not just "similar wording"),
53
- call \`state_hash_cache\` (action="lookup") instead of \`smarter_cache\`. It only returns a hit
54
- when the referenced files' content hashes are unchanged — a guaranteed-valid answer, not a
55
- similarity guess. After answering, call it again with action="store" to cache it. Use
56
- \`smarter_cache\` only for file-independent questions (explanations, general how-tos).
31
+ **7. Edit in place.** \`apply_workspace_patch\` or \`surgical_ast_insert\` over rewriting a file with \`write_workspace_file\`; reserve the full write for renames, structural rewrites and new files. For mechanical work, \`get_routing_advice\` says whether a cheaper model suffices.
57
32
 
58
- ### 7. Route model choice through get_routing_advice, don't assume the biggest model
59
- Before a mechanical/low-complexity task (formatting, boilerplate, simple lookups), call
60
- \`get_routing_advice\` with the task prompt. If it recommends a cheaper model and the current
61
- provider supports switching, say so instead of silently burning a large-model budget on trivial work.
33
+ **8. "How much did we save" → token_receipt, don't estimate.** It returns a real auditable log. Show the byType breakdown so the user can verify it.
62
34
 
63
- ### 8. Default all file edits to symbol-level patches via surgical_ast_insert
64
- When adding a method/function/property to an existing class, interface, or file, prefer
65
- \`surgical_ast_insert\` over regenerating and rewriting the whole file. Only fall back to a full
66
- \`write_workspace_file\` when the change isn't a clean insertion (renames, structural rewrites,
67
- new files).
35
+ **9. What the toolbox hands you is a candidate, not a verdict.** Auto-fixes, cached patterns and Brain-suggested patches are suggestions. Check a fix addresses the actual failing assertion before applying it.
68
36
 
69
- ### 9. When asked "how much did we actually save" → call token_receipt, don't estimate
70
- \`token_receipt\` returns a real, auditable log of what happened this session (exact cache hits,
71
- semantic cache hits, file reads, fresh reasoning) — not a projected percentage. Use it instead of
72
- inventing a savings number, and show the byType breakdown so the user can verify it themselves.
37
+ ## SCORING
38
+ Correctness first, then cost — a fast wrong answer costs more than a slow right one once you count the round-trip to fix it. A verified cache hit is best; a MISS followed by reasoning from scratch is the normal GOOD outcome, not a failure. A cached or auto-fixed result delivered without disclosure or verification is a FAIL regardless of tokens saved.
73
39
 
74
- ### 10. Anything the toolbox hands you is still a candidate, not a verdict
75
- Tools reached via \`lemma_toolbox\` (auto-fixes for failing tests, cached prompt patterns,
76
- Brain-suggested patches) return suggestions. Check a suggested fix actually addresses the failing
77
- assertion before applying it — a wrong auto-fix applied blind costs far more (broken build,
78
- debugging round-trip) than the tokens saved by skipping the check.
79
-
80
- ## TURBOMODE SCORING
81
- Correctness first, then cost — a fast wrong answer is more expensive than a slow right one once
82
- you count the round-trip to find and fix it.
83
- - Verified cache hit (state_hash_cache, or a disclosed smarter_cache hit above threshold) = best outcome: correct AND ~0 tokens spent.
84
- - Cache MISS + reasoning from scratch = the normal, GOOD outcome whenever no verified cache exists. Reasoning is not a failure mode — it's what you do when you don't already have a trustworthy answer.
85
- - Reading full files without checking get_ast_hologram first = wasteful, avoid it when a cheaper lookup would've told you what you needed.
86
- - Any answer delivered without required disclosure (rule 6's exception), or a cached/auto-fixed result used without verification = FAIL, regardless of tokens saved — it can cost the user far more than tokens once it's wrong.
87
-
88
- Remember: the goal is spending fewer tokens on the SAME quality of answer, not a lower-quality answer for fewer tokens.`;
40
+ Fewer tokens for the SAME quality of answer never a worse answer for fewer tokens.`;
89
41
  export function setupPromptsHandlers(server) {
90
42
  server.setRequestHandler(ListPromptsRequestSchema, async () => {
91
43
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../../src/mcp/prompts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,oCAAoC,CAAC;AAEtG,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wHAsFkF,CAAC;AAEzH,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,MAAM,CAAC,iBAAiB,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QAC5D,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,iBAAiB;oBACvB,WAAW,EAAE,+MAA+M;oBAC5N,SAAS,EAAE,EAAE;iBACd;aACF;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACjE,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;YAC9C,OAAO;gBACL,WAAW,EAAE,8CAA8C;gBAC3D,QAAQ,EAAE;oBACR;wBACE,IAAI,EAAE,MAAM;wBACZ,OAAO,EAAE;4BACP,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,sBAAsB;yBAC7B;qBACF;iBACF;aACF,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,mBAAmB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../../src/mcp/prompts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,oCAAoC,CAAC;AAEtG;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qFA6B+C,CAAC;AAEtF,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,MAAM,CAAC,iBAAiB,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QAC5D,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,iBAAiB;oBACvB,WAAW,EAAE,+MAA+M;oBAC5N,SAAS,EAAE,EAAE;iBACd;aACF;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACjE,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;YAC9C,OAAO;gBACL,WAAW,EAAE,8CAA8C;gBAC3D,QAAQ,EAAE;oBACR;wBACE,IAAI,EAAE,MAAM;wBACZ,OAAO,EAAE;4BACP,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,sBAAsB;yBAC7B;qBACF;iBACF;aACF,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,mBAAmB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -40,10 +40,19 @@ export interface ResolvedToolSurface<T extends {
40
40
  /** Situational groups that matched this project. */
41
41
  activeGroups: string[];
42
42
  }
43
+ export interface ResolveOptions {
44
+ /**
45
+ * Returns false for a tool this session is not licensed to run. Such a tool must not be
46
+ * advertised: its schema would be re-sent every turn purely to be refused at call time.
47
+ * It stays registered and discoverable through `lemma_toolbox`, which is where a user
48
+ * finds out the capability exists and what it takes to unlock it.
49
+ */
50
+ isEntitled?: (name: string) => boolean;
51
+ }
43
52
  export declare function resolveToolSurface<T extends {
44
53
  name: string;
45
54
  description?: string;
46
- }>(allDefinitions: T[], cwd?: string): ResolvedToolSurface<T>;
55
+ }>(allDefinitions: T[], cwd?: string, options?: ResolveOptions): ResolvedToolSurface<T>;
47
56
  /**
48
57
  * One-line-per-tool catalog of everything not advertised, so the model can find and
49
58
  * invoke a hidden tool without its schema having been resident all session.
@@ -1 +1 @@
1
- {"version":3,"file":"tool-profiles.d.ts","sourceRoot":"","sources":["../../../src/mcp/tool-profiles.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnD;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,EA0BnC,CAAC;AA4DF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,UAAoE,CAAC;AAkBnG,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE;IAC7D,OAAO,EAAE,WAAW,CAAC;IACrB,wCAAwC;IACxC,UAAU,EAAE,CAAC,EAAE,CAAC;IAChB,uFAAuF;IACvF,MAAM,EAAE,CAAC,EAAE,CAAC;IACZ,oDAAoD;IACpD,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,wBAAgB,kBAAkB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EACjF,cAAc,EAAE,CAAC,EAAE,EACnB,GAAG,GAAE,MAAsB,GAC1B,mBAAmB,CAAC,CAAC,CAAC,CAmCxB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EACrD,KAAK,CAAC,EAAE,MAAM,GACb,MAAM,CAuDR"}
1
+ {"version":3,"file":"tool-profiles.d.ts","sourceRoot":"","sources":["../../../src/mcp/tool-profiles.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnD;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,EA0BnC,CAAC;AA4DF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,UAAoE,CAAC;AAkBnG,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE;IAC7D,OAAO,EAAE,WAAW,CAAC;IACrB,wCAAwC;IACxC,UAAU,EAAE,CAAC,EAAE,CAAC;IAChB,uFAAuF;IACvF,MAAM,EAAE,CAAC,EAAE,CAAC;IACZ,oDAAoD;IACpD,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACxC;AAED,wBAAgB,kBAAkB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EACjF,cAAc,EAAE,CAAC,EAAE,EACnB,GAAG,GAAE,MAAsB,EAC3B,OAAO,GAAE,cAAmB,GAC3B,mBAAmB,CAAC,CAAC,CAAC,CA4CxB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EACrD,KAAK,CAAC,EAAE,MAAM,GACb,MAAM,CAuDR"}
@@ -105,10 +105,19 @@ function readConfiguredProfile(cwd) {
105
105
  return env;
106
106
  return "auto";
107
107
  }
108
- export function resolveToolSurface(allDefinitions, cwd = process.cwd()) {
108
+ export function resolveToolSurface(allDefinitions, cwd = process.cwd(), options = {}) {
109
109
  const profile = readConfiguredProfile(cwd);
110
+ // `lemma_toolbox` is the only route back to a withheld schema, so no entitlement check may
111
+ // ever remove it — doing so would turn every deferred tool into an unreachable one.
112
+ const entitlementCheck = options.isEntitled ?? (() => true);
113
+ const isEntitled = (name) => name === "lemma_toolbox" || entitlementCheck(name);
110
114
  if (profile === "full") {
111
- return { profile, advertised: allDefinitions, hidden: [], activeGroups: ["*"] };
115
+ const advertised = [];
116
+ const hidden = [];
117
+ for (const def of allDefinitions) {
118
+ (isEntitled(def.name) ? advertised : hidden).push(def);
119
+ }
120
+ return { profile, advertised, hidden, activeGroups: ["*"] };
112
121
  }
113
122
  // Always advertised: it is the only route back to a withheld schema, so hiding it
114
123
  // would turn a deferred tool into a lost one.
@@ -133,7 +142,7 @@ export function resolveToolSurface(allDefinitions, cwd = process.cwd()) {
133
142
  const advertised = [];
134
143
  const hidden = [];
135
144
  for (const def of allDefinitions) {
136
- (allowed.has(def.name) ? advertised : hidden).push(def);
145
+ (allowed.has(def.name) && isEntitled(def.name) ? advertised : hidden).push(def);
137
146
  }
138
147
  return { profile, advertised, hidden, activeGroups };
139
148
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tool-profiles.js","sourceRoot":"","sources":["../../../src/mcp/tool-profiles.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tool-profiles.js","sourceRoot":"","sources":["../../../src/mcp/tool-profiles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAsBxB;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAa;IACvC,8BAA8B;IAC9B,qBAAqB;IACrB,oBAAoB;IACpB,kBAAkB;IAClB,kBAAkB;IAClB,uBAAuB;IACvB,eAAe;IACf,oDAAoD;IACpD,uBAAuB;IACvB,sBAAsB;IACtB,uBAAuB;IACvB,qBAAqB;IACrB,wBAAwB;IACxB,wCAAwC;IACxC,kBAAkB;IAClB,6BAA6B;IAC7B,wBAAwB;IACxB,6CAA6C;IAC7C,kBAAkB;IAClB,eAAe;IACf,eAAe;IACf,cAAc;IACd,uBAAuB;IACvB,eAAe;IACf,oBAAoB;CACrB,CAAC;AAaF,MAAM,MAAM,GAAG,CAAC,GAAW,EAAE,GAAG,GAAa,EAAW,EAAE,CACxD,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;IACb,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,4FAA4F;AAC5F,SAAS,YAAY,CAAC,GAAW,EAAE,GAAW,EAAE,KAAK,GAAG,CAAC;IACvD,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,IAAY,EAAW,EAAE;QAClD,IAAI,OAAoB,CAAC;QACzB,IAAI,CAAC;YACH,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc;gBAAE,SAAS;YAC1E,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC5D,IAAI,KAAK,CAAC,WAAW,EAAE,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC;QACjG,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IACF,OAAO,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,WAAW,GAAgB;IAC/B;QACE,IAAI,EAAE,OAAO;QACb,KAAK,EAAE;YACL,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,oBAAoB;YACvE,gBAAgB,EAAE,wBAAwB,EAAE,qBAAqB;YACjE,2BAA2B,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,cAAc;YAC5F,wBAAwB,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY;YAC7E,cAAc,EAAE,eAAe;YAC/B,gBAAgB,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,iBAAiB;SAC/E;QACD,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACf,+EAA+E;QAC/E,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,EAAE,gCAAgC,CAAC;YAChE,MAAM,CAAC,GAAG,EAAE,aAAa,EAAE,oBAAoB,EAAE,YAAY,EAAE,WAAW,CAAC;YAC3E,YAAY,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;KAC9B;CACF,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,sBAAsB,CAAC,CAAC;AAEnG,SAAS,qBAAqB,CAAC,GAAW;IACxC,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;QACvD,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;YAC/D,MAAM,OAAO,GAAG,MAAM,EAAE,GAAG,EAAE,WAAW,CAAC;YACzC,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,MAAM;gBAAE,OAAO,OAAO,CAAC;QACrF,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,sFAAsF;IACxF,CAAC;IACD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAC3C,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,MAAM;QAAE,OAAO,GAAG,CAAC;IACnE,OAAO,MAAM,CAAC;AAChB,CAAC;AAsBD,MAAM,UAAU,kBAAkB,CAChC,cAAmB,EACnB,MAAc,OAAO,CAAC,GAAG,EAAE,EAC3B,UAA0B,EAAE;IAE5B,MAAM,OAAO,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC;IAC3C,2FAA2F;IAC3F,oFAAoF;IACpF,MAAM,gBAAgB,GAAG,OAAO,CAAC,UAAU,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAC5D,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,KAAK,eAAe,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAExF,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,UAAU,GAAQ,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAQ,EAAE,CAAC;QACvB,KAAK,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;YACjC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;IAC9D,CAAC;IAED,kFAAkF;IAClF,8CAA8C;IAC9C,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,eAAe,EAAE,eAAe,CAAC,CAAC,CAAC;IAC/D,MAAM,YAAY,GAAa,EAAE,CAAC;IAElC,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE,CAAC;YAChC,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,IAAI,CAAC;gBACH,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC/B,CAAC;YAAC,MAAM,CAAC;gBACP,8EAA8E;gBAC9E,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;YACD,IAAI,OAAO,EAAE,CAAC;gBACZ,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC9B,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7C,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAQ,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAQ,EAAE,CAAC;IACvB,KAAK,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;QACjC,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClF,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;AACvD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAAqD,EACrD,KAAc;IAEd,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC7C,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAEtD,IAAI,OAAO,GAAG,MAAM,CAAC;IACrB,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,uFAAuF;QACvF,mFAAmF;QACnF,qFAAqF;QACrF,2EAA2E;QAC3E,MAAM,MAAM,GAAG,MAAM;aAClB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACT,MAAM,QAAQ,GAAG,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC,WAAW,EAAE,CAAC;YAClE,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YACpE,IAAI,KAAK,GAAG,CAAC,CAAC;YACd,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;oBAAE,KAAK,IAAI,CAAC,CAAC,CAAC,iCAAiC;qBAC7D,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC;oBAAE,KAAK,IAAI,CAAC,CAAC,CAAC,iBAAiB;YACrF,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC;QAC5B,CAAC,CAAC;aACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;aAC1B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAErC,mFAAmF;QACnF,gFAAgF;QAChF,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;QACpD,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACxF,CAAC;IAED,MAAM,aAAa,GAAG,EAAE,CAAC;IACzB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,GAAG,aAAa,CAAC,CAAC;IAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;IAE9C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,MAAM;YACjB,CAAC,CAAC,iCAAiC,KAAK,MAAM,MAAM,CAAC,MAAM,iEAAiE;YAC5H,CAAC,CAAC,2EAA2E,CAAC;IAClF,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC5B,iFAAiF;QACjF,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACrE,OAAO,KAAK,CAAC,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,GAAG,OAAO,CAAC,MAAM,gGAAgG;QACjH,kFAAkF;QAClF,EAAE;QACF,GAAG,KAAK;QACR,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,SAAS,uCAAuC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACrF,EAAE;QACF,2GAA2G;KAC5G,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAwjCnE,oFAAoF;AACpF,wBAAgB,kBAAkB,IAAI,MAAM,CAM3C;AAED,oEAAoE;AACpE,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAEjD;AAED,iEAAiE;AACjE,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAEjD;AAED,iFAAiF;AACjF,wBAAgB,qBAAqB,IAAI;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAQA;AAqHD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqCD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAC1C,IAAI,CAwEN"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AA0kCnE,oFAAoF;AACpF,wBAAgB,kBAAkB,IAAI,MAAM,CAM3C;AAED,oEAAoE;AACpE,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAEjD;AAED,iEAAiE;AACjE,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAEjD;AAED,iFAAiF;AACjF,wBAAgB,qBAAqB,IAAI;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAQA;AAqHD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqCD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAC1C,IAAI,CAwEN"}
@@ -38,6 +38,8 @@ import { distillCommandOutput, buildDistillFooter, readRegion } from "../utils/C
38
38
  import { findMatch, reindentReplacement } from "../utils/PatchMatcher.js";
39
39
  import { searchWorkspace, groupSearchResults, parseExtensionFilter } from "../utils/WorkspaceSearch.js";
40
40
  import { checkAlreadySent, recordSent } from "../utils/ReadWorkspaceCache.js";
41
+ import { mapSqueezedToOriginal, withLineNumbers, sequentialNumbers } from "../utils/LineNumberMap.js";
42
+ import { fileOutline, formatOutline } from "../utils/FileOutline.js";
41
43
  import { getCachedSearch, setCachedSearch } from "../utils/SearchCache.js";
42
44
  // ── Pro License Gate ──────────────────────────────────────────────────────────
43
45
  const LICENSE_FILE = path.join(os.homedir(), '.lemma-cache', 'license.json');
@@ -163,13 +165,16 @@ const toolDefinitions = [
163
165
  },
164
166
  {
165
167
  name: "read_workspace_file",
166
- description: "Read a file inside the workspace. Automatically compresses comments/whitespace and scrubs API keys. If this exact file (same compact mode) was already read this session and hasn't changed on disk, returns a short UNCHANGED marker instead of resending the full content — reuse your existing copy. Pass force:true to always get the full content.",
168
+ description: "Read a file inside the workspace. Compresses comments/whitespace and scrubs API keys. Every line carries its REAL line number in the original file (compression removes lines, it never renumbers them), so you can cite file:line and patch precisely without re-reading. The header lists which imports were compressed away. Use offset/limit to read a line range of a large file. If this exact view was already read this session and hasn't changed on disk, returns an UNCHANGED marker plus a symbol outline — reuse your copy, or pass force:true if it's no longer in your context.",
167
169
  inputSchema: {
168
170
  type: "object",
169
171
  properties: {
170
172
  filePath: { type: "string", description: "Path relative to the project root" },
171
173
  compact: { type: "boolean", description: "Whether to compress comments and whitespace", default: true },
172
174
  force: { type: "boolean", description: "Bypass the unchanged-file short-circuit and always return full content", default: false },
175
+ lineNumbers: { type: "boolean", description: "Prefix each line with its real line number in the original file", default: true },
176
+ offset: { type: "number", description: "First line to read (1-indexed). Omit to start at the beginning." },
177
+ limit: { type: "number", description: "How many lines to read from offset. Omit to read to the end." },
173
178
  },
174
179
  required: ["filePath"],
175
180
  },
@@ -213,7 +218,7 @@ const toolDefinitions = [
213
218
  },
214
219
  {
215
220
  name: "run_workspace_command",
216
- description: "Execute a bash command in the workspace root (15s timeout). Captures both stdout and stderr. Long test/build/lint output is distilled deterministically — for jest, vitest and tsc it returns the failing tests or diagnostics with their locations and reasons, dropping code frames and node_modules stack frames; anything else falls back to head/tail plus error-matching lines. The complete output is always stored first and the reply carries a handle: nothing is lost, use output_region to retrieve any part verbatim. Pass raw:true to skip distillation.",
221
+ description: "Execute a bash command in the workspace root. Default timeout 120s — raise it with timeoutMs for a full test or build run (max 600s). Captures both stdout and stderr, and returns whatever was produced even if the command times out. Long test/build/lint output is distilled deterministically — for jest, vitest and tsc it returns the failing tests or diagnostics with their locations and reasons, dropping code frames and node_modules stack frames; anything else falls back to head/tail plus error-matching lines. The complete output is always stored first and the reply carries a handle: nothing is lost, use output_region to retrieve any part verbatim. Pass raw:true to skip distillation.",
217
222
  inputSchema: {
218
223
  type: "object",
219
224
  properties: {
@@ -223,6 +228,11 @@ const toolDefinitions = [
223
228
  description: "Return the complete untouched output instead of the distilled view",
224
229
  default: false,
225
230
  },
231
+ timeoutMs: {
232
+ type: "number",
233
+ description: "Milliseconds before the command is killed (default 120000, max 600000)",
234
+ default: 120000,
235
+ },
226
236
  },
227
237
  required: ["command"],
228
238
  },
@@ -1044,7 +1054,15 @@ const allToolDefinitions = [
1044
1054
  ];
1045
1055
  // Narrow what gets advertised to what this project can actually use. Hidden tools stay
1046
1056
  // registered in `toolHandlers` and callable — both directly and through `lemma_toolbox`.
1047
- const _toolSurface = resolveToolSurface(allToolDefinitions, process.cwd());
1057
+ //
1058
+ // The entitlement filter matters as much as the project filter: without it a free session
1059
+ // advertised the full core surface — read, search, patch, run, AST — and refused fourteen of
1060
+ // those twenty tools at call time. That is the worst of both, paying the per-turn schema cost
1061
+ // for capabilities the session cannot use. They remain listed in `lemma_toolbox`, so the
1062
+ // capability is still discoverable and the upgrade path is still visible.
1063
+ const _toolSurface = resolveToolSurface(allToolDefinitions, process.cwd(), {
1064
+ isEntitled: (name) => isProUser() || FREE_TOOLS.has(name),
1065
+ });
1048
1066
  const toolDefinitionsArray = _toolSurface.advertised;
1049
1067
  const hiddenToolDefinitions = _toolSurface.hidden;
1050
1068
  /** Serialized size of every tool schema sent to the client, for cost accounting. */
@@ -1437,14 +1455,28 @@ async function handleReadWorkspaceFile(args) {
1437
1455
  const filePath = args?.filePath;
1438
1456
  const compact = args?.compact !== false;
1439
1457
  const force = !!args?.force;
1458
+ const lineNumbers = args?.lineNumbers !== false;
1459
+ const offset = typeof args?.offset === "number" && args.offset > 0 ? Math.floor(args.offset) : undefined;
1460
+ const limit = typeof args?.limit === "number" && args.limit > 0 ? Math.floor(args.limit) : undefined;
1440
1461
  if (!filePath)
1441
1462
  throw new Error("filePath is required");
1442
1463
  const workspaceRoot = process.cwd();
1443
1464
  try {
1444
1465
  const { resolved } = safeResolvePath(workspaceRoot, filePath);
1445
- let content = fs.readFileSync(resolved, "utf8");
1466
+ const fullContent = fs.readFileSync(resolved, "utf8");
1467
+ const view = { compact, lineNumbers, offset, limit };
1468
+ // The range is applied to the original file, before any compression: `offset` means the
1469
+ // line the user would see in an editor, not a position in the squeezed output.
1470
+ const allLines = fullContent.split("\n");
1471
+ const startIndex = offset ? Math.min(offset - 1, allLines.length) : 0;
1472
+ const endIndex = limit ? Math.min(startIndex + limit, allLines.length) : allLines.length;
1473
+ const ranged = offset || limit ? allLines.slice(startIndex, endIndex).join("\n") : fullContent;
1474
+ const rangeHeader = offset || limit
1475
+ ? `[lines ${startIndex + 1}-${endIndex} of ${allLines.length} in ${filePath}]\n`
1476
+ : "";
1477
+ let content = ranged;
1446
1478
  const originalSize = content.length;
1447
- const dedup = checkAlreadySent(resolved, compact, content);
1479
+ const dedup = checkAlreadySent(resolved, view, content);
1448
1480
  if (!force && dedup.unchanged) {
1449
1481
  reportSavings({
1450
1482
  source: "contextSqueeze",
@@ -1453,17 +1485,32 @@ async function handleReadWorkspaceFile(args) {
1453
1485
  toolName: "read_workspace_file",
1454
1486
  filePath,
1455
1487
  });
1488
+ // A hash alone is not actionable: the model's context may have been compacted since,
1489
+ // in which case it no longer holds the copy this marker tells it to reuse. The outline
1490
+ // costs ~50 tokens and is enough to either locate what it needs or decide to re-fetch.
1491
+ const outline = formatOutline(fileOutline(content), allLines.length);
1456
1492
  return {
1457
1493
  content: [
1458
1494
  {
1459
1495
  type: "text",
1460
- text: `UNCHANGED: ${filePath} is identical to what you already read this session (hash ${dedup.hash.slice(0, 12)}) — reuse your existing copy. Pass force:true to re-fetch anyway.`,
1496
+ text: [
1497
+ `UNCHANGED: ${filePath} is identical to what you already read this session (hash ${dedup.hash.slice(0, 12)}) — reuse your existing copy.`,
1498
+ `If it is no longer in your context, re-fetch with force:true. Structure:`,
1499
+ outline,
1500
+ ].join("\n"),
1461
1501
  },
1462
1502
  ],
1463
1503
  };
1464
1504
  }
1505
+ // Line numbers are computed against the pre-squeeze text and carried through, so a
1506
+ // compressed read still reports the position the code actually occupies on disk.
1507
+ let numbers = null;
1465
1508
  if (compact) {
1466
- content = squeezeCode(content);
1509
+ const squeezed = squeezeCode(content);
1510
+ if (lineNumbers) {
1511
+ numbers = mapSqueezedToOriginal(content, squeezed).map((n) => (n === null ? null : n + startIndex));
1512
+ }
1513
+ content = squeezed;
1467
1514
  if (content.length < originalSize) {
1468
1515
  reportSavings({
1469
1516
  source: "contextSqueeze",
@@ -1474,16 +1521,26 @@ async function handleReadWorkspaceFile(args) {
1474
1521
  });
1475
1522
  }
1476
1523
  }
1524
+ else if (lineNumbers) {
1525
+ numbers = sequentialNumbers(content, startIndex + 1);
1526
+ }
1477
1527
  try {
1478
1528
  const scrubber = new SemanticScrubber();
1479
1529
  const { maskedPrompt } = scrubber.mask(content);
1530
+ // Masking must not change the line count, or every number after a masked secret would
1531
+ // be off. If it ever does, drop the gutter rather than print numbers that lie.
1532
+ if (numbers && maskedPrompt.split("\n").length !== content.split("\n").length) {
1533
+ numbers = null;
1534
+ }
1480
1535
  content = maskedPrompt;
1481
1536
  }
1482
1537
  catch (err) {
1483
1538
  logWarn("read_workspace_file", "SemanticScrubber failed, returning raw content");
1484
1539
  }
1485
- recordSent(resolved, compact, dedup.hash);
1486
- return { content: [{ type: "text", text: content }] };
1540
+ if (numbers)
1541
+ content = withLineNumbers(content, numbers);
1542
+ recordSent(resolved, view, dedup.hash);
1543
+ return { content: [{ type: "text", text: rangeHeader + content }] };
1487
1544
  }
1488
1545
  catch (err) {
1489
1546
  logError("read_workspace_file", err);
@@ -1535,13 +1592,35 @@ async function handleCreateWorkspaceFile(args) {
1535
1592
  return { content: [{ type: "text", text: `Error creating file: ${err.message}` }] };
1536
1593
  }
1537
1594
  }
1595
+ /**
1596
+ * `read_workspace_file` now returns a "123| code" gutter, which makes it easy to paste a block
1597
+ * back into a patch with the numbers still attached. That block matches nothing on disk, so the
1598
+ * patch fails for a reason that has nothing to do with the edit. Strip the gutter when *every*
1599
+ * non-empty line carries one — a uniform prefix is a gutter; a lone "12| " inside real code is
1600
+ * not, and must be left alone.
1601
+ */
1602
+ function stripLineNumberGutter(text) {
1603
+ const lines = text.split("\n");
1604
+ const nonEmpty = lines.filter((l) => l.trim().length > 0);
1605
+ if (nonEmpty.length === 0)
1606
+ return text;
1607
+ const GUTTER = /^\s*\d+\|\s?/;
1608
+ if (!nonEmpty.every((l) => GUTTER.test(l)))
1609
+ return text;
1610
+ return lines.map((l) => l.replace(GUTTER, "")).join("\n");
1611
+ }
1538
1612
  async function handleApplyWorkspacePatch(args) {
1539
1613
  const filePath = args?.filePath;
1540
- const searchContent = args?.searchContent;
1541
- const replaceContent = args?.replaceContent;
1542
- if (!filePath || !searchContent || replaceContent === undefined) {
1614
+ const rawSearch = args?.searchContent;
1615
+ const rawReplace = args?.replaceContent;
1616
+ if (!filePath || !rawSearch || rawReplace === undefined) {
1543
1617
  throw new Error("filePath, searchContent, and replaceContent are required");
1544
1618
  }
1619
+ const searchContent = stripLineNumberGutter(rawSearch);
1620
+ // Only strip the replacement's gutter when the search block had one too: that pairing means
1621
+ // the model was working from a numbered read. Stripping unconditionally would mangle a
1622
+ // replacement that legitimately contains "1| " (a table, a doc block).
1623
+ const replaceContent = searchContent !== rawSearch ? stripLineNumberGutter(rawReplace) : rawReplace;
1545
1624
  const workspaceRoot = process.cwd();
1546
1625
  try {
1547
1626
  const { resolved } = safeResolvePath(workspaceRoot, filePath);
@@ -1635,32 +1714,38 @@ async function handleRunWorkspaceCommand(args) {
1635
1714
  // stdio is overridden, which silently dropped the output of every tool that reports on
1636
1715
  // stderr (jest and tsc both do) on a successful run. It also gives us stdout, stderr and
1637
1716
  // the exit code through one code path instead of a throw-on-failure split.
1717
+ // 15s was below the runtime of the commands this tool exists for: a real `npm test` or
1718
+ // `tsc --noEmit` always tripped it, so the distillation pipeline never ran on the output
1719
+ // it was built for and the call was pure overhead before falling back to a plain shell.
1720
+ const DEFAULT_TIMEOUT_MS = 120000;
1721
+ const MAX_TIMEOUT_MS = 600000;
1722
+ const requested = typeof args?.timeoutMs === "number" && args.timeoutMs > 0 ? args.timeoutMs : DEFAULT_TIMEOUT_MS;
1723
+ const timeoutMs = Math.min(Math.floor(requested), MAX_TIMEOUT_MS);
1638
1724
  const result = spawnSync(command, {
1639
1725
  cwd: workspaceRoot,
1640
1726
  shell: true,
1641
1727
  encoding: "utf8",
1642
- timeout: 15000,
1728
+ timeout: timeoutMs,
1643
1729
  maxBuffer: 20 * 1024 * 1024,
1644
1730
  env: { ...process.env },
1645
1731
  });
1646
- if (result.error) {
1647
- const timedOut = result.error.code === "ETIMEDOUT";
1732
+ const timedOut = !!result.error && result.error.code === "ETIMEDOUT";
1733
+ if (result.error && !timedOut) {
1648
1734
  return {
1649
- content: [
1650
- {
1651
- type: "text",
1652
- text: timedOut
1653
- ? `Command timed out after 15s: ${command}`
1654
- : `Command could not be executed: ${result.error.message}`,
1655
- },
1656
- ],
1735
+ content: [{ type: "text", text: `Command could not be executed: ${result.error.message}` }],
1657
1736
  };
1658
1737
  }
1659
1738
  const stdout = result.stdout || "";
1660
1739
  const stderr = result.stderr || "";
1661
1740
  const combined = [stdout, stderr].filter((s) => s.trim().length > 0).join("\n");
1662
1741
  const status = result.status ?? 0;
1663
- const header = status === 0 ? "Command completed successfully." : `Command failed with exit code ${status}.`;
1742
+ // A timed-out run still produced output, and that partial output is normally where the
1743
+ // failure is. Discarding it forced the whole command to be re-run somewhere else.
1744
+ const header = timedOut
1745
+ ? `Command timed out after ${Math.round(timeoutMs / 1000)}s (killed): ${command}\nOutput produced before the kill is below — it may be incomplete. Re-run with a higher timeoutMs if it was still making progress.`
1746
+ : status === 0
1747
+ ? "Command completed successfully."
1748
+ : `Command failed with exit code ${status}.`;
1664
1749
  if (!combined.trim()) {
1665
1750
  return { content: [{ type: "text", text: `${header}\n\n(no output)` }] };
1666
1751
  }