@smeltjs/core 0.5.0 → 0.6.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/dist/apply.d.ts.map +1 -1
- package/dist/apply.js +8 -2
- package/dist/apply.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +4 -2
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/report.d.ts +10 -1
- package/dist/cli/report.d.ts.map +1 -1
- package/dist/cli/report.js +16 -1
- package/dist/cli/report.js.map +1 -1
- package/dist/cli/subcommands/flags.d.ts +3 -0
- package/dist/cli/subcommands/flags.d.ts.map +1 -1
- package/dist/cli/subcommands/flags.js +16 -3
- package/dist/cli/subcommands/flags.js.map +1 -1
- package/dist/cli/subcommands/smelt.d.ts +3 -0
- package/dist/cli/subcommands/smelt.d.ts.map +1 -1
- package/dist/cli/subcommands/smelt.js +5 -1
- package/dist/cli/subcommands/smelt.js.map +1 -1
- package/dist/cli/subcommands/stats.d.ts +8 -3
- package/dist/cli/subcommands/stats.d.ts.map +1 -1
- package/dist/cli/subcommands/stats.js +15 -5
- package/dist/cli/subcommands/stats.js.map +1 -1
- package/dist/errors.d.ts +10 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +10 -0
- package/dist/errors.js.map +1 -1
- package/dist/hooks/focus-terms.d.ts +57 -0
- package/dist/hooks/focus-terms.d.ts.map +1 -0
- package/dist/hooks/focus-terms.js +230 -0
- package/dist/hooks/focus-terms.js.map +1 -0
- package/dist/hooks/guard-core.d.ts +8 -16
- package/dist/hooks/guard-core.d.ts.map +1 -1
- package/dist/hooks/guard-core.js +22 -120
- package/dist/hooks/guard-core.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/ops/index.d.ts +3 -3
- package/dist/ops/index.d.ts.map +1 -1
- package/dist/ops/index.js +2 -2
- package/dist/ops/index.js.map +1 -1
- package/dist/ops/verbs.d.ts +57 -1
- package/dist/ops/verbs.d.ts.map +1 -1
- package/dist/ops/verbs.js +49 -1
- package/dist/ops/verbs.js.map +1 -1
- package/dist/plan/auto.d.ts +9 -2
- package/dist/plan/auto.d.ts.map +1 -1
- package/dist/plan/auto.js +14 -2
- package/dist/plan/auto.js.map +1 -1
- package/dist/plan/diff.d.ts +45 -0
- package/dist/plan/diff.d.ts.map +1 -0
- package/dist/plan/diff.js +284 -0
- package/dist/plan/diff.js.map +1 -0
- package/dist/plan/json.d.ts +37 -0
- package/dist/plan/json.d.ts.map +1 -0
- package/dist/plan/json.js +181 -0
- package/dist/plan/json.js.map +1 -0
- package/dist/plan/kind.d.ts +28 -0
- package/dist/plan/kind.d.ts.map +1 -0
- package/dist/plan/kind.js +57 -0
- package/dist/plan/kind.js.map +1 -0
- package/dist/plan/offsets.d.ts +8 -0
- package/dist/plan/offsets.d.ts.map +1 -0
- package/dist/plan/offsets.js +19 -0
- package/dist/plan/offsets.js.map +1 -0
- package/dist/plan/planners.d.ts +15 -5
- package/dist/plan/planners.d.ts.map +1 -1
- package/dist/plan/planners.js +13 -5
- package/dist/plan/planners.js.map +1 -1
- package/dist/plan/structural.d.ts.map +1 -1
- package/dist/plan/structural.js +106 -14
- package/dist/plan/structural.js.map +1 -1
- package/dist/retrieve.d.ts +22 -1
- package/dist/retrieve.d.ts.map +1 -1
- package/dist/retrieve.js +59 -0
- package/dist/retrieve.js.map +1 -1
- package/dist/smelter.d.ts +4 -0
- package/dist/smelter.d.ts.map +1 -1
- package/dist/smelter.js +4 -0
- package/dist/smelter.js.map +1 -1
- package/dist/stats.d.ts +16 -1
- package/dist/stats.d.ts.map +1 -1
- package/dist/stats.js +29 -0
- package/dist/stats.js.map +1 -1
- package/dist/store-dir.d.ts +9 -2
- package/dist/store-dir.d.ts.map +1 -1
- package/dist/store-dir.js +48 -7
- package/dist/store-dir.js.map +1 -1
- package/dist/store.d.ts +4 -2
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +12 -2
- package/dist/store.js.map +1 -1
- package/dist/types.d.ts +116 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/plan/structural.js
CHANGED
|
@@ -3,6 +3,7 @@ import { GrammarUnavailableError, MissingMarkerPricingError } from '../errors.js
|
|
|
3
3
|
import { profileFor, structuralLanguages } from '../lang/registry.js';
|
|
4
4
|
import { predictOutputBytes, savingBytes } from './budget.js';
|
|
5
5
|
import { loadGrammar } from './grammar.js';
|
|
6
|
+
import { utf8OffsetIndex } from './offsets.js';
|
|
6
7
|
export const STRUCTURAL_PLANNER_ID = 'structural/v1';
|
|
7
8
|
/**
|
|
8
9
|
* The languages this planner actually parses — a derived view of the registry: every
|
|
@@ -158,9 +159,15 @@ function planFromTree(tree, input, options, language) {
|
|
|
158
159
|
}
|
|
159
160
|
const start = toByte.get(group[0].start);
|
|
160
161
|
const end = toByte.get(group[group.length - 1].end);
|
|
162
|
+
// The outline: every named unit in the run, in source order. Out of band — the
|
|
163
|
+
// marker below is priced and rendered from `reason` alone, so attaching names
|
|
164
|
+
// moves no byte of the wire surface. Absent rather than empty when nothing in the
|
|
165
|
+
// run has a name (a run of comments, or unparsed regions).
|
|
166
|
+
const names = group.flatMap((unit) => (unit.name === undefined ? [] : [unit.name]));
|
|
161
167
|
const candidate = {
|
|
162
168
|
range: { start, end },
|
|
163
169
|
reason: { rule: SIBLING_COLLAPSE_RULE, explanation: explain(group) },
|
|
170
|
+
...(names.length === 0 ? {} : { names }),
|
|
164
171
|
};
|
|
165
172
|
// Profitability, priced rather than estimated: ask the MarkerPricing seam for the
|
|
166
173
|
// exact cost of the marker this cut would earn — the explanation's length varies
|
|
@@ -415,6 +422,7 @@ function unitsOf(root, text, structure) {
|
|
|
415
422
|
start: child.startIndex,
|
|
416
423
|
end: childEnd,
|
|
417
424
|
kind: kindOf(child, structure),
|
|
425
|
+
...outlineName(child, structure, kindOf(child, structure)),
|
|
418
426
|
});
|
|
419
427
|
}
|
|
420
428
|
else if (attached) {
|
|
@@ -422,6 +430,7 @@ function unitsOf(root, text, structure) {
|
|
|
422
430
|
start: pending[0].startIndex,
|
|
423
431
|
end: childEnd,
|
|
424
432
|
kind: kindOf(child, structure),
|
|
433
|
+
...outlineName(child, structure, kindOf(child, structure)),
|
|
425
434
|
});
|
|
426
435
|
pending = [];
|
|
427
436
|
pendingHasAttribute = false;
|
|
@@ -432,6 +441,7 @@ function unitsOf(root, text, structure) {
|
|
|
432
441
|
start: child.startIndex,
|
|
433
442
|
end: childEnd,
|
|
434
443
|
kind: kindOf(child, structure),
|
|
444
|
+
...outlineName(child, structure, kindOf(child, structure)),
|
|
435
445
|
});
|
|
436
446
|
}
|
|
437
447
|
for (const comment of trailing)
|
|
@@ -584,22 +594,104 @@ function kindOf(node, structure) {
|
|
|
584
594
|
return 'declaration';
|
|
585
595
|
}
|
|
586
596
|
/**
|
|
597
|
+
* `{ name }` when the unit is a declaration with a name, `{}` otherwise — spread into
|
|
598
|
+
* a unit. Only declarations are named: an import, a package clause or a bare statement
|
|
599
|
+
* has an identifier in it too (`require 'json'`, `using System;`) but naming it would
|
|
600
|
+
* put a word on the outline that names nothing a model could be looking for.
|
|
601
|
+
*/
|
|
602
|
+
function outlineName(node, structure, kind) {
|
|
603
|
+
if (UNNAMED_KINDS.has(kind))
|
|
604
|
+
return {};
|
|
605
|
+
const name = nameOf(node, structure, 0);
|
|
606
|
+
return name === undefined ? {} : { name };
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Kinds whose units carry no outline name: everything that is not a declaration, plus
|
|
610
|
+
* the declaration-shaped kinds that name a *module* rather than something defined
|
|
611
|
+
* here (imports, includes, usings, package clauses).
|
|
612
|
+
*/
|
|
613
|
+
const UNNAMED_KINDS = new Set([
|
|
614
|
+
...NON_DECLARATION_KINDS,
|
|
615
|
+
'import statement',
|
|
616
|
+
'import declaration',
|
|
617
|
+
'import list',
|
|
618
|
+
'include directive',
|
|
619
|
+
'using directive',
|
|
620
|
+
'using declaration',
|
|
621
|
+
'package clause',
|
|
622
|
+
'package declaration',
|
|
623
|
+
'package header',
|
|
624
|
+
]);
|
|
625
|
+
/**
|
|
626
|
+
* The node types that *are* a name, across the bundled grammars: `identifier` almost
|
|
627
|
+
* everywhere, `simple_identifier` in kotlin and swift, `type_identifier` for a rust
|
|
628
|
+
* `impl` block's type or a C typedef, `constant` for a ruby class, `name` in php,
|
|
629
|
+
* `word` for a bash function. A grammar that names its declaration through a field
|
|
630
|
+
* is read through the field first; this set is the fallback for the ones that do not.
|
|
631
|
+
*/
|
|
632
|
+
const NAME_NODE_TYPES = new Set([
|
|
633
|
+
'identifier',
|
|
634
|
+
'simple_identifier',
|
|
635
|
+
'type_identifier',
|
|
636
|
+
'property_identifier',
|
|
637
|
+
'field_identifier',
|
|
638
|
+
'constant',
|
|
639
|
+
'name',
|
|
640
|
+
'word',
|
|
641
|
+
]);
|
|
642
|
+
/** How deep the name search descends through wrappers and declarators before giving up. */
|
|
643
|
+
const NAME_SEARCH_DEPTH = 4;
|
|
644
|
+
/**
|
|
645
|
+
* The declaration's name, read off the tree — or `undefined`, honestly, when the node
|
|
646
|
+
* has none (an expression statement, an import, an unparsed region). Three readings in
|
|
647
|
+
* order, each a fact the grammar states rather than a guess about the text:
|
|
648
|
+
*
|
|
649
|
+
* 1. A wrapper (`export …`, `@decorator`) names what it wraps, so descend to it.
|
|
650
|
+
* 2. The `name` field, which most grammars put on a declaration — and failing that
|
|
651
|
+
* the `declarator` chain C and C++ use (`int (*fn)(void)` names `fn` three levels
|
|
652
|
+
* down), which is followed to the identifier at its end.
|
|
653
|
+
* 3. The first named child that is itself a name node, or that carries a `name`
|
|
654
|
+
* field of its own (go's `type_declaration` → `type_spec`, a `lexical_declaration`
|
|
655
|
+
* → `variable_declarator`).
|
|
587
656
|
*
|
|
588
|
-
*
|
|
589
|
-
*
|
|
590
|
-
*
|
|
591
|
-
* always a character boundary. One forward pass, so the conversion is linear.
|
|
657
|
+
* The search is bounded so a pathological tree cannot make it quadratic, and a name
|
|
658
|
+
* is returned only when the tree says so — a wrong name on an outline would send a
|
|
659
|
+
* model to retrieve the wrong marker, which is worse than no outline at all.
|
|
592
660
|
*/
|
|
593
|
-
function
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
661
|
+
function nameOf(node, structure, depth) {
|
|
662
|
+
if (depth > NAME_SEARCH_DEPTH)
|
|
663
|
+
return undefined;
|
|
664
|
+
if (NAME_NODE_TYPES.has(node.type))
|
|
665
|
+
return node.text;
|
|
666
|
+
if (structure.wrapperTypes[node.type] !== undefined) {
|
|
667
|
+
for (const child of node.namedChildren) {
|
|
668
|
+
if (child === null || structure.commentTypes.has(child.type))
|
|
669
|
+
continue;
|
|
670
|
+
const inner = nameOf(child, structure, depth + 1);
|
|
671
|
+
if (inner !== undefined)
|
|
672
|
+
return inner;
|
|
673
|
+
}
|
|
674
|
+
return undefined;
|
|
675
|
+
}
|
|
676
|
+
const byField = node.childForFieldName('name');
|
|
677
|
+
if (byField !== null) {
|
|
678
|
+
return NAME_NODE_TYPES.has(byField.type) || byField.namedChildCount === 0
|
|
679
|
+
? byField.text
|
|
680
|
+
: (nameOf(byField, structure, depth + 1) ?? byField.text);
|
|
681
|
+
}
|
|
682
|
+
const declarator = node.childForFieldName('declarator');
|
|
683
|
+
if (declarator !== null)
|
|
684
|
+
return nameOf(declarator, structure, depth + 1);
|
|
685
|
+
for (const child of node.namedChildren) {
|
|
686
|
+
if (child === null || structure.commentTypes.has(child.type))
|
|
687
|
+
continue;
|
|
688
|
+
if (NAME_NODE_TYPES.has(child.type))
|
|
689
|
+
return child.text;
|
|
690
|
+
if (child.childForFieldName('name') !== null ||
|
|
691
|
+
child.childForFieldName('declarator') !== null) {
|
|
692
|
+
return nameOf(child, structure, depth + 1);
|
|
693
|
+
}
|
|
602
694
|
}
|
|
603
|
-
return
|
|
695
|
+
return undefined;
|
|
604
696
|
}
|
|
605
697
|
//# sourceMappingURL=structural.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"structural.js","sourceRoot":"","sources":["../../src/plan/structural.ts"],"names":[],"mappings":"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","sourcesContent":["import { Parser } from 'web-tree-sitter';\nimport type { Node, Tree } from 'web-tree-sitter';\n\nimport { GrammarUnavailableError, MissingMarkerPricingError } from '../errors.ts';\nimport type { LanguageStructure } from '../lang/profile.ts';\nimport { profileFor, structuralLanguages } from '../lang/registry.ts';\nimport type {\n ElisionPlan,\n LanguageId,\n MarkerPricing,\n PlanInput,\n PlannedElision,\n Planner,\n} from '../types.ts';\n\nimport { predictOutputBytes, savingBytes } from './budget.ts';\nimport { loadGrammar } from './grammar.ts';\n\nexport const STRUCTURAL_PLANNER_ID = 'structural/v1';\n\n/**\n * The languages this planner actually parses — a derived view of the registry: every\n * {@link LanguageProfile} that carries a `structure` section, in registry order. A\n * language appears here only once its node kinds are mapped in its profile\n * (`src/lang/<id>.ts`), because claiming a language before that would produce markers\n * that mislabel what they collapsed.\n *\n * Exported for the totality guard (`test/guards/structural-totality.test.ts`): every\n * language named here must have a fixture, a snapshot and a doc-comment case, so a\n * language cannot be claimed without tests.\n */\nexport const STRUCTURAL_LANGUAGES: readonly LanguageId[] = structuralLanguages();\n\n/**\n * Historically the union of the claimed ids; every `LanguageId` carries a profile\n * now, so the compile-time totality this type enforced lives on the registry\n * (`Record<LanguageId, LanguageProfile>`) instead.\n */\nexport type StructuralLanguage = LanguageId;\n\n/**\n * Every elision this planner produces carries this rule id, and the profitability\n * check below prices the marker that rule would earn — so the two must not drift.\n */\nconst SIBLING_COLLAPSE_RULE = 'sibling-collapse';\n\nexport interface StructuralPlannerOptions {\n /**\n * Never collapse a sibling group smaller than this. Defaults to 1 — the byte\n * profitability check already refuses collapses that would not pay for their marker.\n */\n readonly minSiblings?: number;\n /** Focus matching is substring, case-insensitive by default — same as lexical. */\n readonly caseSensitive?: boolean;\n}\n\n/**\n * One top-level declaration, together with the doc comment attached to it. The unit is\n * the atom of every decision here: a unit is kept whole or collapsed whole, which is\n * what makes \"a kept declaration keeps its signature line and attached doc comment\"\n * true by construction rather than by patching ranges afterwards.\n *\n * All positions are UTF-16 code-unit indices (what web-tree-sitter reports for a JS\n * string); they are converted to UTF-8 byte offsets in one place, at the end.\n */\ninterface Unit {\n /** Start of the unit — the first attached comment or attribute if there is one. */\n readonly start: number;\n /** End of the declaration node. */\n readonly end: number;\n /** Human word for the declaration's kind, e.g. `'function'`. */\n readonly kind: string;\n /**\n * A unit the planner must never collapse, matched or not. Every pin follows one\n * law: collapsing it would silently change what the survivor *is*, not what it\n * contains. Go's `//go:build` constraint governs which builds see the whole file;\n * shebang lines (bash, ruby, python as comments; javascript, typescript, tsx,\n * kotlin and swift as their grammars' own shebang nodes) and ruby's\n * `# frozen_string_literal:` magic comment govern how the file is executed at all;\n * php's `<?php` open tag is what makes the rest of the file php; and c/c++'s\n * `#pragma once` governs what including the file means.\n */\n readonly pinned?: boolean;\n}\n\n/**\n * The structural planner — the reason smelt exists.\n *\n * It parses with the language's tree-sitter grammar, finds the top-level declarations\n * whose text matches the caller's focus, keeps each match whole — signature line,\n * attached doc comment, body — and collapses each contiguous run of non-matching\n * *siblings* into one marker that names them from the parse tree: `collapsed 3 sibling\n * functions`. Structure is what makes that explanation possible; a line window can only\n * ever say \"collapsed 40 lines\".\n *\n * It throws {@link GrammarUnavailableError} rather than falling back to the lexical\n * planner, and that is the deliberate part. A silent fallback would mean a caller who\n * asked for structural planning, and whose grammar failed to load, gets line-window\n * output labelled `structural/v1` — plausible, wrong, and undetectable from the\n * outside. A caller who wants the fallback asks for it, by planning lexically itself.\n */\nexport class StructuralPlanner implements Planner {\n readonly id = STRUCTURAL_PLANNER_ID;\n readonly options: StructuralPlannerOptions;\n\n constructor(options: StructuralPlannerOptions = {}) {\n this.options = options;\n }\n\n plan(input: PlanInput): Promise<ElisionPlan> {\n return planStructural(input, this.options);\n }\n}\n\n/**\n * The planner as a function, exported like {@link planLexical} so it can be tested and\n * reused directly. Deterministic: same text, same language, same focus, same options —\n * byte-identical plan.\n *\n * `budgetBytes` is a target, not a guarantee. The plan is built in two passes: every\n * maximal run of non-matching siblings is collapsed where that pays for its marker,\n * and then — only if the plan is still over budget — each run the first pass refused\n * is re-asked as its own best profitable sub-run (the budget rung, in\n * {@link planFromTree}). Under the last of those there is nothing left to cut except\n * the declarations the caller asked to keep, and an optimizer that silently drops the\n * thing you searched for is the exact failure this design refuses. Callers who need a\n * hard ceiling check `outputBytes` and decide.\n *\n * @throws {GrammarUnavailableError} when the language is not one this planner parses,\n * when the grammar cannot be loaded, or when the parser produces no tree. Never a\n * lexical fallback.\n */\nexport async function planStructural(\n input: PlanInput,\n options: StructuralPlannerOptions = {},\n): Promise<ElisionPlan> {\n const language = assertStructuralLanguage(input.language);\n // The runtime backstop for JS callers: TypeScript makes `pricing` required, but a\n // JS caller can omit it, and the honest answer is a named refusal rather than the\n // planner quietly pricing markers itself — the inversion the seam removed.\n const pricing: MarkerPricing | undefined = input.pricing;\n if (pricing === undefined) throw new MissingMarkerPricingError(STRUCTURAL_PLANNER_ID);\n const grammar = await loadGrammar(language);\n\n const parser = new Parser();\n let tree: Tree | null = null;\n try {\n parser.setLanguage(grammar);\n tree = parser.parse(input.text);\n if (tree === null) {\n throw new GrammarUnavailableError(\n `smelt: the ${language} parser returned no tree. Structural planning cannot ` +\n `proceed, and it does not fall back to lexical — a caller who wants the ` +\n `fallback plans lexically itself.`,\n );\n }\n return {\n planner: STRUCTURAL_PLANNER_ID,\n language: input.language,\n elisions: planFromTree(tree, input, options, language),\n };\n } finally {\n tree?.delete();\n parser.delete();\n }\n}\n\nfunction assertStructuralLanguage(language: PlanInput['language']): StructuralLanguage {\n if (isStructuralLanguage(language)) return language;\n const named = `${STRUCTURAL_LANGUAGES.slice(0, -1).join(', ')} and ${\n STRUCTURAL_LANGUAGES[STRUCTURAL_LANGUAGES.length - 1]\n }`;\n throw new GrammarUnavailableError(\n `smelt: structural planning covers ${named} in this ` +\n `slice; got \"${language}\". It does not fall back to the lexical planner — output ` +\n `labelled structural/v1 that is really line windows would be undetectable from ` +\n `the outside. Use \\`strategy: 'auto'\\` for a mixed stream that is sometimes not ` +\n `code — it picks the planner from the language and labels the one it ran — or ` +\n `the lexical planner explicitly if that is what you want.`,\n );\n}\n\n/**\n * Whether this planner has a bundled grammar for the language — the one membership\n * test, shared with the `auto` strategy (`plan/auto.ts`).\n *\n * The refusal above and auto's selection are the same question asked for opposite\n * reasons, and two spellings of it would be two answers: a language auto routed to\n * `structural` that `assertStructuralLanguage` then refused would be a\n * `GrammarUnavailableError` raised by the strategy whose entire job is not raising\n * one.\n */\nexport function isStructuralLanguage(\n language: PlanInput['language'],\n): language is StructuralLanguage {\n for (const supported of STRUCTURAL_LANGUAGES) {\n if (language === supported) return true;\n }\n return false;\n}\n\nfunction planFromTree(\n tree: Tree,\n input: PlanInput,\n options: StructuralPlannerOptions,\n language: StructuralLanguage,\n): readonly PlannedElision[] {\n const profile = profileFor(language);\n // assertStructuralLanguage admits only languages whose profile carries a\n // `structure` section — that is the definition of a structural language — so the\n // assertion states a fact the seam already proved.\n const structure = profile.structure!;\n const pricing = input.pricing;\n // The marker lands as a line comment in every structural language (the profile's\n // markerLeader), and a line comment comments out everything to the end of its\n // line — so a collapse is only legal where nothing kept follows on the marker's\n // own line. See the flush below.\n const markerIsLineComment = profile.markerLeader !== undefined;\n const units = unitsOf(tree.rootNode, input.text, structure);\n const matched = matchUnits(units, input, options);\n const minSiblings = options.minSiblings ?? 1;\n const toByte = utf8OffsetIndex(\n input.text,\n units.flatMap((unit) => [unit.start, unit.end]),\n );\n\n /**\n * One contiguous group of siblings as an elision — or nothing, when collapsing it\n * would be illegal or would not pay for its marker. Every cut in this planner,\n * first pass and budget rung alike, is minted here: one legality rule, one price,\n * one explanation, so the rung cannot cut something the first pass would refuse.\n */\n const collapse = (group: readonly Unit[]): PlannedElision | undefined => {\n if (group.length === 0 || group.length < minSiblings) return undefined;\n // A line-comment marker swallows the rest of its line. When the group's last unit\n // ends mid-line — python's `stmt_a(); stmt_b()` puts two top-level statements on\n // one line — the marker's `# ` leader would comment out the *kept* code after it.\n // Refusing the collapse is the honest move; extending the range would elide code\n // no unit accounted for.\n if (markerIsLineComment && !restOfLineIsBlank(input.text, group[group.length - 1]!.end)) {\n return undefined;\n }\n const start = toByte.get(group[0]!.start)!;\n const end = toByte.get(group[group.length - 1]!.end)!;\n const candidate: PlannedElision = {\n range: { start, end },\n reason: { rule: SIBLING_COLLAPSE_RULE, explanation: explain(group) },\n };\n // Profitability, priced rather than estimated: ask the MarkerPricing seam for the\n // exact cost of the marker this cut would earn — the explanation's length varies\n // with kind diversity, and the pricing carries the language's comment leader (or a\n // caller's custom builder), so a fixed estimate can pass a cut whose marker is\n // bigger than what it removes, and a marker that costs more than it removes grows\n // the output.\n return savingBytes(candidate, pricing) > 0 ? candidate : undefined;\n };\n\n /**\n * The UTF-8 bytes a sub-run `[from, to)` spans — read off the same offset index\n * `collapse` mints ranges from, so it is the exact upper bound on what collapsing\n * that sub-run could save, in O(1) and without slicing. The budget rung's span\n * bound depends on it being both exact and cheap.\n */\n const spanBytes = (group: readonly Unit[], from: number, to: number): number =>\n toByte.get(group[to - 1]!.end)! - toByte.get(group[from]!.start)!;\n\n // The first pass: every maximal run of siblings no focus term matched and no rule\n // pinned, collapsed whole. A run that earns no cut is remembered, not forgotten —\n // the budget rung below is the only reader of that list.\n const elisions: PlannedElision[] = [];\n const refused: (readonly Unit[])[] = [];\n for (const group of maximalRuns(units, matched)) {\n const cut = collapse(group);\n if (cut === undefined) refused.push(group);\n else elisions.push(cut);\n }\n\n // THE BUDGET RUNG — the structural answer to the lexical planner's context ladder.\n //\n // Until this existed, `planStructural` never read `input.budgetBytes` at all: it\n // took every profitable maximal run and stopped, which is right until the plan\n // comes back over budget with a profitable cut still on the table. The review's\n // case: 158 bytes, a budget of 120, and a run of three sibling classes worth 92\n // bytes left whole — because the *maximal* run also swallowed a `VERSION = 1`\n // statement above them, and a mixed-kind explanation (\"1 statement, 3 classes\")\n // priced a 106-byte marker against a 105-byte cut. The planner returned over budget\n // having elided nothing, while the three classes alone priced an 82-byte marker\n // against 92 bytes — a real saving of 10 bytes nobody was offered.\n //\n // So, and only when the plan is still over budget, each refused run is re-asked as\n // its own best profitable sub-run, earliest run first, stopping the moment the plan\n // fits. Four properties hold by construction rather than by care:\n //\n // - **A focus match is never cut.** A matched unit is not in any run, so no\n // sub-run of any run can contain one. Same for a pinned unit.\n // - **The output never grows.** Every candidate goes through `collapse`, which\n // mints nothing whose marker is not strictly cheaper than the bytes it removes.\n // - **It stays deterministic.** Sub-runs are enumerated start-ascending and\n // length-descending, and a tie in saving keeps the one already held — the\n // earliest start, then the longest. Both of the rung's bounds read that same\n // order, so a bounded sweep and an exhaustive one answer alike.\n // - **It stays bounded.** A refused run can be the whole file — the mid-line\n // refusal is about one character, not about size — so the sweep stops at the\n // first cut that fits the budget and skips every candidate whose span already\n // proves it cannot win. See {@link bestSubRun}.\n //\n // Budget pressure is the trigger, not profitability, because a maximal run is the\n // better *explanation*: one marker naming everything it hid beats two naming halves\n // of it. Trading that away is worth it to fit a budget and not worth it otherwise.\n const inputBytes = Buffer.byteLength(input.text, 'utf8');\n for (const group of refused) {\n const currentBytes = predictOutputBytes(inputBytes, elisions, pricing);\n if (currentBytes <= input.budgetBytes) break;\n // What this run would have to save to land the plan under budget — the sweep's\n // stopping condition, so a run that can fit the budget is priced a handful of\n // times instead of exhaustively. See {@link bestSubRun}.\n const enough = currentBytes - input.budgetBytes;\n const cut = bestSubRun(group, collapse, pricing, spanBytes, enough);\n if (cut !== undefined) elisions.push(cut);\n }\n\n return elisions.toSorted((a, b) => a.range.start - b.range.start);\n}\n\n/**\n * The runs the first pass collapses: every maximal contiguous group of units that no\n * focus term matched and no rule pinned. A matched or pinned unit ends the run before\n * it and starts none — which is what makes \"never cut a focus-matched declaration\" a\n * property of the *shape* of the data rather than a check anyone has to remember.\n */\nfunction maximalRuns(\n units: readonly Unit[],\n matched: readonly boolean[],\n): readonly (readonly Unit[])[] {\n const runs: Unit[][] = [];\n let run: Unit[] = [];\n for (let i = 0; i < units.length; i += 1) {\n if (matched[i] === true || units[i]!.pinned === true) {\n if (run.length > 0) runs.push(run);\n run = [];\n } else {\n run.push(units[i]!);\n }\n }\n if (run.length > 0) runs.push(run);\n return runs;\n}\n\n/**\n * The best cut available inside one refused run: the contiguous sub-run whose real\n * marker is cheapest against the bytes it removes.\n *\n * The enumeration is bounded, and it has to be. A run reaches here refused for any of\n * three reasons, and only one of them is \"it lost to a ~100-byte marker\": a run is also\n * refused when it holds fewer than `minSiblings` units, and — the one that bites —\n * when its last unit ends mid-line, because a line-comment marker would swallow the\n * kept code after it. That last rule is a property of one character in the file, not of\n * the run's size, so a *whole-file* run can land here: one bash script whose last line\n * is `cleanup_tmp; deploy_release --now` refuses its only maximal run, and an\n * unbounded `(from, to)` sweep over its thousands of units — each candidate slicing,\n * explaining and pricing — is cubic. Measured, before this bound: 19.5 KB / 373 ms,\n * 39 KB / 2.3 s, 79 KB / 17.2 s, 159 KB / 131 s. That is a CPU loop on tool-controlled\n * input, on the path the caller takes precisely when it is over budget — the normal\n * case. Two bounds, neither of which changes which cut a bounded sweep would have\n * found by exhaustion:\n *\n * - **`enough`** — the saving that lands the plan under budget. The rung exists to\n * fit a budget; the first candidate that does is the answer, and enumeration order\n * (start-ascending, length-descending) makes it the earliest, longest such cut.\n * This is what turns the whole-file case into two priced candidates.\n * - **The span bound** — a cut can never save more than the bytes it spans, so once\n * a candidate's span has shrunk to `bestSaving` or below, neither it nor any\n * shorter candidate from the same start can win, and once the *longest* candidate\n * from a start is that small, no later start can win either. Spans shrink\n * monotonically along both loops, so both breaks are exact, not heuristic: they\n * skip only candidates already proven to lose.\n */\nfunction bestSubRun(\n group: readonly Unit[],\n collapse: (group: readonly Unit[]) => PlannedElision | undefined,\n pricing: MarkerPricing,\n spanBytes: (group: readonly Unit[], from: number, to: number) => number,\n enough: number,\n): PlannedElision | undefined {\n let best: PlannedElision | undefined;\n let bestSaving = 0;\n for (let from = 0; from < group.length; from += 1) {\n if (spanBytes(group, from, group.length) <= bestSaving) break;\n for (let to = group.length; to > from; to -= 1) {\n if (spanBytes(group, from, to) <= bestSaving) break;\n const candidate = collapse(group.slice(from, to));\n if (candidate === undefined) continue;\n const saving = savingBytes(candidate, pricing);\n if (saving > bestSaving) {\n best = candidate;\n bestSaving = saving;\n if (saving >= enough) return best;\n }\n }\n }\n return best;\n}\n\n/** True when nothing but whitespace follows `index` on its own line. */\nfunction restOfLineIsBlank(text: string, index: number): boolean {\n for (let i = index; i < text.length; i += 1) {\n const ch = text[i]!;\n if (ch === '\\n') return true;\n if (!/\\s/.test(ch)) return false;\n }\n return true;\n}\n\n/**\n * Group the root's named children into units: each declaration plus the comment block\n * — and, in rust, the outer attributes — attached to it. A comment attaches when only\n * blank-free whitespace separates it from what follows — one newline at most — so a doc\n * comment travels with its declaration, while a comment left floating above a blank\n * line stands alone.\n *\n * Attributes attach *unconditionally*: tree-sitter-rust parses `#[inline]` as a\n * top-level sibling of the item it decorates, but the language's own rule is that an\n * outer attribute modifies the next item, blank lines or not. So once an attribute is\n * pending, the whole pending prefix (doc comments included) rides forward to the next\n * declaration — treating it as its own unit would let a collapse strip `#[derive(…)]`\n * and the doc comment above it off a kept declaration.\n */\nfunction unitsOf(root: Node, text: string, structure: LanguageStructure): readonly Unit[] {\n const units: Unit[] = [];\n /** Comments and attributes waiting to attach to the next declaration. */\n let pending: Node[] = [];\n /** Once true, blank lines no longer detach the pending prefix — see above. */\n let pendingHasAttribute = false;\n\n const flushPending = (): void => {\n if (pending.length === 0) return;\n units.push({\n start: pending[0]!.startIndex,\n end: pending[pending.length - 1]!.endIndex,\n kind: pendingHasAttribute ? 'attribute' : 'comment',\n pinned: !pendingHasAttribute && pending.some((node) => pinnedComment(node, text, structure)),\n });\n pending = [];\n pendingHasAttribute = false;\n };\n\n for (const child of root.namedChildren) {\n if (child === null) continue;\n if (structure.ridesBackwardTypes?.has(child.type) === true && units.length > 0) {\n // A node that is grammatically part of the *previous* statement, parsed as a\n // top-level sibling: tree-sitter-ruby emits a heredoc's body as a sibling of\n // the statement holding its `<<~SQL` opener. Splitting them lets a collapse\n // keep the opener and cut the body — an unterminated heredoc that swallows\n // every kept declaration after it, without a single ERROR node to show for\n // it. The body extends the preceding unit instead, so opener and body are\n // kept whole or collapsed whole, always.\n flushPending();\n const last = units[units.length - 1]!;\n units[units.length - 1] = { ...last, end: child.endIndex };\n continue;\n }\n if (\n structure.pinnedTypes?.has(child.type) === true ||\n pinnedDirective(child, text, structure)\n ) {\n // A pinned node (php's `<?php` tag, a `#!` shebang line, c's `#pragma once`)\n // is its own uncollapsible unit — nothing attaches to it, and no run may\n // swallow it.\n flushPending();\n units.push({\n start: child.startIndex,\n end: child.endIndex,\n kind: kindOf(child, structure),\n pinned: true,\n });\n continue;\n }\n const isComment = structure.commentTypes.has(child.type);\n const isAttribute = structure.attributeTypes.has(child.type);\n if (isComment || isAttribute) {\n if (\n pending.length > 0 &&\n !pendingHasAttribute &&\n !adjacent(text, pending[pending.length - 1]!.endIndex, child.startIndex)\n ) {\n flushPending();\n }\n pending.push(child);\n pendingHasAttribute ||= isAttribute;\n continue;\n }\n\n // tree-sitter-kotlin extends the import_list node over a doc comment that\n // follows it, so the KDoc of the first documented declaration after the imports\n // would be collapsed *with the imports* — a kept declaration losing its attached\n // doc comment. Split such trailing comments off: the unit ends at its last\n // non-comment token, and the comments ride forward like any other pending block.\n const { end: childEnd, trailing } =\n structure.trailingCommentSplitTypes?.has(child.type) === true\n ? splitTrailingComments(child, structure)\n : { end: child.endIndex, trailing: [] };\n\n const attached =\n pending.length > 0 &&\n (pendingHasAttribute ||\n adjacent(text, pending[pending.length - 1]!.endIndex, child.startIndex));\n if (attached && pending.some((node) => pinnedComment(node, text, structure))) {\n // A pinned comment (`//go:build`) governs the file, not the declaration it\n // happens to touch — keep it a standalone, uncollapsible unit either way.\n flushPending();\n units.push({\n start: child.startIndex,\n end: childEnd,\n kind: kindOf(child, structure),\n });\n } else if (attached) {\n units.push({\n start: pending[0]!.startIndex,\n end: childEnd,\n kind: kindOf(child, structure),\n });\n pending = [];\n pendingHasAttribute = false;\n } else {\n flushPending();\n units.push({\n start: child.startIndex,\n end: childEnd,\n kind: kindOf(child, structure),\n });\n }\n for (const comment of trailing) pending.push(comment);\n }\n flushPending();\n return units;\n}\n\n/** Whether this comment node is one the language pins to the file. See {@link Unit}. */\nfunction pinnedComment(node: Node, text: string, structure: LanguageStructure): boolean {\n if (structure.pinnedCommentPattern === undefined) return false;\n if (!structure.commentTypes.has(node.type)) return false;\n return structure.pinnedCommentPattern.test(text.slice(node.startIndex, node.endIndex));\n}\n\n/**\n * Whether this non-comment node is pinned by a per-type text pattern. C's\n * `#pragma once` is a `preproc_call` like any other pragma, but collapsing it\n * silently changes header inclusion semantics — the same class as `//go:build`, on a\n * node kind a comment pattern cannot reach.\n */\nfunction pinnedDirective(node: Node, text: string, structure: LanguageStructure): boolean {\n const pattern = structure.pinnedPatternsByType?.[node.type];\n if (pattern === undefined) return false;\n return pattern.test(text.slice(node.startIndex, node.endIndex));\n}\n\n/**\n * The end of `node`'s last non-comment token, and every comment node after it. Used\n * for node types the grammar extends over comments that belong to what follows —\n * kotlin's import_list swallowing the next declaration's KDoc.\n */\nfunction splitTrailingComments(\n node: Node,\n structure: LanguageStructure,\n): { readonly end: number; readonly trailing: readonly Node[] } {\n const comments: Node[] = [];\n let end = node.startIndex;\n const walk = (current: Node): void => {\n if (structure.commentTypes.has(current.type)) {\n comments.push(current);\n return;\n }\n let hasChildren = false;\n for (const child of current.children) {\n if (child === null) continue;\n hasChildren = true;\n walk(child);\n }\n if (!hasChildren && current.endIndex > end) end = current.endIndex;\n };\n for (const child of node.children) {\n if (child !== null) walk(child);\n }\n return { end, trailing: comments.filter((comment) => comment.startIndex >= end) };\n}\n\n/** Nothing but whitespace between the two indices, and at most one newline. */\nfunction adjacent(text: string, end: number, start: number): boolean {\n const between = text.slice(end, start);\n if (!/^\\s*$/.test(between)) return false;\n return (between.match(/\\n/g) ?? []).length <= 1;\n}\n\nfunction matchUnits(\n units: readonly Unit[],\n input: PlanInput,\n options: StructuralPlannerOptions,\n): readonly boolean[] {\n const caseSensitive = options.caseSensitive ?? false;\n const focus = (input.focus ?? []).filter((term) => term.length > 0);\n const needles = caseSensitive ? focus : focus.map((term) => term.toLowerCase());\n\n return units.map((unit) => {\n if (needles.length === 0) return false;\n const raw = input.text.slice(unit.start, unit.end);\n const haystack = caseSensitive ? raw : raw.toLowerCase();\n return needles.some((needle) => haystack.includes(needle));\n });\n}\n\n/**\n * Law 2, for this rule: the explanation names the *kind* and the *count*, read off the\n * parse tree — `collapsed 3 sibling functions` — never a line count as the claim. A\n * mixed run says what it mixed: `collapsed 4 sibling declarations (3 functions,\n * 1 class)` — and when the run holds anything that is not a declaration (a statement,\n * a floating comment, an unparsed region), the heading says `nodes`, because calling\n * a parse error a declaration would be the marker lying about the tree.\n */\nfunction explain(group: readonly Unit[]): string {\n const counts = new Map<string, number>();\n for (const unit of group) counts.set(unit.kind, (counts.get(unit.kind) ?? 0) + 1);\n\n const total = group.length;\n if (counts.size === 1) {\n const kind = group[0]!.kind;\n return `collapsed ${String(total)} sibling ${countNoun(kind, total)}`;\n }\n const parts = [...counts.entries()].map(\n ([kind, count]) => `${String(count)} ${countNoun(kind, count)}`,\n );\n const heading = [...counts.keys()].every((kind) => !NON_DECLARATION_KINDS.has(kind))\n ? 'declarations'\n : 'nodes';\n return `collapsed ${String(total)} sibling ${heading} (${parts.join(', ')})`;\n}\n\n/** The kind labels that are not declarations, so a mixed heading never overclaims. */\nconst NON_DECLARATION_KINDS: ReadonlySet<string> = new Set([\n 'statement',\n 'comment',\n 'unparsed region',\n 'package clause',\n 'package header',\n 'package declaration',\n 'attribute',\n 'command',\n 'variable assignment',\n 'include directive',\n 'shebang',\n 'php tag',\n 'html section',\n 'heredoc body',\n 'preprocessor directive',\n 'preprocessor conditional',\n]);\n\nfunction countNoun(kind: string, count: number): string {\n if (count === 1) return kind;\n if (/[^aeiou]y$/.test(kind)) return `${kind.slice(0, -1)}ies`;\n return /(?:s|sh|ch)$/.test(kind) ? `${kind}es` : `${kind}s`;\n}\n\n/**\n * The kind of a declaration, unwrapping wrappers (`export …`, `@decorator`) so the\n * marker names what it hides. What the language's map does not name is still labelled\n * honestly — see {@link LanguageStructure.kindLabels} for the labelling rules.\n */\nfunction kindOf(node: Node, structure: LanguageStructure): string {\n const wrapperFallback = structure.wrapperTypes[node.type];\n if (wrapperFallback !== undefined) {\n for (const child of node.namedChildren) {\n if (child === null || structure.commentTypes.has(child.type)) continue;\n const label = structure.kindLabels[child.type];\n if (label !== undefined) return label;\n }\n return wrapperFallback;\n }\n const label = structure.kindLabels[node.type];\n if (label !== undefined) return label;\n if (node.type === 'ERROR') return 'unparsed region';\n if (node.type.endsWith('_statement') || node.type === 'statement_block') return 'statement';\n return 'declaration';\n}\n\n/**\n *\n * web-tree-sitter reports positions in code units of the JS string it parsed;\n * {@link ElisionPlan} ranges are UTF-8 bytes. Because every converted index is a parse\n * node boundary, a range can never split a multi-byte character — a node boundary is\n * always a character boundary. One forward pass, so the conversion is linear.\n */\nfunction utf8OffsetIndex(text: string, indices: readonly number[]): ReadonlyMap<number, number> {\n const sorted = [...new Set(indices)].toSorted((a, b) => a - b);\n const map = new Map<number, number>();\n let previousIndex = 0;\n let previousByte = 0;\n for (const index of sorted) {\n previousByte += Buffer.byteLength(text.slice(previousIndex, index), 'utf8');\n previousIndex = index;\n map.set(index, previousByte);\n }\n return map;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"structural.js","sourceRoot":"","sources":["../../src/plan/structural.ts"],"names":[],"mappings":"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","sourcesContent":["import { Parser } from 'web-tree-sitter';\nimport type { Node, Tree } from 'web-tree-sitter';\n\nimport { GrammarUnavailableError, MissingMarkerPricingError } from '../errors.ts';\nimport type { LanguageStructure } from '../lang/profile.ts';\nimport { profileFor, structuralLanguages } from '../lang/registry.ts';\nimport type {\n ElisionPlan,\n LanguageId,\n MarkerPricing,\n PlanInput,\n PlannedElision,\n Planner,\n} from '../types.ts';\n\nimport { predictOutputBytes, savingBytes } from './budget.ts';\nimport { loadGrammar } from './grammar.ts';\nimport { utf8OffsetIndex } from './offsets.ts';\n\nexport const STRUCTURAL_PLANNER_ID = 'structural/v1';\n\n/**\n * The languages this planner actually parses — a derived view of the registry: every\n * {@link LanguageProfile} that carries a `structure` section, in registry order. A\n * language appears here only once its node kinds are mapped in its profile\n * (`src/lang/<id>.ts`), because claiming a language before that would produce markers\n * that mislabel what they collapsed.\n *\n * Exported for the totality guard (`test/guards/structural-totality.test.ts`): every\n * language named here must have a fixture, a snapshot and a doc-comment case, so a\n * language cannot be claimed without tests.\n */\nexport const STRUCTURAL_LANGUAGES: readonly LanguageId[] = structuralLanguages();\n\n/**\n * Historically the union of the claimed ids; every `LanguageId` carries a profile\n * now, so the compile-time totality this type enforced lives on the registry\n * (`Record<LanguageId, LanguageProfile>`) instead.\n */\nexport type StructuralLanguage = LanguageId;\n\n/**\n * Every elision this planner produces carries this rule id, and the profitability\n * check below prices the marker that rule would earn — so the two must not drift.\n */\nconst SIBLING_COLLAPSE_RULE = 'sibling-collapse';\n\nexport interface StructuralPlannerOptions {\n /**\n * Never collapse a sibling group smaller than this. Defaults to 1 — the byte\n * profitability check already refuses collapses that would not pay for their marker.\n */\n readonly minSiblings?: number;\n /** Focus matching is substring, case-insensitive by default — same as lexical. */\n readonly caseSensitive?: boolean;\n}\n\n/**\n * One top-level declaration, together with the doc comment attached to it. The unit is\n * the atom of every decision here: a unit is kept whole or collapsed whole, which is\n * what makes \"a kept declaration keeps its signature line and attached doc comment\"\n * true by construction rather than by patching ranges afterwards.\n *\n * All positions are UTF-16 code-unit indices (what web-tree-sitter reports for a JS\n * string); they are converted to UTF-8 byte offsets in one place, at the end.\n */\ninterface Unit {\n /** Start of the unit — the first attached comment or attribute if there is one. */\n readonly start: number;\n /** End of the declaration node. */\n readonly end: number;\n /** Human word for the declaration's kind, e.g. `'function'`. */\n readonly kind: string;\n /** The declaration's own name, read off the tree — `'parseConfig'` — when it has one. */\n readonly name?: string;\n /**\n * A unit the planner must never collapse, matched or not. Every pin follows one\n * law: collapsing it would silently change what the survivor *is*, not what it\n * contains. Go's `//go:build` constraint governs which builds see the whole file;\n * shebang lines (bash, ruby, python as comments; javascript, typescript, tsx,\n * kotlin and swift as their grammars' own shebang nodes) and ruby's\n * `# frozen_string_literal:` magic comment govern how the file is executed at all;\n * php's `<?php` open tag is what makes the rest of the file php; and c/c++'s\n * `#pragma once` governs what including the file means.\n */\n readonly pinned?: boolean;\n}\n\n/**\n * The structural planner — the reason smelt exists.\n *\n * It parses with the language's tree-sitter grammar, finds the top-level declarations\n * whose text matches the caller's focus, keeps each match whole — signature line,\n * attached doc comment, body — and collapses each contiguous run of non-matching\n * *siblings* into one marker that names them from the parse tree: `collapsed 3 sibling\n * functions`. Structure is what makes that explanation possible; a line window can only\n * ever say \"collapsed 40 lines\".\n *\n * It throws {@link GrammarUnavailableError} rather than falling back to the lexical\n * planner, and that is the deliberate part. A silent fallback would mean a caller who\n * asked for structural planning, and whose grammar failed to load, gets line-window\n * output labelled `structural/v1` — plausible, wrong, and undetectable from the\n * outside. A caller who wants the fallback asks for it, by planning lexically itself.\n */\nexport class StructuralPlanner implements Planner {\n readonly id = STRUCTURAL_PLANNER_ID;\n readonly options: StructuralPlannerOptions;\n\n constructor(options: StructuralPlannerOptions = {}) {\n this.options = options;\n }\n\n plan(input: PlanInput): Promise<ElisionPlan> {\n return planStructural(input, this.options);\n }\n}\n\n/**\n * The planner as a function, exported like {@link planLexical} so it can be tested and\n * reused directly. Deterministic: same text, same language, same focus, same options —\n * byte-identical plan.\n *\n * `budgetBytes` is a target, not a guarantee. The plan is built in two passes: every\n * maximal run of non-matching siblings is collapsed where that pays for its marker,\n * and then — only if the plan is still over budget — each run the first pass refused\n * is re-asked as its own best profitable sub-run (the budget rung, in\n * {@link planFromTree}). Under the last of those there is nothing left to cut except\n * the declarations the caller asked to keep, and an optimizer that silently drops the\n * thing you searched for is the exact failure this design refuses. Callers who need a\n * hard ceiling check `outputBytes` and decide.\n *\n * @throws {GrammarUnavailableError} when the language is not one this planner parses,\n * when the grammar cannot be loaded, or when the parser produces no tree. Never a\n * lexical fallback.\n */\nexport async function planStructural(\n input: PlanInput,\n options: StructuralPlannerOptions = {},\n): Promise<ElisionPlan> {\n const language = assertStructuralLanguage(input.language);\n // The runtime backstop for JS callers: TypeScript makes `pricing` required, but a\n // JS caller can omit it, and the honest answer is a named refusal rather than the\n // planner quietly pricing markers itself — the inversion the seam removed.\n const pricing: MarkerPricing | undefined = input.pricing;\n if (pricing === undefined) throw new MissingMarkerPricingError(STRUCTURAL_PLANNER_ID);\n const grammar = await loadGrammar(language);\n\n const parser = new Parser();\n let tree: Tree | null = null;\n try {\n parser.setLanguage(grammar);\n tree = parser.parse(input.text);\n if (tree === null) {\n throw new GrammarUnavailableError(\n `smelt: the ${language} parser returned no tree. Structural planning cannot ` +\n `proceed, and it does not fall back to lexical — a caller who wants the ` +\n `fallback plans lexically itself.`,\n );\n }\n return {\n planner: STRUCTURAL_PLANNER_ID,\n language: input.language,\n elisions: planFromTree(tree, input, options, language),\n };\n } finally {\n tree?.delete();\n parser.delete();\n }\n}\n\nfunction assertStructuralLanguage(language: PlanInput['language']): StructuralLanguage {\n if (isStructuralLanguage(language)) return language;\n const named = `${STRUCTURAL_LANGUAGES.slice(0, -1).join(', ')} and ${\n STRUCTURAL_LANGUAGES[STRUCTURAL_LANGUAGES.length - 1]\n }`;\n throw new GrammarUnavailableError(\n `smelt: structural planning covers ${named} in this ` +\n `slice; got \"${language}\". It does not fall back to the lexical planner — output ` +\n `labelled structural/v1 that is really line windows would be undetectable from ` +\n `the outside. Use \\`strategy: 'auto'\\` for a mixed stream that is sometimes not ` +\n `code — it picks the planner from the language and labels the one it ran — or ` +\n `the lexical planner explicitly if that is what you want.`,\n );\n}\n\n/**\n * Whether this planner has a bundled grammar for the language — the one membership\n * test, shared with the `auto` strategy (`plan/auto.ts`).\n *\n * The refusal above and auto's selection are the same question asked for opposite\n * reasons, and two spellings of it would be two answers: a language auto routed to\n * `structural` that `assertStructuralLanguage` then refused would be a\n * `GrammarUnavailableError` raised by the strategy whose entire job is not raising\n * one.\n */\nexport function isStructuralLanguage(\n language: PlanInput['language'],\n): language is StructuralLanguage {\n for (const supported of STRUCTURAL_LANGUAGES) {\n if (language === supported) return true;\n }\n return false;\n}\n\nfunction planFromTree(\n tree: Tree,\n input: PlanInput,\n options: StructuralPlannerOptions,\n language: StructuralLanguage,\n): readonly PlannedElision[] {\n const profile = profileFor(language);\n // assertStructuralLanguage admits only languages whose profile carries a\n // `structure` section — that is the definition of a structural language — so the\n // assertion states a fact the seam already proved.\n const structure = profile.structure!;\n const pricing = input.pricing;\n // The marker lands as a line comment in every structural language (the profile's\n // markerLeader), and a line comment comments out everything to the end of its\n // line — so a collapse is only legal where nothing kept follows on the marker's\n // own line. See the flush below.\n const markerIsLineComment = profile.markerLeader !== undefined;\n const units = unitsOf(tree.rootNode, input.text, structure);\n const matched = matchUnits(units, input, options);\n const minSiblings = options.minSiblings ?? 1;\n const toByte = utf8OffsetIndex(\n input.text,\n units.flatMap((unit) => [unit.start, unit.end]),\n );\n\n /**\n * One contiguous group of siblings as an elision — or nothing, when collapsing it\n * would be illegal or would not pay for its marker. Every cut in this planner,\n * first pass and budget rung alike, is minted here: one legality rule, one price,\n * one explanation, so the rung cannot cut something the first pass would refuse.\n */\n const collapse = (group: readonly Unit[]): PlannedElision | undefined => {\n if (group.length === 0 || group.length < minSiblings) return undefined;\n // A line-comment marker swallows the rest of its line. When the group's last unit\n // ends mid-line — python's `stmt_a(); stmt_b()` puts two top-level statements on\n // one line — the marker's `# ` leader would comment out the *kept* code after it.\n // Refusing the collapse is the honest move; extending the range would elide code\n // no unit accounted for.\n if (markerIsLineComment && !restOfLineIsBlank(input.text, group[group.length - 1]!.end)) {\n return undefined;\n }\n const start = toByte.get(group[0]!.start)!;\n const end = toByte.get(group[group.length - 1]!.end)!;\n // The outline: every named unit in the run, in source order. Out of band — the\n // marker below is priced and rendered from `reason` alone, so attaching names\n // moves no byte of the wire surface. Absent rather than empty when nothing in the\n // run has a name (a run of comments, or unparsed regions).\n const names = group.flatMap((unit) => (unit.name === undefined ? [] : [unit.name]));\n const candidate: PlannedElision = {\n range: { start, end },\n reason: { rule: SIBLING_COLLAPSE_RULE, explanation: explain(group) },\n ...(names.length === 0 ? {} : { names }),\n };\n // Profitability, priced rather than estimated: ask the MarkerPricing seam for the\n // exact cost of the marker this cut would earn — the explanation's length varies\n // with kind diversity, and the pricing carries the language's comment leader (or a\n // caller's custom builder), so a fixed estimate can pass a cut whose marker is\n // bigger than what it removes, and a marker that costs more than it removes grows\n // the output.\n return savingBytes(candidate, pricing) > 0 ? candidate : undefined;\n };\n\n /**\n * The UTF-8 bytes a sub-run `[from, to)` spans — read off the same offset index\n * `collapse` mints ranges from, so it is the exact upper bound on what collapsing\n * that sub-run could save, in O(1) and without slicing. The budget rung's span\n * bound depends on it being both exact and cheap.\n */\n const spanBytes = (group: readonly Unit[], from: number, to: number): number =>\n toByte.get(group[to - 1]!.end)! - toByte.get(group[from]!.start)!;\n\n // The first pass: every maximal run of siblings no focus term matched and no rule\n // pinned, collapsed whole. A run that earns no cut is remembered, not forgotten —\n // the budget rung below is the only reader of that list.\n const elisions: PlannedElision[] = [];\n const refused: (readonly Unit[])[] = [];\n for (const group of maximalRuns(units, matched)) {\n const cut = collapse(group);\n if (cut === undefined) refused.push(group);\n else elisions.push(cut);\n }\n\n // THE BUDGET RUNG — the structural answer to the lexical planner's context ladder.\n //\n // Until this existed, `planStructural` never read `input.budgetBytes` at all: it\n // took every profitable maximal run and stopped, which is right until the plan\n // comes back over budget with a profitable cut still on the table. The review's\n // case: 158 bytes, a budget of 120, and a run of three sibling classes worth 92\n // bytes left whole — because the *maximal* run also swallowed a `VERSION = 1`\n // statement above them, and a mixed-kind explanation (\"1 statement, 3 classes\")\n // priced a 106-byte marker against a 105-byte cut. The planner returned over budget\n // having elided nothing, while the three classes alone priced an 82-byte marker\n // against 92 bytes — a real saving of 10 bytes nobody was offered.\n //\n // So, and only when the plan is still over budget, each refused run is re-asked as\n // its own best profitable sub-run, earliest run first, stopping the moment the plan\n // fits. Four properties hold by construction rather than by care:\n //\n // - **A focus match is never cut.** A matched unit is not in any run, so no\n // sub-run of any run can contain one. Same for a pinned unit.\n // - **The output never grows.** Every candidate goes through `collapse`, which\n // mints nothing whose marker is not strictly cheaper than the bytes it removes.\n // - **It stays deterministic.** Sub-runs are enumerated start-ascending and\n // length-descending, and a tie in saving keeps the one already held — the\n // earliest start, then the longest. Both of the rung's bounds read that same\n // order, so a bounded sweep and an exhaustive one answer alike.\n // - **It stays bounded.** A refused run can be the whole file — the mid-line\n // refusal is about one character, not about size — so the sweep stops at the\n // first cut that fits the budget and skips every candidate whose span already\n // proves it cannot win. See {@link bestSubRun}.\n //\n // Budget pressure is the trigger, not profitability, because a maximal run is the\n // better *explanation*: one marker naming everything it hid beats two naming halves\n // of it. Trading that away is worth it to fit a budget and not worth it otherwise.\n const inputBytes = Buffer.byteLength(input.text, 'utf8');\n for (const group of refused) {\n const currentBytes = predictOutputBytes(inputBytes, elisions, pricing);\n if (currentBytes <= input.budgetBytes) break;\n // What this run would have to save to land the plan under budget — the sweep's\n // stopping condition, so a run that can fit the budget is priced a handful of\n // times instead of exhaustively. See {@link bestSubRun}.\n const enough = currentBytes - input.budgetBytes;\n const cut = bestSubRun(group, collapse, pricing, spanBytes, enough);\n if (cut !== undefined) elisions.push(cut);\n }\n\n return elisions.toSorted((a, b) => a.range.start - b.range.start);\n}\n\n/**\n * The runs the first pass collapses: every maximal contiguous group of units that no\n * focus term matched and no rule pinned. A matched or pinned unit ends the run before\n * it and starts none — which is what makes \"never cut a focus-matched declaration\" a\n * property of the *shape* of the data rather than a check anyone has to remember.\n */\nfunction maximalRuns(\n units: readonly Unit[],\n matched: readonly boolean[],\n): readonly (readonly Unit[])[] {\n const runs: Unit[][] = [];\n let run: Unit[] = [];\n for (let i = 0; i < units.length; i += 1) {\n if (matched[i] === true || units[i]!.pinned === true) {\n if (run.length > 0) runs.push(run);\n run = [];\n } else {\n run.push(units[i]!);\n }\n }\n if (run.length > 0) runs.push(run);\n return runs;\n}\n\n/**\n * The best cut available inside one refused run: the contiguous sub-run whose real\n * marker is cheapest against the bytes it removes.\n *\n * The enumeration is bounded, and it has to be. A run reaches here refused for any of\n * three reasons, and only one of them is \"it lost to a ~100-byte marker\": a run is also\n * refused when it holds fewer than `minSiblings` units, and — the one that bites —\n * when its last unit ends mid-line, because a line-comment marker would swallow the\n * kept code after it. That last rule is a property of one character in the file, not of\n * the run's size, so a *whole-file* run can land here: one bash script whose last line\n * is `cleanup_tmp; deploy_release --now` refuses its only maximal run, and an\n * unbounded `(from, to)` sweep over its thousands of units — each candidate slicing,\n * explaining and pricing — is cubic. Measured, before this bound: 19.5 KB / 373 ms,\n * 39 KB / 2.3 s, 79 KB / 17.2 s, 159 KB / 131 s. That is a CPU loop on tool-controlled\n * input, on the path the caller takes precisely when it is over budget — the normal\n * case. Two bounds, neither of which changes which cut a bounded sweep would have\n * found by exhaustion:\n *\n * - **`enough`** — the saving that lands the plan under budget. The rung exists to\n * fit a budget; the first candidate that does is the answer, and enumeration order\n * (start-ascending, length-descending) makes it the earliest, longest such cut.\n * This is what turns the whole-file case into two priced candidates.\n * - **The span bound** — a cut can never save more than the bytes it spans, so once\n * a candidate's span has shrunk to `bestSaving` or below, neither it nor any\n * shorter candidate from the same start can win, and once the *longest* candidate\n * from a start is that small, no later start can win either. Spans shrink\n * monotonically along both loops, so both breaks are exact, not heuristic: they\n * skip only candidates already proven to lose.\n */\nfunction bestSubRun(\n group: readonly Unit[],\n collapse: (group: readonly Unit[]) => PlannedElision | undefined,\n pricing: MarkerPricing,\n spanBytes: (group: readonly Unit[], from: number, to: number) => number,\n enough: number,\n): PlannedElision | undefined {\n let best: PlannedElision | undefined;\n let bestSaving = 0;\n for (let from = 0; from < group.length; from += 1) {\n if (spanBytes(group, from, group.length) <= bestSaving) break;\n for (let to = group.length; to > from; to -= 1) {\n if (spanBytes(group, from, to) <= bestSaving) break;\n const candidate = collapse(group.slice(from, to));\n if (candidate === undefined) continue;\n const saving = savingBytes(candidate, pricing);\n if (saving > bestSaving) {\n best = candidate;\n bestSaving = saving;\n if (saving >= enough) return best;\n }\n }\n }\n return best;\n}\n\n/** True when nothing but whitespace follows `index` on its own line. */\nfunction restOfLineIsBlank(text: string, index: number): boolean {\n for (let i = index; i < text.length; i += 1) {\n const ch = text[i]!;\n if (ch === '\\n') return true;\n if (!/\\s/.test(ch)) return false;\n }\n return true;\n}\n\n/**\n * Group the root's named children into units: each declaration plus the comment block\n * — and, in rust, the outer attributes — attached to it. A comment attaches when only\n * blank-free whitespace separates it from what follows — one newline at most — so a doc\n * comment travels with its declaration, while a comment left floating above a blank\n * line stands alone.\n *\n * Attributes attach *unconditionally*: tree-sitter-rust parses `#[inline]` as a\n * top-level sibling of the item it decorates, but the language's own rule is that an\n * outer attribute modifies the next item, blank lines or not. So once an attribute is\n * pending, the whole pending prefix (doc comments included) rides forward to the next\n * declaration — treating it as its own unit would let a collapse strip `#[derive(…)]`\n * and the doc comment above it off a kept declaration.\n */\nfunction unitsOf(root: Node, text: string, structure: LanguageStructure): readonly Unit[] {\n const units: Unit[] = [];\n /** Comments and attributes waiting to attach to the next declaration. */\n let pending: Node[] = [];\n /** Once true, blank lines no longer detach the pending prefix — see above. */\n let pendingHasAttribute = false;\n\n const flushPending = (): void => {\n if (pending.length === 0) return;\n units.push({\n start: pending[0]!.startIndex,\n end: pending[pending.length - 1]!.endIndex,\n kind: pendingHasAttribute ? 'attribute' : 'comment',\n pinned: !pendingHasAttribute && pending.some((node) => pinnedComment(node, text, structure)),\n });\n pending = [];\n pendingHasAttribute = false;\n };\n\n for (const child of root.namedChildren) {\n if (child === null) continue;\n if (structure.ridesBackwardTypes?.has(child.type) === true && units.length > 0) {\n // A node that is grammatically part of the *previous* statement, parsed as a\n // top-level sibling: tree-sitter-ruby emits a heredoc's body as a sibling of\n // the statement holding its `<<~SQL` opener. Splitting them lets a collapse\n // keep the opener and cut the body — an unterminated heredoc that swallows\n // every kept declaration after it, without a single ERROR node to show for\n // it. The body extends the preceding unit instead, so opener and body are\n // kept whole or collapsed whole, always.\n flushPending();\n const last = units[units.length - 1]!;\n units[units.length - 1] = { ...last, end: child.endIndex };\n continue;\n }\n if (\n structure.pinnedTypes?.has(child.type) === true ||\n pinnedDirective(child, text, structure)\n ) {\n // A pinned node (php's `<?php` tag, a `#!` shebang line, c's `#pragma once`)\n // is its own uncollapsible unit — nothing attaches to it, and no run may\n // swallow it.\n flushPending();\n units.push({\n start: child.startIndex,\n end: child.endIndex,\n kind: kindOf(child, structure),\n pinned: true,\n });\n continue;\n }\n const isComment = structure.commentTypes.has(child.type);\n const isAttribute = structure.attributeTypes.has(child.type);\n if (isComment || isAttribute) {\n if (\n pending.length > 0 &&\n !pendingHasAttribute &&\n !adjacent(text, pending[pending.length - 1]!.endIndex, child.startIndex)\n ) {\n flushPending();\n }\n pending.push(child);\n pendingHasAttribute ||= isAttribute;\n continue;\n }\n\n // tree-sitter-kotlin extends the import_list node over a doc comment that\n // follows it, so the KDoc of the first documented declaration after the imports\n // would be collapsed *with the imports* — a kept declaration losing its attached\n // doc comment. Split such trailing comments off: the unit ends at its last\n // non-comment token, and the comments ride forward like any other pending block.\n const { end: childEnd, trailing } =\n structure.trailingCommentSplitTypes?.has(child.type) === true\n ? splitTrailingComments(child, structure)\n : { end: child.endIndex, trailing: [] };\n\n const attached =\n pending.length > 0 &&\n (pendingHasAttribute ||\n adjacent(text, pending[pending.length - 1]!.endIndex, child.startIndex));\n if (attached && pending.some((node) => pinnedComment(node, text, structure))) {\n // A pinned comment (`//go:build`) governs the file, not the declaration it\n // happens to touch — keep it a standalone, uncollapsible unit either way.\n flushPending();\n units.push({\n start: child.startIndex,\n end: childEnd,\n kind: kindOf(child, structure),\n ...outlineName(child, structure, kindOf(child, structure)),\n });\n } else if (attached) {\n units.push({\n start: pending[0]!.startIndex,\n end: childEnd,\n kind: kindOf(child, structure),\n ...outlineName(child, structure, kindOf(child, structure)),\n });\n pending = [];\n pendingHasAttribute = false;\n } else {\n flushPending();\n units.push({\n start: child.startIndex,\n end: childEnd,\n kind: kindOf(child, structure),\n ...outlineName(child, structure, kindOf(child, structure)),\n });\n }\n for (const comment of trailing) pending.push(comment);\n }\n flushPending();\n return units;\n}\n\n/** Whether this comment node is one the language pins to the file. See {@link Unit}. */\nfunction pinnedComment(node: Node, text: string, structure: LanguageStructure): boolean {\n if (structure.pinnedCommentPattern === undefined) return false;\n if (!structure.commentTypes.has(node.type)) return false;\n return structure.pinnedCommentPattern.test(text.slice(node.startIndex, node.endIndex));\n}\n\n/**\n * Whether this non-comment node is pinned by a per-type text pattern. C's\n * `#pragma once` is a `preproc_call` like any other pragma, but collapsing it\n * silently changes header inclusion semantics — the same class as `//go:build`, on a\n * node kind a comment pattern cannot reach.\n */\nfunction pinnedDirective(node: Node, text: string, structure: LanguageStructure): boolean {\n const pattern = structure.pinnedPatternsByType?.[node.type];\n if (pattern === undefined) return false;\n return pattern.test(text.slice(node.startIndex, node.endIndex));\n}\n\n/**\n * The end of `node`'s last non-comment token, and every comment node after it. Used\n * for node types the grammar extends over comments that belong to what follows —\n * kotlin's import_list swallowing the next declaration's KDoc.\n */\nfunction splitTrailingComments(\n node: Node,\n structure: LanguageStructure,\n): { readonly end: number; readonly trailing: readonly Node[] } {\n const comments: Node[] = [];\n let end = node.startIndex;\n const walk = (current: Node): void => {\n if (structure.commentTypes.has(current.type)) {\n comments.push(current);\n return;\n }\n let hasChildren = false;\n for (const child of current.children) {\n if (child === null) continue;\n hasChildren = true;\n walk(child);\n }\n if (!hasChildren && current.endIndex > end) end = current.endIndex;\n };\n for (const child of node.children) {\n if (child !== null) walk(child);\n }\n return { end, trailing: comments.filter((comment) => comment.startIndex >= end) };\n}\n\n/** Nothing but whitespace between the two indices, and at most one newline. */\nfunction adjacent(text: string, end: number, start: number): boolean {\n const between = text.slice(end, start);\n if (!/^\\s*$/.test(between)) return false;\n return (between.match(/\\n/g) ?? []).length <= 1;\n}\n\nfunction matchUnits(\n units: readonly Unit[],\n input: PlanInput,\n options: StructuralPlannerOptions,\n): readonly boolean[] {\n const caseSensitive = options.caseSensitive ?? false;\n const focus = (input.focus ?? []).filter((term) => term.length > 0);\n const needles = caseSensitive ? focus : focus.map((term) => term.toLowerCase());\n\n return units.map((unit) => {\n if (needles.length === 0) return false;\n const raw = input.text.slice(unit.start, unit.end);\n const haystack = caseSensitive ? raw : raw.toLowerCase();\n return needles.some((needle) => haystack.includes(needle));\n });\n}\n\n/**\n * Law 2, for this rule: the explanation names the *kind* and the *count*, read off the\n * parse tree — `collapsed 3 sibling functions` — never a line count as the claim. A\n * mixed run says what it mixed: `collapsed 4 sibling declarations (3 functions,\n * 1 class)` — and when the run holds anything that is not a declaration (a statement,\n * a floating comment, an unparsed region), the heading says `nodes`, because calling\n * a parse error a declaration would be the marker lying about the tree.\n */\nfunction explain(group: readonly Unit[]): string {\n const counts = new Map<string, number>();\n for (const unit of group) counts.set(unit.kind, (counts.get(unit.kind) ?? 0) + 1);\n\n const total = group.length;\n if (counts.size === 1) {\n const kind = group[0]!.kind;\n return `collapsed ${String(total)} sibling ${countNoun(kind, total)}`;\n }\n const parts = [...counts.entries()].map(\n ([kind, count]) => `${String(count)} ${countNoun(kind, count)}`,\n );\n const heading = [...counts.keys()].every((kind) => !NON_DECLARATION_KINDS.has(kind))\n ? 'declarations'\n : 'nodes';\n return `collapsed ${String(total)} sibling ${heading} (${parts.join(', ')})`;\n}\n\n/** The kind labels that are not declarations, so a mixed heading never overclaims. */\nconst NON_DECLARATION_KINDS: ReadonlySet<string> = new Set([\n 'statement',\n 'comment',\n 'unparsed region',\n 'package clause',\n 'package header',\n 'package declaration',\n 'attribute',\n 'command',\n 'variable assignment',\n 'include directive',\n 'shebang',\n 'php tag',\n 'html section',\n 'heredoc body',\n 'preprocessor directive',\n 'preprocessor conditional',\n]);\n\nfunction countNoun(kind: string, count: number): string {\n if (count === 1) return kind;\n if (/[^aeiou]y$/.test(kind)) return `${kind.slice(0, -1)}ies`;\n return /(?:s|sh|ch)$/.test(kind) ? `${kind}es` : `${kind}s`;\n}\n\n/**\n * The kind of a declaration, unwrapping wrappers (`export …`, `@decorator`) so the\n * marker names what it hides. What the language's map does not name is still labelled\n * honestly — see {@link LanguageStructure.kindLabels} for the labelling rules.\n */\nfunction kindOf(node: Node, structure: LanguageStructure): string {\n const wrapperFallback = structure.wrapperTypes[node.type];\n if (wrapperFallback !== undefined) {\n for (const child of node.namedChildren) {\n if (child === null || structure.commentTypes.has(child.type)) continue;\n const label = structure.kindLabels[child.type];\n if (label !== undefined) return label;\n }\n return wrapperFallback;\n }\n const label = structure.kindLabels[node.type];\n if (label !== undefined) return label;\n if (node.type === 'ERROR') return 'unparsed region';\n if (node.type.endsWith('_statement') || node.type === 'statement_block') return 'statement';\n return 'declaration';\n}\n\n/**\n * `{ name }` when the unit is a declaration with a name, `{}` otherwise — spread into\n * a unit. Only declarations are named: an import, a package clause or a bare statement\n * has an identifier in it too (`require 'json'`, `using System;`) but naming it would\n * put a word on the outline that names nothing a model could be looking for.\n */\nfunction outlineName(\n node: Node,\n structure: LanguageStructure,\n kind: string,\n): { readonly name?: string } {\n if (UNNAMED_KINDS.has(kind)) return {};\n const name = nameOf(node, structure, 0);\n return name === undefined ? {} : { name };\n}\n\n/**\n * Kinds whose units carry no outline name: everything that is not a declaration, plus\n * the declaration-shaped kinds that name a *module* rather than something defined\n * here (imports, includes, usings, package clauses).\n */\nconst UNNAMED_KINDS: ReadonlySet<string> = new Set([\n ...NON_DECLARATION_KINDS,\n 'import statement',\n 'import declaration',\n 'import list',\n 'include directive',\n 'using directive',\n 'using declaration',\n 'package clause',\n 'package declaration',\n 'package header',\n]);\n\n/**\n * The node types that *are* a name, across the bundled grammars: `identifier` almost\n * everywhere, `simple_identifier` in kotlin and swift, `type_identifier` for a rust\n * `impl` block's type or a C typedef, `constant` for a ruby class, `name` in php,\n * `word` for a bash function. A grammar that names its declaration through a field\n * is read through the field first; this set is the fallback for the ones that do not.\n */\nconst NAME_NODE_TYPES: ReadonlySet<string> = new Set([\n 'identifier',\n 'simple_identifier',\n 'type_identifier',\n 'property_identifier',\n 'field_identifier',\n 'constant',\n 'name',\n 'word',\n]);\n\n/** How deep the name search descends through wrappers and declarators before giving up. */\nconst NAME_SEARCH_DEPTH = 4;\n\n/**\n * The declaration's name, read off the tree — or `undefined`, honestly, when the node\n * has none (an expression statement, an import, an unparsed region). Three readings in\n * order, each a fact the grammar states rather than a guess about the text:\n *\n * 1. A wrapper (`export …`, `@decorator`) names what it wraps, so descend to it.\n * 2. The `name` field, which most grammars put on a declaration — and failing that\n * the `declarator` chain C and C++ use (`int (*fn)(void)` names `fn` three levels\n * down), which is followed to the identifier at its end.\n * 3. The first named child that is itself a name node, or that carries a `name`\n * field of its own (go's `type_declaration` → `type_spec`, a `lexical_declaration`\n * → `variable_declarator`).\n *\n * The search is bounded so a pathological tree cannot make it quadratic, and a name\n * is returned only when the tree says so — a wrong name on an outline would send a\n * model to retrieve the wrong marker, which is worse than no outline at all.\n */\nfunction nameOf(node: Node, structure: LanguageStructure, depth: number): string | undefined {\n if (depth > NAME_SEARCH_DEPTH) return undefined;\n if (NAME_NODE_TYPES.has(node.type)) return node.text;\n if (structure.wrapperTypes[node.type] !== undefined) {\n for (const child of node.namedChildren) {\n if (child === null || structure.commentTypes.has(child.type)) continue;\n const inner = nameOf(child, structure, depth + 1);\n if (inner !== undefined) return inner;\n }\n return undefined;\n }\n const byField = node.childForFieldName('name');\n if (byField !== null) {\n return NAME_NODE_TYPES.has(byField.type) || byField.namedChildCount === 0\n ? byField.text\n : (nameOf(byField, structure, depth + 1) ?? byField.text);\n }\n const declarator = node.childForFieldName('declarator');\n if (declarator !== null) return nameOf(declarator, structure, depth + 1);\n for (const child of node.namedChildren) {\n if (child === null || structure.commentTypes.has(child.type)) continue;\n if (NAME_NODE_TYPES.has(child.type)) return child.text;\n if (\n child.childForFieldName('name') !== null ||\n child.childForFieldName('declarator') !== null\n ) {\n return nameOf(child, structure, depth + 1);\n }\n }\n return undefined;\n}\n"]}
|
package/dist/retrieve.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { ElisionStore, RetrieveTool } from './types.ts';
|
|
1
|
+
import type { ElisionStore, RetrieveBatchTool, RetrievedBlock, RetrieveTool } from './types.ts';
|
|
2
2
|
/** The tool name smelt's markers reference. Consumers hard-code it; do not rename it. */
|
|
3
3
|
export declare const RETRIEVE_TOOL_NAME = "smelt_retrieve";
|
|
4
|
+
/** The batched sibling's name. Also stable; also never a rename of the one above. */
|
|
5
|
+
export declare const RETRIEVE_BATCH_TOOL_NAME = "smelt_retrieve_batch";
|
|
4
6
|
/**
|
|
5
7
|
* Wrap a store as the tool a model calls.
|
|
6
8
|
*
|
|
@@ -19,4 +21,23 @@ export declare const RETRIEVE_TOOL_NAME = "smelt_retrieve";
|
|
|
19
21
|
* tool name and behaviour guarantee covers.
|
|
20
22
|
*/
|
|
21
23
|
export declare function createRetrieveTool(store: ElisionStore): RetrieveTool;
|
|
24
|
+
/**
|
|
25
|
+
* Retrieve each hash through the counted path, keeping the store's own refusal per
|
|
26
|
+
* hash. The one loop behind both {@link createRetrieveBatchTool} and the
|
|
27
|
+
* `retrieveMany` op: a batch is N single retrievals that share one round trip, and
|
|
28
|
+
* *nothing else* — `store.retrieve` journals every hit and every miss exactly as a
|
|
29
|
+
* single call would, so the expansion rate keeps its exact meaning across both
|
|
30
|
+
* shapes. Only a {@link SmeltError} is kept inside a block; anything else is not the
|
|
31
|
+
* store saying no, and propagates.
|
|
32
|
+
*/
|
|
33
|
+
export declare function retrieveEach(store: ElisionStore, hashes: readonly string[]): readonly RetrievedBlock[];
|
|
34
|
+
/**
|
|
35
|
+
* Wrap a store as the batched tool a model calls beside {@link createRetrieveTool}.
|
|
36
|
+
*
|
|
37
|
+
* Same store, same counters, same strict-mode shape. The two tools are siblings, not
|
|
38
|
+
* a replacement: `smelt_retrieve` is the name every marker's `retrieve("hash")` points
|
|
39
|
+
* at and is frozen; this one exists for the measured case where several markers
|
|
40
|
+
* matter at once and the round trips, not the bytes, are the cost.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createRetrieveBatchTool(store: ElisionStore): RetrieveBatchTool;
|
|
22
43
|
//# sourceMappingURL=retrieve.d.ts.map
|
package/dist/retrieve.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retrieve.d.ts","sourceRoot":"","sources":["../src/retrieve.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"retrieve.d.ts","sourceRoot":"","sources":["../src/retrieve.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEhG,yFAAyF;AACzF,eAAO,MAAM,kBAAkB,mBAAmB,CAAC;AAEnD,qFAAqF;AACrF,eAAO,MAAM,wBAAwB,yBAAyB,CAAC;AAwB/D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY,CAcpE;AAYD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,SAAS,MAAM,EAAE,GACxB,SAAS,cAAc,EAAE,CAS3B;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,YAAY,GAAG,iBAAiB,CAkB9E","sourcesContent":["import { defaultMarker } from './apply.ts';\nimport { SmeltError } from './errors.ts';\nimport type { ElisionStore, RetrieveBatchTool, RetrievedBlock, RetrieveTool } from './types.ts';\n\n/** The tool name smelt's markers reference. Consumers hard-code it; do not rename it. */\nexport const RETRIEVE_TOOL_NAME = 'smelt_retrieve';\n\n/** The batched sibling's name. Also stable; also never a rename of the one above. */\nexport const RETRIEVE_BATCH_TOOL_NAME = 'smelt_retrieve_batch';\n\n/**\n * The example marker inside the tool description is *rendered by the real marker\n * builder*, never hand-written — the description is the one string a model reads to\n * recognize markers, and an example whose shape drifted from the wire format\n * (`<<smelt: …>>` when real markers say `<<smelt/v1: …>>`) would teach the model to\n * miss every marker it actually receives. `test/guards/marker-format.test.ts` pins\n * this to `MARKER_FORMAT_VERSION`.\n */\nconst EXAMPLE_MARKER = defaultMarker({\n hash: 'a1b2c3d4e5f60718',\n bytes: 412,\n rule: 'sibling-collapse',\n explanation: 'collapsed 3 sibling functions',\n});\n\nconst DESCRIPTION =\n 'Return the exact original text that was elided from a previous tool result. ' +\n `Context you were given may contain markers like \\`${EXAMPLE_MARKER}\\`. ` +\n 'Call this with that hash to get those bytes ' +\n 'back verbatim. Nothing was deleted — it is all still here. Ask whenever the elided ' +\n 'material might matter; guessing at what a marker hid is never correct.';\n\n/**\n * Wrap a store as the tool a model calls.\n *\n * Why the description says \"ask whenever it might matter\": under-retrieval is the\n * failure mode that looks like success. A model that never calls this produces a\n * confident answer built on material it never saw, and the retrieve rate reads 0% —\n * which is indistinguishable from perfect pruning. Encouraging retrieval keeps the\n * signal in {@link ElisionStore.stats} honest.\n *\n * **The schema is strict-mode shaped on purpose.** `additionalProperties: false`, and\n * `required` naming every property — the two rules OpenAI's structured-outputs strict\n * mode enforces before it will register a function at all. Without them a whole class\n * of consumer simply cannot expose this tool. It says nothing new: `hash` was always\n * the only key `invoke` reads, and an extra key was always ignored. A schema that is\n * strictly more precise about the same shape is not a change to the wire surface the\n * tool name and behaviour guarantee covers.\n */\nexport function createRetrieveTool(store: ElisionStore): RetrieveTool {\n return {\n name: RETRIEVE_TOOL_NAME,\n description: DESCRIPTION,\n inputSchema: {\n type: 'object',\n properties: {\n hash: { type: 'string', description: 'The hash from a marker\\'s retrieve(\"hash\").' },\n },\n required: ['hash'],\n additionalProperties: false,\n },\n invoke: ({ hash }) => store.retrieve(hash),\n };\n}\n\nconst BATCH_DESCRIPTION =\n 'Return the exact original text for several markers at once — the same bytes ' +\n `${RETRIEVE_TOOL_NAME} returns for one hash, in one call. Context you were given may ` +\n `contain markers like \\`${EXAMPLE_MARKER}\\`. Pass every hash you need in one array: ` +\n 'each hash comes back as its own block, in the order asked, and a hash the store does ' +\n 'not hold comes back as a refusal in its block without failing the others. Prefer this ' +\n `over repeated ${RETRIEVE_TOOL_NAME} calls whenever more than one marker matters — every ` +\n 'call is a new request, and asking for eighteen blobs one at a time pays for the whole ' +\n 'conversation eighteen times.';\n\n/**\n * Retrieve each hash through the counted path, keeping the store's own refusal per\n * hash. The one loop behind both {@link createRetrieveBatchTool} and the\n * `retrieveMany` op: a batch is N single retrievals that share one round trip, and\n * *nothing else* — `store.retrieve` journals every hit and every miss exactly as a\n * single call would, so the expansion rate keeps its exact meaning across both\n * shapes. Only a {@link SmeltError} is kept inside a block; anything else is not the\n * store saying no, and propagates.\n */\nexport function retrieveEach(\n store: ElisionStore,\n hashes: readonly string[],\n): readonly RetrievedBlock[] {\n return hashes.map((hash) => {\n try {\n return { hash, text: store.retrieve(hash) };\n } catch (error) {\n if (error instanceof SmeltError) return { hash, error };\n throw error;\n }\n });\n}\n\n/**\n * Wrap a store as the batched tool a model calls beside {@link createRetrieveTool}.\n *\n * Same store, same counters, same strict-mode shape. The two tools are siblings, not\n * a replacement: `smelt_retrieve` is the name every marker's `retrieve(\"hash\")` points\n * at and is frozen; this one exists for the measured case where several markers\n * matter at once and the round trips, not the bytes, are the cost.\n */\nexport function createRetrieveBatchTool(store: ElisionStore): RetrieveBatchTool {\n return {\n name: RETRIEVE_BATCH_TOOL_NAME,\n description: BATCH_DESCRIPTION,\n inputSchema: {\n type: 'object',\n properties: {\n hashes: {\n type: 'array',\n items: { type: 'string' },\n description: 'The hashes from the markers\\' retrieve(\"hash\"), every one you need.',\n },\n },\n required: ['hashes'],\n additionalProperties: false,\n },\n invoke: ({ hashes }) => retrieveEach(store, hashes),\n };\n}\n"]}
|
package/dist/retrieve.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { defaultMarker } from './apply.js';
|
|
2
|
+
import { SmeltError } from './errors.js';
|
|
2
3
|
/** The tool name smelt's markers reference. Consumers hard-code it; do not rename it. */
|
|
3
4
|
export const RETRIEVE_TOOL_NAME = 'smelt_retrieve';
|
|
5
|
+
/** The batched sibling's name. Also stable; also never a rename of the one above. */
|
|
6
|
+
export const RETRIEVE_BATCH_TOOL_NAME = 'smelt_retrieve_batch';
|
|
4
7
|
/**
|
|
5
8
|
* The example marker inside the tool description is *rendered by the real marker
|
|
6
9
|
* builder*, never hand-written — the description is the one string a model reads to
|
|
@@ -52,4 +55,60 @@ export function createRetrieveTool(store) {
|
|
|
52
55
|
invoke: ({ hash }) => store.retrieve(hash),
|
|
53
56
|
};
|
|
54
57
|
}
|
|
58
|
+
const BATCH_DESCRIPTION = 'Return the exact original text for several markers at once — the same bytes ' +
|
|
59
|
+
`${RETRIEVE_TOOL_NAME} returns for one hash, in one call. Context you were given may ` +
|
|
60
|
+
`contain markers like \`${EXAMPLE_MARKER}\`. Pass every hash you need in one array: ` +
|
|
61
|
+
'each hash comes back as its own block, in the order asked, and a hash the store does ' +
|
|
62
|
+
'not hold comes back as a refusal in its block without failing the others. Prefer this ' +
|
|
63
|
+
`over repeated ${RETRIEVE_TOOL_NAME} calls whenever more than one marker matters — every ` +
|
|
64
|
+
'call is a new request, and asking for eighteen blobs one at a time pays for the whole ' +
|
|
65
|
+
'conversation eighteen times.';
|
|
66
|
+
/**
|
|
67
|
+
* Retrieve each hash through the counted path, keeping the store's own refusal per
|
|
68
|
+
* hash. The one loop behind both {@link createRetrieveBatchTool} and the
|
|
69
|
+
* `retrieveMany` op: a batch is N single retrievals that share one round trip, and
|
|
70
|
+
* *nothing else* — `store.retrieve` journals every hit and every miss exactly as a
|
|
71
|
+
* single call would, so the expansion rate keeps its exact meaning across both
|
|
72
|
+
* shapes. Only a {@link SmeltError} is kept inside a block; anything else is not the
|
|
73
|
+
* store saying no, and propagates.
|
|
74
|
+
*/
|
|
75
|
+
export function retrieveEach(store, hashes) {
|
|
76
|
+
return hashes.map((hash) => {
|
|
77
|
+
try {
|
|
78
|
+
return { hash, text: store.retrieve(hash) };
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (error instanceof SmeltError)
|
|
82
|
+
return { hash, error };
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Wrap a store as the batched tool a model calls beside {@link createRetrieveTool}.
|
|
89
|
+
*
|
|
90
|
+
* Same store, same counters, same strict-mode shape. The two tools are siblings, not
|
|
91
|
+
* a replacement: `smelt_retrieve` is the name every marker's `retrieve("hash")` points
|
|
92
|
+
* at and is frozen; this one exists for the measured case where several markers
|
|
93
|
+
* matter at once and the round trips, not the bytes, are the cost.
|
|
94
|
+
*/
|
|
95
|
+
export function createRetrieveBatchTool(store) {
|
|
96
|
+
return {
|
|
97
|
+
name: RETRIEVE_BATCH_TOOL_NAME,
|
|
98
|
+
description: BATCH_DESCRIPTION,
|
|
99
|
+
inputSchema: {
|
|
100
|
+
type: 'object',
|
|
101
|
+
properties: {
|
|
102
|
+
hashes: {
|
|
103
|
+
type: 'array',
|
|
104
|
+
items: { type: 'string' },
|
|
105
|
+
description: 'The hashes from the markers\' retrieve("hash"), every one you need.',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
required: ['hashes'],
|
|
109
|
+
additionalProperties: false,
|
|
110
|
+
},
|
|
111
|
+
invoke: ({ hashes }) => retrieveEach(store, hashes),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
55
114
|
//# sourceMappingURL=retrieve.js.map
|
package/dist/retrieve.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retrieve.js","sourceRoot":"","sources":["../src/retrieve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"retrieve.js","sourceRoot":"","sources":["../src/retrieve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,yFAAyF;AACzF,MAAM,CAAC,MAAM,kBAAkB,GAAG,gBAAgB,CAAC;AAEnD,qFAAqF;AACrF,MAAM,CAAC,MAAM,wBAAwB,GAAG,sBAAsB,CAAC;AAE/D;;;;;;;GAOG;AACH,MAAM,cAAc,GAAG,aAAa,CAAC;IACnC,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,GAAG;IACV,IAAI,EAAE,kBAAkB;IACxB,WAAW,EAAE,+BAA+B;CAC7C,CAAC,CAAC;AAEH,MAAM,WAAW,GACf,8EAA8E;IAC9E,qDAAqD,cAAc,MAAM;IACzE,8CAA8C;IAC9C,qFAAqF;IACrF,wEAAwE,CAAC;AAE3E;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAmB;IACpD,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,WAAW,EAAE,WAAW;QACxB,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,6CAA6C,EAAE;aACrF;YACD,QAAQ,EAAE,CAAC,MAAM,CAAC;YAClB,oBAAoB,EAAE,KAAK;SAC5B;QACD,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;KAC3C,CAAC;AACJ,CAAC;AAED,MAAM,iBAAiB,GACrB,8EAA8E;IAC9E,GAAG,kBAAkB,iEAAiE;IACtF,0BAA0B,cAAc,6CAA6C;IACrF,uFAAuF;IACvF,wFAAwF;IACxF,iBAAiB,kBAAkB,uDAAuD;IAC1F,wFAAwF;IACxF,8BAA8B,CAAC;AAEjC;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAmB,EACnB,MAAyB;IAEzB,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACzB,IAAI,CAAC;YACH,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,UAAU;gBAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;YACxD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAmB;IACzD,OAAO;QACL,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EAAE,iBAAiB;QAC9B,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,MAAM,EAAE;oBACN,IAAI,EAAE,OAAO;oBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACzB,WAAW,EAAE,qEAAqE;iBACnF;aACF;YACD,QAAQ,EAAE,CAAC,QAAQ,CAAC;YACpB,oBAAoB,EAAE,KAAK;SAC5B;QACD,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC;KACpD,CAAC;AACJ,CAAC","sourcesContent":["import { defaultMarker } from './apply.ts';\nimport { SmeltError } from './errors.ts';\nimport type { ElisionStore, RetrieveBatchTool, RetrievedBlock, RetrieveTool } from './types.ts';\n\n/** The tool name smelt's markers reference. Consumers hard-code it; do not rename it. */\nexport const RETRIEVE_TOOL_NAME = 'smelt_retrieve';\n\n/** The batched sibling's name. Also stable; also never a rename of the one above. */\nexport const RETRIEVE_BATCH_TOOL_NAME = 'smelt_retrieve_batch';\n\n/**\n * The example marker inside the tool description is *rendered by the real marker\n * builder*, never hand-written — the description is the one string a model reads to\n * recognize markers, and an example whose shape drifted from the wire format\n * (`<<smelt: …>>` when real markers say `<<smelt/v1: …>>`) would teach the model to\n * miss every marker it actually receives. `test/guards/marker-format.test.ts` pins\n * this to `MARKER_FORMAT_VERSION`.\n */\nconst EXAMPLE_MARKER = defaultMarker({\n hash: 'a1b2c3d4e5f60718',\n bytes: 412,\n rule: 'sibling-collapse',\n explanation: 'collapsed 3 sibling functions',\n});\n\nconst DESCRIPTION =\n 'Return the exact original text that was elided from a previous tool result. ' +\n `Context you were given may contain markers like \\`${EXAMPLE_MARKER}\\`. ` +\n 'Call this with that hash to get those bytes ' +\n 'back verbatim. Nothing was deleted — it is all still here. Ask whenever the elided ' +\n 'material might matter; guessing at what a marker hid is never correct.';\n\n/**\n * Wrap a store as the tool a model calls.\n *\n * Why the description says \"ask whenever it might matter\": under-retrieval is the\n * failure mode that looks like success. A model that never calls this produces a\n * confident answer built on material it never saw, and the retrieve rate reads 0% —\n * which is indistinguishable from perfect pruning. Encouraging retrieval keeps the\n * signal in {@link ElisionStore.stats} honest.\n *\n * **The schema is strict-mode shaped on purpose.** `additionalProperties: false`, and\n * `required` naming every property — the two rules OpenAI's structured-outputs strict\n * mode enforces before it will register a function at all. Without them a whole class\n * of consumer simply cannot expose this tool. It says nothing new: `hash` was always\n * the only key `invoke` reads, and an extra key was always ignored. A schema that is\n * strictly more precise about the same shape is not a change to the wire surface the\n * tool name and behaviour guarantee covers.\n */\nexport function createRetrieveTool(store: ElisionStore): RetrieveTool {\n return {\n name: RETRIEVE_TOOL_NAME,\n description: DESCRIPTION,\n inputSchema: {\n type: 'object',\n properties: {\n hash: { type: 'string', description: 'The hash from a marker\\'s retrieve(\"hash\").' },\n },\n required: ['hash'],\n additionalProperties: false,\n },\n invoke: ({ hash }) => store.retrieve(hash),\n };\n}\n\nconst BATCH_DESCRIPTION =\n 'Return the exact original text for several markers at once — the same bytes ' +\n `${RETRIEVE_TOOL_NAME} returns for one hash, in one call. Context you were given may ` +\n `contain markers like \\`${EXAMPLE_MARKER}\\`. Pass every hash you need in one array: ` +\n 'each hash comes back as its own block, in the order asked, and a hash the store does ' +\n 'not hold comes back as a refusal in its block without failing the others. Prefer this ' +\n `over repeated ${RETRIEVE_TOOL_NAME} calls whenever more than one marker matters — every ` +\n 'call is a new request, and asking for eighteen blobs one at a time pays for the whole ' +\n 'conversation eighteen times.';\n\n/**\n * Retrieve each hash through the counted path, keeping the store's own refusal per\n * hash. The one loop behind both {@link createRetrieveBatchTool} and the\n * `retrieveMany` op: a batch is N single retrievals that share one round trip, and\n * *nothing else* — `store.retrieve` journals every hit and every miss exactly as a\n * single call would, so the expansion rate keeps its exact meaning across both\n * shapes. Only a {@link SmeltError} is kept inside a block; anything else is not the\n * store saying no, and propagates.\n */\nexport function retrieveEach(\n store: ElisionStore,\n hashes: readonly string[],\n): readonly RetrievedBlock[] {\n return hashes.map((hash) => {\n try {\n return { hash, text: store.retrieve(hash) };\n } catch (error) {\n if (error instanceof SmeltError) return { hash, error };\n throw error;\n }\n });\n}\n\n/**\n * Wrap a store as the batched tool a model calls beside {@link createRetrieveTool}.\n *\n * Same store, same counters, same strict-mode shape. The two tools are siblings, not\n * a replacement: `smelt_retrieve` is the name every marker's `retrieve(\"hash\")` points\n * at and is frozen; this one exists for the measured case where several markers\n * matter at once and the round trips, not the bytes, are the cost.\n */\nexport function createRetrieveBatchTool(store: ElisionStore): RetrieveBatchTool {\n return {\n name: RETRIEVE_BATCH_TOOL_NAME,\n description: BATCH_DESCRIPTION,\n inputSchema: {\n type: 'object',\n properties: {\n hashes: {\n type: 'array',\n items: { type: 'string' },\n description: 'The hashes from the markers\\' retrieve(\"hash\"), every one you need.',\n },\n },\n required: ['hashes'],\n additionalProperties: false,\n },\n invoke: ({ hashes }) => retrieveEach(store, hashes),\n };\n}\n"]}
|
package/dist/smelter.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { MarkerBuilder } from './apply.ts';
|
|
2
|
+
import type { DiffPlannerOptions } from './plan/diff.ts';
|
|
3
|
+
import type { JsonPlannerOptions } from './plan/json.ts';
|
|
2
4
|
import type { LexicalPlannerOptions } from './plan/lexical.ts';
|
|
3
5
|
import type { Strategy } from './plan/planners.ts';
|
|
4
6
|
import type { StructuralPlannerOptions } from './plan/structural.ts';
|
|
@@ -33,6 +35,8 @@ export interface SmelterConfig {
|
|
|
33
35
|
readonly measure?: Measure;
|
|
34
36
|
readonly lexical?: LexicalPlannerOptions;
|
|
35
37
|
readonly structural?: StructuralPlannerOptions;
|
|
38
|
+
readonly json?: JsonPlannerOptions;
|
|
39
|
+
readonly diff?: DiffPlannerOptions;
|
|
36
40
|
}
|
|
37
41
|
/** Options for one `smelt()` call. `budgetBytes` may come from the smelter instead. */
|
|
38
42
|
export interface SmeltCallOptions {
|