ctxloom-pro 1.2.6 → 1.3.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.
package/README.md
CHANGED
|
@@ -502,6 +502,83 @@ Pass `--no-git` to disable the overlay entirely. Tools degrade gracefully — th
|
|
|
502
502
|
|
|
503
503
|
---
|
|
504
504
|
|
|
505
|
+
## Response Budgets (v1.2.7+)
|
|
506
|
+
|
|
507
|
+
Twelve source-returning tools accept a server-enforced **token budget**. When a response would exceed the budget, the server auto-substitutes a lighter form (Skeletonizer signature view, summary-only XML, or paths-without-snippets) instead of dumping 50KB of source into your context window.
|
|
508
|
+
|
|
509
|
+
### Opting in
|
|
510
|
+
|
|
511
|
+
Pass any of these three optional fields to any of the 12 supported tools:
|
|
512
|
+
|
|
513
|
+
```json
|
|
514
|
+
{
|
|
515
|
+
"max_response_tokens": 4000,
|
|
516
|
+
"on_budget_exceeded": "skeleton",
|
|
517
|
+
"response_format": "auto"
|
|
518
|
+
}
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
| Field | Values | Default |
|
|
522
|
+
|---|---|---|
|
|
523
|
+
| `max_response_tokens` | positive integer | per-tool (see below) |
|
|
524
|
+
| `on_budget_exceeded` | `"skeleton"` \| `"truncate"` \| `"error"` | `"skeleton"` |
|
|
525
|
+
| `response_format` | `"full"` \| `"skeleton"` \| `"auto"` | `"auto"` |
|
|
526
|
+
|
|
527
|
+
**Back-compat:** when none of these fields are passed, the tool returns its raw response unchanged. Existing callers see zero behavior change.
|
|
528
|
+
|
|
529
|
+
### Response envelope
|
|
530
|
+
|
|
531
|
+
When you opt in, the response is wrapped in a JSON envelope:
|
|
532
|
+
|
|
533
|
+
```json
|
|
534
|
+
{
|
|
535
|
+
"data": "<the actual tool output — XML, text, or whatever the tool returns>",
|
|
536
|
+
"meta": {
|
|
537
|
+
"format": "full" | "skeleton" | "truncated",
|
|
538
|
+
"original_tokens_est": 8400,
|
|
539
|
+
"returned_tokens_est": 1600,
|
|
540
|
+
"fallback_reason": null | "budget_exceeded" | "minified_input" | "size_cap" | "skeleton_failed"
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
### Supported tools + default budgets
|
|
546
|
+
|
|
547
|
+
Defaults activate only when you opt in (any of the 3 fields above) without specifying `max_response_tokens` explicitly.
|
|
548
|
+
|
|
549
|
+
| Tool | Default | Skeleton fallback |
|
|
550
|
+
|---|---:|---|
|
|
551
|
+
| `ctx_get_file` | 8000 | Skeletonizer view of the file (~90% reduction on TS) |
|
|
552
|
+
| `ctx_get_context_packet` | 6000 | Re-render with the primary file skeletonized |
|
|
553
|
+
| `ctx_get_definition` | 2000 | none — truncate-only (already structural) |
|
|
554
|
+
| `ctx_git_diff_review` | 8000 | Drop `<skeleton>` blocks + omit transitive importers |
|
|
555
|
+
| `ctx_search` | 4000 | Drop content snippets (paths + scores only) |
|
|
556
|
+
| `ctx_full_text_search` | 4000 | Drop match snippets (paths + match counts only) |
|
|
557
|
+
| `ctx_wiki_generate` | 12000 | Downgrade to `detail_level=minimal` |
|
|
558
|
+
| `ctx_find_large_functions` | 2000 | none — truncate-only |
|
|
559
|
+
| `ctx_apply_refactor` | 2000 | none — truncate-only |
|
|
560
|
+
| `ctx_refactor_preview` | 4000 | Drop per-change before/after, keep file summary |
|
|
561
|
+
| `ctx_cross_repo_search` | 4000 | Drop content snippets |
|
|
562
|
+
| `ctx_execution_flow` | 4000 | none — truncate-only |
|
|
563
|
+
|
|
564
|
+
Defaults are **provisional** (derived from the issue's initial table); a future release will re-derive them from real per-tool p75 telemetry once enough usage data accumulates.
|
|
565
|
+
|
|
566
|
+
### Token estimator
|
|
567
|
+
|
|
568
|
+
Default = `chars / 4` — within ±10% of GPT/Claude tokenizers on code with zero tokenization cost. Pluggable per-tool via the `estimator` option on `BudgetOptions` for callers that need accuracy-critical estimation (e.g. tiktoken).
|
|
569
|
+
|
|
570
|
+
### Kill switch
|
|
571
|
+
|
|
572
|
+
Set `CTXLOOM_DISABLE_BUDGET=1` in the environment to silently ignore every `max_response_tokens` arg server-wide. Tools behave exactly as in pre-v1.2.7. Documented escape hatch for the soak period.
|
|
573
|
+
|
|
574
|
+
### Telemetry
|
|
575
|
+
|
|
576
|
+
Set `CTXLOOM_TELEMETRY_LEVEL=full` to emit structured `mcp.budget.exceeded` and `mcp.fallback.used` events to stderr. Useful for tuning defaults against your own usage patterns.
|
|
577
|
+
|
|
578
|
+
> **Note:** `CTXLOOM_TELEMETRY_LEVEL` is also consumed by the license / PostHog telemetry layer (see [Telemetry](#telemetry) below) which only recognizes `all` / `error` / `off`. `full` is a separate, **additive** level — it enables budget-event emission *without narrowing* PostHog scope. To narrow PostHog telemetry, set the variable to `error` or `off`; those values disable budget events as a side effect.
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
505
582
|
## CLI Commands
|
|
506
583
|
|
|
507
584
|
```
|
|
@@ -8290,16 +8290,29 @@ init_logger();
|
|
|
8290
8290
|
|
|
8291
8291
|
// ../../packages/core/src/tools/search.ts
|
|
8292
8292
|
init_embedder();
|
|
8293
|
+
|
|
8294
|
+
// ../../packages/core/src/budget/budget.ts
|
|
8295
|
+
init_logger();
|
|
8296
|
+
|
|
8297
|
+
// ../../packages/core/src/tools/search.ts
|
|
8293
8298
|
var Schema = external_exports.object({
|
|
8294
8299
|
query: external_exports.string().describe("Search query \u2014 natural language or code fragment"),
|
|
8295
8300
|
limit: external_exports.number().max(100).optional().default(10).describe("Maximum results to return"),
|
|
8296
|
-
project_root: ProjectRootField
|
|
8301
|
+
project_root: ProjectRootField,
|
|
8302
|
+
// ─── Phase B2 budget surface ──
|
|
8303
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 4000 (when budget surface is opted into). Over-budget rebuilds the result list without the content snippets (paths + scores only)."),
|
|
8304
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton' (default) drops snippets; 'truncate' slices the raw XML; 'error' throws."),
|
|
8305
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'skeleton' forces the path-and-score-only view; 'full'/'auto' lets the budget decide.")
|
|
8297
8306
|
});
|
|
8298
8307
|
|
|
8299
8308
|
// ../../packages/core/src/tools/file.ts
|
|
8300
8309
|
var Schema2 = external_exports.object({
|
|
8301
8310
|
path: external_exports.string().describe("Relative path to the file"),
|
|
8302
|
-
project_root: ProjectRootField
|
|
8311
|
+
project_root: ProjectRootField,
|
|
8312
|
+
// ─── Phase B2 budget surface (all optional; back-compat preserved) ──
|
|
8313
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget in tokens. Falls back to a skeleton when exceeded. Default: 8000 (when budget surface is opted into)."),
|
|
8314
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when the response would exceed max_response_tokens. 'skeleton' (default) substitutes a Skeletonizer signature view; 'truncate' slices the raw text; 'error' throws a structured error with token counts so the caller can re-ask."),
|
|
8315
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'skeleton' forces a Skeletonizer view regardless of budget; 'full'/'auto' lets the budget decide.")
|
|
8303
8316
|
});
|
|
8304
8317
|
|
|
8305
8318
|
// ../../packages/core/src/tools/context-packet.ts
|
|
@@ -8307,7 +8320,11 @@ import path16 from "path";
|
|
|
8307
8320
|
var Schema3 = external_exports.object({
|
|
8308
8321
|
target_file: external_exports.string().describe("Relative path to the primary file"),
|
|
8309
8322
|
mode: external_exports.enum(["edit", "read"]).optional().default("edit").describe("Context mode"),
|
|
8310
|
-
project_root: ProjectRootField
|
|
8323
|
+
project_root: ProjectRootField,
|
|
8324
|
+
// ─── Phase B2 budget surface ──
|
|
8325
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 6000 (when budget surface is opted into). Over-budget rebuilds the packet with the primary file replaced by its skeleton."),
|
|
8326
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton' (default) re-renders the packet with the primary file skeletonized; 'truncate' slices the raw envelope; 'error' throws."),
|
|
8327
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'skeleton' forces the skeletonized-primary packet; 'full'/'auto' lets the budget decide.")
|
|
8311
8328
|
});
|
|
8312
8329
|
|
|
8313
8330
|
// ../../packages/core/src/tools/findCallers.ts
|
|
@@ -8325,7 +8342,11 @@ var Schema4 = external_exports.object({
|
|
|
8325
8342
|
// ../../packages/core/src/tools/definition.ts
|
|
8326
8343
|
var Schema5 = external_exports.object({
|
|
8327
8344
|
symbol: external_exports.string().describe("Symbol name to look up"),
|
|
8328
|
-
project_root: ProjectRootField
|
|
8345
|
+
project_root: ProjectRootField,
|
|
8346
|
+
// ─── Phase B2 budget surface (all optional; back-compat preserved) ──
|
|
8347
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget in tokens. Default: 2000 (when budget surface is opted into). No skeleton fallback for this tool \u2014 the response is structural metadata; over-budget falls back to truncation."),
|
|
8348
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton'/'truncate' both slice the XML (no file context to skeletonize from); 'error' throws."),
|
|
8349
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'full'/'auto' default. 'skeleton' is accepted for consistency but produces the same output as 'full' here \u2014 the response is already a compact symbol list.")
|
|
8329
8350
|
});
|
|
8330
8351
|
|
|
8331
8352
|
// ../../packages/core/src/tools/rules.ts
|
|
@@ -8445,7 +8466,11 @@ var Schema15 = external_exports.object({
|
|
|
8445
8466
|
detail_level: external_exports.enum(["standard", "minimal"]).default("standard").describe(
|
|
8446
8467
|
'"standard" (default) lists each written page with size. "minimal" returns counts only.'
|
|
8447
8468
|
),
|
|
8448
|
-
project_root: ProjectRootField
|
|
8469
|
+
project_root: ProjectRootField,
|
|
8470
|
+
// ─── Phase B2 budget surface ──
|
|
8471
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 12000 (when opted in). Over-budget re-renders at detail_level=minimal (counts only) \u2014 the wiki files themselves are unaffected."),
|
|
8472
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton' (default) downgrades to minimal output; 'truncate' slices; 'error' throws."),
|
|
8473
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'skeleton' forces minimal output regardless of budget; 'full'/'auto' lets the budget decide.")
|
|
8449
8474
|
});
|
|
8450
8475
|
|
|
8451
8476
|
// ../../packages/core/src/tools/graph-export.ts
|
|
@@ -8473,7 +8498,11 @@ var Schema17 = external_exports.object({
|
|
|
8473
8498
|
max_diff_lines: external_exports.number().min(10).max(2e3).optional().default(300).describe(
|
|
8474
8499
|
"Max diff lines to include per file (default: 300)"
|
|
8475
8500
|
),
|
|
8476
|
-
project_root: ProjectRootField
|
|
8501
|
+
project_root: ProjectRootField,
|
|
8502
|
+
// ─── Phase B2 budget surface ──
|
|
8503
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 8000 (when opted in). Over-budget re-renders without <skeleton> blocks and without the transitive_importers section \u2014 keeps diffs, direct importers, and call sites."),
|
|
8504
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton' (default) drops skeletons + transitive importers; 'truncate' slices; 'error' throws."),
|
|
8505
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'skeleton' forces the lighter view; 'full'/'auto' lets the budget decide.")
|
|
8477
8506
|
});
|
|
8478
8507
|
|
|
8479
8508
|
// ../../packages/core/src/tools/refactor-preview.ts
|
|
@@ -8485,7 +8514,11 @@ var Schema18 = external_exports.object({
|
|
|
8485
8514
|
max_files: external_exports.number().min(1).max(200).optional().default(50).describe(
|
|
8486
8515
|
"Maximum number of files to scan for occurrences (default: 50)"
|
|
8487
8516
|
),
|
|
8488
|
-
project_root: ProjectRootField
|
|
8517
|
+
project_root: ProjectRootField,
|
|
8518
|
+
// ─── Phase B2 budget surface ──
|
|
8519
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 4000 (when opted in). Over-budget drops the per-change before/after lines; keeps the file+occurrence summary so callers can decide which files to drill into."),
|
|
8520
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton' (default) drops change details; 'truncate' slices; 'error' throws."),
|
|
8521
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'skeleton' forces the summary-only view; 'full'/'auto' lets the budget decide.")
|
|
8489
8522
|
});
|
|
8490
8523
|
|
|
8491
8524
|
// ../../packages/core/src/tools/execution-flow.ts
|
|
@@ -8498,7 +8531,11 @@ var Schema19 = external_exports.object({
|
|
|
8498
8531
|
max_nodes: external_exports.number().min(1).max(200).optional().default(50).describe(
|
|
8499
8532
|
"Max total steps to include in output (default: 50)"
|
|
8500
8533
|
),
|
|
8501
|
-
project_root: ProjectRootField
|
|
8534
|
+
project_root: ProjectRootField,
|
|
8535
|
+
// ─── Phase B2 budget surface ──
|
|
8536
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 4000 (when opted in). No skeleton fallback \u2014 response is already a bounded step list; over-budget falls through to truncation."),
|
|
8537
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton'/'truncate' both slice; 'error' throws."),
|
|
8538
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'full'/'auto' default; 'skeleton' same output.")
|
|
8502
8539
|
});
|
|
8503
8540
|
|
|
8504
8541
|
// ../../packages/core/src/tools/cross-repo-search.ts
|
|
@@ -8515,7 +8552,11 @@ var Schema20 = external_exports.object({
|
|
|
8515
8552
|
repos: external_exports.array(external_exports.string()).optional().describe(
|
|
8516
8553
|
"Specific repo root paths to search. Omit to search all registered repos."
|
|
8517
8554
|
),
|
|
8518
|
-
project_root: ProjectRootField
|
|
8555
|
+
project_root: ProjectRootField,
|
|
8556
|
+
// ─── Phase B2 budget surface ──
|
|
8557
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 4000 (when opted in). Over-budget drops content snippets (repo + path + score only)."),
|
|
8558
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton' (default) drops content snippets; 'truncate' slices; 'error' throws."),
|
|
8559
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'skeleton' forces the path-and-score-only view; 'full'/'auto' lets the budget decide.")
|
|
8519
8560
|
});
|
|
8520
8561
|
|
|
8521
8562
|
// ../../packages/core/src/tools/apply-refactor.ts
|
|
@@ -8530,7 +8571,11 @@ var Schema21 = external_exports.object({
|
|
|
8530
8571
|
max_files: external_exports.number().min(1).max(200).optional().default(50).describe(
|
|
8531
8572
|
"Maximum candidate files to process (default: 50)"
|
|
8532
8573
|
),
|
|
8533
|
-
project_root: ProjectRootField
|
|
8574
|
+
project_root: ProjectRootField,
|
|
8575
|
+
// ─── Phase B2 budget surface ──
|
|
8576
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 2000 (when opted in). No skeleton fallback \u2014 response is already compact; over-budget falls through to truncation."),
|
|
8577
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton'/'truncate' both slice the XML; 'error' throws."),
|
|
8578
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'full'/'auto' default; 'skeleton' produces the same output.")
|
|
8534
8579
|
});
|
|
8535
8580
|
|
|
8536
8581
|
// ../../packages/core/src/tools/detect-changes.ts
|
|
@@ -8557,7 +8602,11 @@ var Schema23 = external_exports.object({
|
|
|
8557
8602
|
case_sensitive: external_exports.boolean().optional().default(false),
|
|
8558
8603
|
limit: external_exports.number().min(1).max(100).optional().default(20),
|
|
8559
8604
|
context_lines: external_exports.number().min(0).max(5).optional().default(1),
|
|
8560
|
-
project_root: ProjectRootField
|
|
8605
|
+
project_root: ProjectRootField,
|
|
8606
|
+
// ─── Phase B2 budget surface ──
|
|
8607
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 4000 (when budget surface is opted into). Over-budget rebuilds the result list without match snippets (paths + match counts only)."),
|
|
8608
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton' (default) drops snippets; 'truncate' slices the raw XML; 'error' throws."),
|
|
8609
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'skeleton' forces the path-and-count-only view; 'full'/'auto' lets the budget decide.")
|
|
8561
8610
|
});
|
|
8562
8611
|
|
|
8563
8612
|
// ../../packages/core/src/tools/suggested-questions.ts
|
|
@@ -8610,7 +8659,11 @@ var schema3 = external_exports.object({
|
|
|
8610
8659
|
limit: external_exports.number().int().min(1).max(200).default(30).describe(
|
|
8611
8660
|
"Maximum results to return (default: 30)."
|
|
8612
8661
|
),
|
|
8613
|
-
project_root: ProjectRootField
|
|
8662
|
+
project_root: ProjectRootField,
|
|
8663
|
+
// ─── Phase B2 budget surface ──
|
|
8664
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 2000 (when opted in). No skeleton fallback \u2014 response is already structural; over-budget falls through to truncation."),
|
|
8665
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton'/'truncate' both slice the XML; 'error' throws."),
|
|
8666
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'full'/'auto' default; 'skeleton' produces the same output (response is already compact).")
|
|
8614
8667
|
});
|
|
8615
8668
|
|
|
8616
8669
|
// ../../packages/core/src/tools/git-coupling.ts
|
|
@@ -11369,7 +11422,7 @@ function resolveTelemetryLevel() {
|
|
|
11369
11422
|
}
|
|
11370
11423
|
var TELEMETRY_LEVEL = resolveTelemetryLevel();
|
|
11371
11424
|
var TELEMETRY_DISABLED = TELEMETRY_LEVEL === "off";
|
|
11372
|
-
var CTXLOOM_VERSION = "1.
|
|
11425
|
+
var CTXLOOM_VERSION = "1.3.0".length > 0 ? "1.3.0" : "dev";
|
|
11373
11426
|
var POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
11374
11427
|
var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (true ? "phc_CiDkmFLcZ2K6uCpcoSUQLmFrnnUvsyXGhSxopX5TVKE6" : "");
|
|
11375
11428
|
var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (true ? "https://81c94a0f04a8e242dee493ac1e17f733@o4508531702497280.ingest.de.sentry.io/4511256875368528" : "");
|