@smeltjs/core 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/apply.d.ts.map +1 -1
- package/dist/apply.js.map +1 -1
- package/dist/cache/prefix.d.ts.map +1 -1
- package/dist/cache/prefix.js.map +1 -1
- package/dist/cli/args.d.ts +16 -70
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +18 -338
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/bin.d.ts.map +1 -1
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/config.d.ts +40 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +55 -1
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/hooks.d.ts +33 -38
- package/dist/cli/hooks.d.ts.map +1 -1
- package/dist/cli/hooks.js +140 -485
- package/dist/cli/hooks.js.map +1 -1
- package/dist/cli/init.d.ts +30 -15
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +167 -38
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/report.d.ts.map +1 -1
- package/dist/cli/report.js +2 -2
- package/dist/cli/report.js.map +1 -1
- package/dist/cli/run.d.ts +25 -88
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +32 -341
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/shell.d.ts +106 -0
- package/dist/cli/shell.d.ts.map +1 -0
- package/dist/cli/shell.js +100 -0
- package/dist/cli/shell.js.map +1 -0
- package/dist/cli/subcommands/flags.d.ts +141 -0
- package/dist/cli/subcommands/flags.d.ts.map +1 -0
- package/dist/cli/subcommands/flags.js +208 -0
- package/dist/cli/subcommands/flags.js.map +1 -0
- package/dist/cli/subcommands/hooks.d.ts +18 -0
- package/dist/cli/subcommands/hooks.d.ts.map +1 -0
- package/dist/cli/subcommands/hooks.js +64 -0
- package/dist/cli/subcommands/hooks.js.map +1 -0
- package/dist/cli/subcommands/init.d.ts +19 -0
- package/dist/cli/subcommands/init.d.ts.map +1 -0
- package/dist/cli/subcommands/init.js +44 -0
- package/dist/cli/subcommands/init.js.map +1 -0
- package/dist/cli/subcommands/map.d.ts +75 -0
- package/dist/cli/subcommands/map.d.ts.map +1 -0
- package/dist/cli/subcommands/map.js +132 -0
- package/dist/cli/subcommands/map.js.map +1 -0
- package/dist/cli/subcommands/registry.d.ts +80 -0
- package/dist/cli/subcommands/registry.d.ts.map +1 -0
- package/dist/cli/subcommands/registry.js +138 -0
- package/dist/cli/subcommands/registry.js.map +1 -0
- package/dist/cli/subcommands/retrieve.d.ts +74 -0
- package/dist/cli/subcommands/retrieve.d.ts.map +1 -0
- package/dist/cli/subcommands/retrieve.js +84 -0
- package/dist/cli/subcommands/retrieve.js.map +1 -0
- package/dist/cli/subcommands/smelt.d.ts +107 -0
- package/dist/cli/subcommands/smelt.d.ts.map +1 -0
- package/dist/cli/subcommands/smelt.js +304 -0
- package/dist/cli/subcommands/smelt.js.map +1 -0
- package/dist/cli/subcommands/stats.d.ts +43 -0
- package/dist/cli/subcommands/stats.d.ts.map +1 -0
- package/dist/cli/subcommands/stats.js +62 -0
- package/dist/cli/subcommands/stats.js.map +1 -0
- package/dist/cli/subcommands/subcommand.d.ts +113 -0
- package/dist/cli/subcommands/subcommand.d.ts.map +1 -0
- package/dist/cli/subcommands/subcommand.js +3 -0
- package/dist/cli/subcommands/subcommand.js.map +1 -0
- package/dist/cli/usage.d.ts +3 -0
- package/dist/cli/usage.d.ts.map +1 -0
- package/dist/cli/usage.js +95 -0
- package/dist/cli/usage.js.map +1 -0
- package/dist/detect.d.ts.map +1 -1
- package/dist/detect.js.map +1 -1
- package/dist/errors.d.ts +21 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +33 -1
- package/dist/errors.js.map +1 -1
- package/dist/harness/aider.d.ts +10 -0
- package/dist/harness/aider.d.ts.map +1 -0
- package/dist/harness/aider.js +21 -0
- package/dist/harness/aider.js.map +1 -0
- package/dist/harness/claude-code.d.ts +3 -0
- package/dist/harness/claude-code.d.ts.map +1 -0
- package/dist/harness/claude-code.js +68 -0
- package/dist/harness/claude-code.js.map +1 -0
- package/dist/harness/cline.d.ts +3 -0
- package/dist/harness/cline.d.ts.map +1 -0
- package/dist/harness/cline.js +54 -0
- package/dist/harness/cline.js.map +1 -0
- package/dist/harness/codex.d.ts +3 -0
- package/dist/harness/codex.d.ts.map +1 -0
- package/dist/harness/codex.js +91 -0
- package/dist/harness/codex.js.map +1 -0
- package/dist/harness/cursor.d.ts +3 -0
- package/dist/harness/cursor.d.ts.map +1 -0
- package/dist/harness/cursor.js +54 -0
- package/dist/harness/cursor.js.map +1 -0
- package/dist/harness/gemini.d.ts +3 -0
- package/dist/harness/gemini.d.ts.map +1 -0
- package/dist/harness/gemini.js +59 -0
- package/dist/harness/gemini.js.map +1 -0
- package/dist/harness/grok.d.ts +3 -0
- package/dist/harness/grok.d.ts.map +1 -0
- package/dist/harness/grok.js +46 -0
- package/dist/harness/grok.js.map +1 -0
- package/dist/harness/hermes.d.ts +3 -0
- package/dist/harness/hermes.d.ts.map +1 -0
- package/dist/harness/hermes.js +71 -0
- package/dist/harness/hermes.js.map +1 -0
- package/dist/harness/kilocode.d.ts +3 -0
- package/dist/harness/kilocode.d.ts.map +1 -0
- package/dist/harness/kilocode.js +35 -0
- package/dist/harness/kilocode.js.map +1 -0
- package/dist/harness/opencode.d.ts +3 -0
- package/dist/harness/opencode.d.ts.map +1 -0
- package/dist/harness/opencode.js +97 -0
- package/dist/harness/opencode.js.map +1 -0
- package/dist/harness/paths.d.ts +17 -0
- package/dist/harness/paths.d.ts.map +1 -0
- package/dist/harness/paths.js +45 -0
- package/dist/harness/paths.js.map +1 -0
- package/dist/harness/profile.d.ts +184 -0
- package/dist/harness/profile.d.ts.map +1 -0
- package/dist/harness/profile.js +26 -0
- package/dist/harness/profile.js.map +1 -0
- package/dist/harness/registry.d.ts +51 -0
- package/dist/harness/registry.d.ts.map +1 -0
- package/dist/harness/registry.js +87 -0
- package/dist/harness/registry.js.map +1 -0
- package/dist/harness/snippet.d.ts +19 -0
- package/dist/harness/snippet.d.ts.map +1 -0
- package/dist/harness/snippet.js +41 -0
- package/dist/harness/snippet.js.map +1 -0
- package/dist/hash.d.ts.map +1 -1
- package/dist/hash.js.map +1 -1
- package/dist/hooks/guard-core.d.ts +14 -14
- package/dist/hooks/guard-core.d.ts.map +1 -1
- package/dist/hooks/guard-core.js +15 -37
- package/dist/hooks/guard-core.js.map +1 -1
- package/dist/hooks/shim.d.ts +95 -4
- package/dist/hooks/shim.d.ts.map +1 -1
- package/dist/hooks/shim.js +87 -2
- package/dist/hooks/shim.js.map +1 -1
- package/dist/hooks/shims/claude-code.d.ts +5 -18
- package/dist/hooks/shims/claude-code.d.ts.map +1 -1
- package/dist/hooks/shims/claude-code.js +9 -56
- package/dist/hooks/shims/claude-code.js.map +1 -1
- package/dist/hooks/shims/cline.d.ts +5 -12
- package/dist/hooks/shims/cline.d.ts.map +1 -1
- package/dist/hooks/shims/cline.js +9 -34
- package/dist/hooks/shims/cline.js.map +1 -1
- package/dist/hooks/shims/codex.d.ts +5 -18
- package/dist/hooks/shims/codex.d.ts.map +1 -1
- package/dist/hooks/shims/codex.js +9 -51
- package/dist/hooks/shims/codex.js.map +1 -1
- package/dist/hooks/shims/cursor.d.ts +5 -14
- package/dist/hooks/shims/cursor.d.ts.map +1 -1
- package/dist/hooks/shims/cursor.js +9 -42
- package/dist/hooks/shims/cursor.js.map +1 -1
- package/dist/hooks/shims/gemini.d.ts +5 -18
- package/dist/hooks/shims/gemini.d.ts.map +1 -1
- package/dist/hooks/shims/gemini.js +9 -48
- package/dist/hooks/shims/gemini.js.map +1 -1
- package/dist/hooks/shims/grok.d.ts +5 -13
- package/dist/hooks/shims/grok.d.ts.map +1 -1
- package/dist/hooks/shims/grok.js +9 -32
- package/dist/hooks/shims/grok.js.map +1 -1
- package/dist/hooks/shims/hermes.d.ts +5 -17
- package/dist/hooks/shims/hermes.d.ts.map +1 -1
- package/dist/hooks/shims/hermes.js +9 -45
- package/dist/hooks/shims/hermes.js.map +1 -1
- package/dist/index.d.ts +33 -76
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -67
- package/dist/index.js.map +1 -1
- package/dist/lang/bash.d.ts.map +1 -1
- package/dist/lang/bash.js.map +1 -1
- package/dist/lang/c.d.ts.map +1 -1
- package/dist/lang/c.js.map +1 -1
- package/dist/lang/c_sharp.d.ts.map +1 -1
- package/dist/lang/c_sharp.js.map +1 -1
- package/dist/lang/cpp.d.ts.map +1 -1
- package/dist/lang/cpp.js.map +1 -1
- package/dist/lang/go.d.ts.map +1 -1
- package/dist/lang/go.js.map +1 -1
- package/dist/lang/java.d.ts.map +1 -1
- package/dist/lang/java.js.map +1 -1
- package/dist/lang/javascript.d.ts.map +1 -1
- package/dist/lang/javascript.js.map +1 -1
- package/dist/lang/kotlin.d.ts.map +1 -1
- package/dist/lang/kotlin.js.map +1 -1
- package/dist/lang/php.d.ts.map +1 -1
- package/dist/lang/php.js.map +1 -1
- package/dist/lang/profile.d.ts.map +1 -1
- package/dist/lang/profile.js.map +1 -1
- package/dist/lang/python.d.ts.map +1 -1
- package/dist/lang/python.js.map +1 -1
- package/dist/lang/registry.d.ts.map +1 -1
- package/dist/lang/registry.js.map +1 -1
- package/dist/lang/ruby.d.ts.map +1 -1
- package/dist/lang/ruby.js.map +1 -1
- package/dist/lang/rust.d.ts.map +1 -1
- package/dist/lang/rust.js.map +1 -1
- package/dist/lang/swift.d.ts.map +1 -1
- package/dist/lang/swift.js.map +1 -1
- package/dist/lang/tsx.d.ts.map +1 -1
- package/dist/lang/tsx.js.map +1 -1
- package/dist/lang/typescript.d.ts.map +1 -1
- package/dist/lang/typescript.js.map +1 -1
- package/dist/net/policy.d.ts +18 -1
- package/dist/net/policy.d.ts.map +1 -1
- package/dist/net/policy.js +6 -4
- package/dist/net/policy.js.map +1 -1
- package/dist/ops/index.d.ts +39 -0
- package/dist/ops/index.d.ts.map +1 -0
- package/dist/ops/index.js +37 -0
- package/dist/ops/index.js.map +1 -0
- package/dist/ops/inputs.d.ts +170 -0
- package/dist/ops/inputs.d.ts.map +1 -0
- package/dist/ops/inputs.js +142 -0
- package/dist/ops/inputs.js.map +1 -0
- package/dist/ops/verbs.d.ts +136 -0
- package/dist/ops/verbs.d.ts.map +1 -0
- package/dist/ops/verbs.js +76 -0
- package/dist/ops/verbs.js.map +1 -0
- package/dist/plan/auto.d.ts +69 -0
- package/dist/plan/auto.d.ts.map +1 -0
- package/dist/plan/auto.js +70 -0
- package/dist/plan/auto.js.map +1 -0
- package/dist/plan/budget.d.ts +21 -0
- package/dist/plan/budget.d.ts.map +1 -0
- package/dist/plan/budget.js +26 -0
- package/dist/plan/budget.js.map +1 -0
- package/dist/plan/grammar.d.ts +14 -0
- package/dist/plan/grammar.d.ts.map +1 -1
- package/dist/plan/grammar.js +47 -6
- package/dist/plan/grammar.js.map +1 -1
- package/dist/plan/lexical.d.ts.map +1 -1
- package/dist/plan/lexical.js +1 -7
- package/dist/plan/lexical.js.map +1 -1
- package/dist/plan/planners.d.ts +35 -2
- package/dist/plan/planners.d.ts.map +1 -1
- package/dist/plan/planners.js +35 -2
- package/dist/plan/planners.js.map +1 -1
- package/dist/plan/structural.d.ts +19 -5
- package/dist/plan/structural.d.ts.map +1 -1
- package/dist/plan/structural.js +180 -33
- package/dist/plan/structural.js.map +1 -1
- package/dist/repomap/cache.d.ts +48 -0
- package/dist/repomap/cache.d.ts.map +1 -1
- package/dist/repomap/cache.js +112 -13
- package/dist/repomap/cache.js.map +1 -1
- package/dist/repomap/io.d.ts +24 -0
- package/dist/repomap/io.d.ts.map +1 -0
- package/dist/repomap/io.js +34 -0
- package/dist/repomap/io.js.map +1 -0
- package/dist/repomap/map.d.ts +69 -6
- package/dist/repomap/map.d.ts.map +1 -1
- package/dist/repomap/map.js +104 -22
- package/dist/repomap/map.js.map +1 -1
- package/dist/repomap/rank.d.ts +26 -2
- package/dist/repomap/rank.d.ts.map +1 -1
- package/dist/repomap/rank.js +20 -0
- package/dist/repomap/rank.js.map +1 -1
- package/dist/repomap/reader.d.ts +73 -0
- package/dist/repomap/reader.d.ts.map +1 -0
- package/dist/repomap/reader.js +30 -0
- package/dist/repomap/reader.js.map +1 -0
- package/dist/repomap/tags.d.ts.map +1 -1
- package/dist/repomap/tags.js.map +1 -1
- package/dist/retrieve.d.ts +8 -0
- package/dist/retrieve.d.ts.map +1 -1
- package/dist/retrieve.js +12 -1
- package/dist/retrieve.js.map +1 -1
- package/dist/smelter.d.ts +79 -0
- package/dist/smelter.d.ts.map +1 -0
- package/dist/smelter.js +65 -0
- package/dist/smelter.js.map +1 -0
- package/dist/stages.d.ts.map +1 -1
- package/dist/stages.js +2 -2
- package/dist/stages.js.map +1 -1
- package/dist/stats.d.ts +2 -1
- package/dist/stats.d.ts.map +1 -1
- package/dist/stats.js +2 -1
- package/dist/stats.js.map +1 -1
- package/dist/store-dir.d.ts +43 -4
- package/dist/store-dir.d.ts.map +1 -1
- package/dist/store-dir.js +48 -5
- package/dist/store-dir.js.map +1 -1
- package/dist/store.d.ts +6 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +6 -0
- package/dist/store.js.map +1 -1
- package/dist/types.d.ts +35 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/grammars/tree-sitter-bash.wasm +0 -0
- package/grammars/tree-sitter-c.wasm +0 -0
- package/grammars/tree-sitter-c_sharp.wasm +0 -0
- package/grammars/tree-sitter-cpp.wasm +0 -0
- package/grammars/tree-sitter-go.wasm +0 -0
- package/grammars/tree-sitter-java.wasm +0 -0
- package/grammars/tree-sitter-javascript.wasm +0 -0
- package/grammars/tree-sitter-kotlin.wasm +0 -0
- package/grammars/tree-sitter-php.wasm +0 -0
- package/grammars/tree-sitter-python.wasm +0 -0
- package/grammars/tree-sitter-ruby.wasm +0 -0
- package/grammars/tree-sitter-rust.wasm +0 -0
- package/grammars/tree-sitter-swift.wasm +0 -0
- package/grammars/tree-sitter-tsx.wasm +0 -0
- package/grammars/tree-sitter-typescript.wasm +0 -0
- package/package.json +11 -11
- package/dist/cli/resolve.d.ts +0 -103
- package/dist/cli/resolve.d.ts.map +0 -1
- package/dist/cli/resolve.js +0 -106
- package/dist/cli/resolve.js.map +0 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Structure-aware, reversible context optimization for coding agents. Zero network calls.
|
|
4
4
|
|
|
5
5
|
This is the library package. The project README, the four laws and their reasoning, the
|
|
6
|
-
|
|
6
|
+
architecture and the consumer contract all live in the repository root:
|
|
7
7
|
**https://github.com/smeltjs/smelt**
|
|
8
8
|
|
|
9
9
|
```ts
|
|
@@ -45,11 +45,11 @@ The parsers ship inside this tarball — no native build step, no post-install d
|
|
|
45
45
|
That makes smelt a redistributor, so [`THIRD-PARTY.md`](./THIRD-PARTY.md) carries the
|
|
46
46
|
licences, generated from package metadata rather than written by hand.
|
|
47
47
|
|
|
48
|
-
**0.x.**
|
|
48
|
+
**0.x.** In the box: structural planning (tree-sitter, with
|
|
49
49
|
signatures and doc comments always kept), the lexical planner, a persistent
|
|
50
50
|
content-addressed store, cache-prefix hygiene (detect, never rewrite), a repo-map
|
|
51
51
|
planner modelled on Aider's, the `smelt init` wizard, and a committed measurement
|
|
52
52
|
harness. An unsupported language under `strategy: 'structural'` is refused, never
|
|
53
|
-
approximated. See [`docs/
|
|
53
|
+
approximated. See [`docs/ARCHITECTURE.md`](https://github.com/smeltjs/smelt/blob/main/docs/ARCHITECTURE.md).
|
|
54
54
|
|
|
55
55
|
Apache-2.0.
|
package/dist/apply.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../src/apply.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,aAAa,EACb,OAAO,EACP,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB,0DAA0D;AAC1D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,UAAU,KAAK,MAAM,CAAC;AAEzD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,EAAE,aACoE,CAAC;AAEjG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,2BAA2B,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAKzF,CAAC;AAEJ;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,gBAAgB,EAC1B,IAAI,GAAE,aAA6B,GAClC,aAAa,CAIf;AAQD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,GAAE,gBAA4B,EACtC,aAAa,CAAC,EAAE,aAAa,GAC5B,aAAa,CAef;AAED,MAAM,WAAW,YAAY;IAC3B;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;IAChC,mFAAmF;IACnF,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,YAAY,EACnB,OAAO,GAAE,YAAiB,GACzB,WAAW,CA6Eb;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,GAAG,MAAM,CAiB5E"}
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../src/apply.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,aAAa,EACb,OAAO,EACP,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB,0DAA0D;AAC1D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,UAAU,KAAK,MAAM,CAAC;AAEzD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,EAAE,aACoE,CAAC;AAEjG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,2BAA2B,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAKzF,CAAC;AAEJ;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,gBAAgB,EAC1B,IAAI,GAAE,aAA6B,GAClC,aAAa,CAIf;AAQD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,GAAE,gBAA4B,EACtC,aAAa,CAAC,EAAE,aAAa,GAC5B,aAAa,CAef;AAED,MAAM,WAAW,YAAY;IAC3B;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;IAChC,mFAAmF;IACnF,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,YAAY,EACnB,OAAO,GAAE,YAAiB,GACzB,WAAW,CA6Eb;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,GAAG,MAAM,CAiB5E","sourcesContent":["import { OverlappingElisionError, RangeOutOfBoundsError, UnknownHashError } from './errors.ts';\nimport { HASH_LENGTH } from './hash.ts';\nimport { LANGUAGE_PROFILES } from './lang/registry.ts';\nimport type {\n AppliedElision,\n ByteRange,\n DetectedLanguage,\n ElisionPlan,\n ElisionStore,\n MarkerPricing,\n Measure,\n SmeltResult,\n} from './types.ts';\n\n/** Everything the marker text is allowed to depend on. */\nexport interface MarkerInfo {\n readonly hash: string;\n readonly bytes: number;\n readonly rule: string;\n readonly explanation: string;\n}\n\nexport type MarkerBuilder = (info: MarkerInfo) => string;\n\n/**\n * The version of the marker format itself, carried **in band** in every marker.\n *\n * The marker is the one part of smelt a *model* sees, and it goes into prompts.\n * Changing its shape changes model behaviour downstream and shows up as worse output\n * with no error anywhere — this project's signature failure mode, shipped as a patch\n * release. So the wire surface is frozen from 0.1 and treated as 1.0\n * (`CONTRIBUTING.md` § \"Two promises, not one\"), and a future format is *additive and\n * identifiable*: `smelt/v2` markers can coexist with `smelt/v1` ones, and a consumer\n * parsing markers can tell which it is holding. A format that changed silently would\n * be a substitution; this makes it a declaration.\n *\n * `test/guards/marker-format.test.ts` pins the rendered marker per version and fails if\n * the format moves without the version moving.\n */\nexport const MARKER_FORMAT_VERSION = 'v1';\n\n/**\n * The default marker.\n *\n * Its shape is the user-facing form of Laws 2 and 3, in one line the model reads:\n * *which format this is* (the version), *what was removed* (the explanation), *how\n * much* (the byte count), and *how to get it back* (the hash). Anything that cannot\n * fill in all of those is not allowed to be an elision.\n *\n * `<<…>>` rather than a Unicode bracket because it survives every tokenizer, terminal,\n * and diff tool without becoming three tokens of nothing.\n */\nexport const defaultMarker: MarkerBuilder = ({ explanation, bytes, hash }) =>\n `<<smelt/${MARKER_FORMAT_VERSION}: ${explanation} (${String(bytes)}B) — retrieve(\"${hash}\")>>`;\n\n/**\n * Line-comment leaders per language: the marker always lands as a comment in the\n * survivor's own syntax, because a bare marker line breaks the syntax of what remains\n * around it — in **every** grammar this was tested against, not just the indented ones.\n *\n * The failure classes, each verified by reparsing a bare-marker survivor:\n *\n * - **python** — significant indentation means a parse error does not stay local.\n * The ERROR node swallows the *neighbouring definitions too* — the survivor stops\n * being Python at all, not just at the marker line.\n * - **ruby** and **bash** — the marker *begins with* `<<`, which both languages read\n * as a heredoc operator. A bare marker line opens a heredoc whose terminator never\n * arrives, and everything after it — every kept declaration — is swallowed into a\n * string literal.\n * - **php** — `<<` is an operator here too: the kept function after a bare marker is\n * re-typed into an anonymous-function operand inside the marker's binary\n * expression. The kept declaration is no longer a declaration in the survivor.\n * - **kotlin**, **swift**, and the rest of the brace-delimited set (typescript, tsx,\n * javascript, rust, go, java, c, cpp, c_sharp) — the folk claim that braces keep a\n * parse error local is **empirically false**: reparsing each language's fixture\n * survivor with its own bundled grammar shows ERROR nodes spanning the kept\n * declarations (a C function's signature absorbed into an ERROR, fifteen cascading\n * ERRORs in swift, and so on). The survivor-reparse guard in\n * `test/guards/structural.test.ts` now asserts the opposite property for every\n * structural language: the survivor reparses with no new issues.\n *\n * Only `'unknown'` keeps the bare marker — lexical text has no syntax to break.\n *\n * This does **not** move the frozen wire surface. The `<<smelt/v1: … >>` core is\n * rendered by {@link defaultMarker}, byte-identical and still versioned in band; the\n * leader is part of the substituted marker text, so `outputRange` covers it and\n * reconstruction stays byte-exact. A comment leader in the survivor's own syntax is\n * the one wrapping that cannot change what a model reads out of the marker.\n *\n * A derived view: each leader is the `markerLeader` fact on the language's\n * {@link LanguageProfile} (`src/lang/`), collected here so marker construction keeps\n * one lookup table.\n */\nexport const MARKER_LINE_COMMENT_LEADERS: Readonly<Partial<Record<DetectedLanguage, string>>> =\n Object.fromEntries(\n Object.values(LANGUAGE_PROFILES)\n .filter((profile) => profile.markerLeader !== undefined)\n .map((profile) => [profile.id, profile.markerLeader]),\n );\n\n/**\n * The marker builder for a language: {@link defaultMarker}, wrapped in the language's\n * line-comment leader when {@link MARKER_LINE_COMMENT_LEADERS} names one — so a Python\n * survivor still parses as Python. Everything else gets `base` unchanged.\n */\nexport function markerForLanguage(\n language: DetectedLanguage,\n base: MarkerBuilder = defaultMarker,\n): MarkerBuilder {\n const leader = MARKER_LINE_COMMENT_LEADERS[language];\n if (leader === undefined) return base;\n return (info) => `${leader}${base(info)}`;\n}\n\n/**\n * A stand-in hash of the real length, so a marker can be priced before the cut that\n * would earn it exists. Marker cost depends on the hash's *length*, never its value.\n */\nconst PLACEHOLDER_HASH = '0'.repeat(HASH_LENGTH);\n\n/**\n * The one adapter behind the {@link MarkerPricing} seam.\n *\n * Marker cost is this module's fact: `applyPlan` renders the marker, so only this\n * module can price it without guessing. The pricing is built from the **exact builder\n * `applyPlan` will use** — the same resolution, in the same order: a caller-supplied\n * builder (`SmelterConfig.marker` / `ApplyOptions.marker`) wins wholesale, otherwise\n * the language's leader-wrapped default via {@link markerForLanguage}.\n *\n * The custom-builder leg is load-bearing, not a convenience: a caller who installs a\n * longer `MarkerBuilder` changes what every elision costs, and a planner still pricing\n * the *default* marker would keep planning elisions the real marker makes\n * unprofitable — cuts that grow the output, silently. Pricing with the builder's own\n * rendering closes that hole: `costBytes` measures the marker *that builder* would\n * emit, byte for byte.\n *\n * `createSmelter` (and through it, the CLI) calls this centrally, once per smelt call;\n * a caller driving `planLexical`/`planStructural` directly builds its own and puts it\n * on the {@link PlanInput}.\n */\nexport function markerPricing(\n language: DetectedLanguage = 'unknown',\n markerBuilder?: MarkerBuilder,\n): MarkerPricing {\n // The same resolution applyPlan performs: a supplied builder wins wholesale.\n const build = markerBuilder ?? markerForLanguage(language);\n return {\n costBytes: (reason, elidedBytes) =>\n Buffer.byteLength(\n build({\n hash: PLACEHOLDER_HASH,\n bytes: elidedBytes,\n rule: reason.rule,\n explanation: reason.explanation,\n }),\n 'utf8',\n ),\n };\n}\n\nexport interface ApplyOptions {\n /**\n * Overrides the marker builder. The default follows the *plan's* language —\n * {@link markerForLanguage} — so the documented composition\n * `planStructural → applyPlan` lands a `# `-led marker in python without the caller\n * wiring it, the same as `createSmelter` does. A bare {@link defaultMarker} in a\n * python survivor is exactly the parse-breaking failure the leader exists to prevent.\n */\n readonly marker?: MarkerBuilder;\n /** A consumer-supplied counter. See {@link Measure}; the budget stays in bytes. */\n readonly measure?: Measure;\n}\n\n/**\n * Turn a plan into text.\n *\n * This is the only function in smelt that removes anything, and it contains no\n * judgement at all: it validates the plan, stores every removed run, substitutes\n * markers, and records where each marker landed. All the deciding happens in a\n * {@link Planner}, which is why a plan can be reviewed before a byte moves.\n *\n * @throws {RangeOutOfBoundsError} if a range falls outside the input's UTF-8 bytes.\n * @throws {OverlappingElisionError} if two ranges overlap — applying both would\n * corrupt the output, and picking a winner would be a silent guess.\n */\nexport function applyPlan(\n text: string,\n plan: ElisionPlan,\n store: ElisionStore,\n options: ApplyOptions = {},\n): SmeltResult {\n const buildMarker = options.marker ?? markerForLanguage(plan.language);\n const input = Buffer.from(text, 'utf8');\n\n const ordered = plan.elisions.toSorted((a, b) => a.range.start - b.range.start);\n for (const { range } of ordered) assertInBounds(range, input.length);\n for (let i = 1; i < ordered.length; i += 1) {\n const previous = ordered[i - 1]!;\n const current = ordered[i]!;\n if (current.range.start < previous.range.end) {\n throw new OverlappingElisionError(\n `smelt: plan from \"${plan.planner}\" elides overlapping ranges ` +\n `[${previous.range.start},${previous.range.end}) and ` +\n `[${current.range.start},${current.range.end}). A plan must be a partition.`,\n );\n }\n }\n\n const pieces: Buffer[] = [];\n const applied: AppliedElision[] = [];\n let cursor = 0;\n let outputBytes = 0;\n\n for (const { range, reason } of ordered) {\n const kept = input.subarray(cursor, range.start);\n pieces.push(kept);\n outputBytes += kept.length;\n\n const removed = input.subarray(range.start, range.end);\n const removedText = removed.toString('utf8');\n const hash = store.put(removedText);\n const marker = buildMarker({\n hash,\n bytes: removed.length,\n rule: reason.rule,\n explanation: reason.explanation,\n });\n const markerBuffer = Buffer.from(marker, 'utf8');\n pieces.push(markerBuffer);\n\n applied.push({\n hash,\n range,\n outputRange: { start: outputBytes, end: outputBytes + markerBuffer.length },\n bytes: removed.length,\n reason,\n marker,\n });\n outputBytes += markerBuffer.length;\n cursor = range.end;\n }\n\n const tail = input.subarray(cursor);\n pieces.push(tail);\n outputBytes += tail.length;\n\n const output = Buffer.concat(pieces).toString('utf8');\n const measure = options.measure;\n\n return {\n text: output,\n inputBytes: input.length,\n outputBytes,\n planner: plan.planner,\n language: plan.language,\n elisions: applied,\n ...(measure === undefined\n ? {}\n : {\n measured: {\n measure: measure.id,\n unit: measure.unit,\n input: measure.count(text),\n output: measure.count(output),\n },\n }),\n };\n}\n\n/**\n * Put it all back. `reconstruct(smelt(x), store) === x`, byte for byte — this is Law 3\n * expressed as an executable equation, and `test/guards/reversibility.test.ts` asserts\n * it on every input the suite knows about.\n *\n * Reads through `peek`, **not** `retrieve`: `retrieveCalls` and the expansion rate\n * exist to count *the model asking for hidden material back* — the honest signal this\n * whole project sells. A caller reassembling the original (to diff it, to verify a\n * round trip, to write it to disk) is not that, and counting it would inflate the one\n * number that must never flatter. The guard in\n * `test/guards/expansion-counter.test.ts` pins this: reconstruction leaves every\n * counter exactly where it was.\n *\n * @throws {UnknownHashError} if the store no longer holds an elision's bytes.\n */\nexport function reconstruct(result: SmeltResult, store: ElisionStore): string {\n const output = Buffer.from(result.text, 'utf8');\n const ordered = result.elisions.toSorted((a, b) => a.outputRange.start - b.outputRange.start);\n const pieces: Buffer[] = [];\n let cursor = 0;\n\n for (const elision of ordered) {\n assertInBounds(elision.outputRange, output.length);\n const content = store.peek(elision.hash);\n if (content === undefined) throw new UnknownHashError(elision.hash);\n pieces.push(output.subarray(cursor, elision.outputRange.start));\n pieces.push(Buffer.from(content, 'utf8'));\n cursor = elision.outputRange.end;\n }\n pieces.push(output.subarray(cursor));\n\n return Buffer.concat(pieces).toString('utf8');\n}\n\nfunction assertInBounds(range: ByteRange, length: number): void {\n if (\n !Number.isInteger(range.start) ||\n !Number.isInteger(range.end) ||\n range.start < 0 ||\n range.end > length ||\n range.start >= range.end\n ) {\n throw new RangeOutOfBoundsError(\n `smelt: range [${range.start},${range.end}) is not a non-empty range inside ` +\n `${String(length)} bytes.`,\n );\n }\n}\n"]}
|
package/dist/apply.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../src/apply.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../src/apply.ts"],"names":[],"mappings":"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","sourcesContent":["import { OverlappingElisionError, RangeOutOfBoundsError, UnknownHashError } from './errors.ts';\nimport { HASH_LENGTH } from './hash.ts';\nimport { LANGUAGE_PROFILES } from './lang/registry.ts';\nimport type {\n AppliedElision,\n ByteRange,\n DetectedLanguage,\n ElisionPlan,\n ElisionStore,\n MarkerPricing,\n Measure,\n SmeltResult,\n} from './types.ts';\n\n/** Everything the marker text is allowed to depend on. */\nexport interface MarkerInfo {\n readonly hash: string;\n readonly bytes: number;\n readonly rule: string;\n readonly explanation: string;\n}\n\nexport type MarkerBuilder = (info: MarkerInfo) => string;\n\n/**\n * The version of the marker format itself, carried **in band** in every marker.\n *\n * The marker is the one part of smelt a *model* sees, and it goes into prompts.\n * Changing its shape changes model behaviour downstream and shows up as worse output\n * with no error anywhere — this project's signature failure mode, shipped as a patch\n * release. So the wire surface is frozen from 0.1 and treated as 1.0\n * (`CONTRIBUTING.md` § \"Two promises, not one\"), and a future format is *additive and\n * identifiable*: `smelt/v2` markers can coexist with `smelt/v1` ones, and a consumer\n * parsing markers can tell which it is holding. A format that changed silently would\n * be a substitution; this makes it a declaration.\n *\n * `test/guards/marker-format.test.ts` pins the rendered marker per version and fails if\n * the format moves without the version moving.\n */\nexport const MARKER_FORMAT_VERSION = 'v1';\n\n/**\n * The default marker.\n *\n * Its shape is the user-facing form of Laws 2 and 3, in one line the model reads:\n * *which format this is* (the version), *what was removed* (the explanation), *how\n * much* (the byte count), and *how to get it back* (the hash). Anything that cannot\n * fill in all of those is not allowed to be an elision.\n *\n * `<<…>>` rather than a Unicode bracket because it survives every tokenizer, terminal,\n * and diff tool without becoming three tokens of nothing.\n */\nexport const defaultMarker: MarkerBuilder = ({ explanation, bytes, hash }) =>\n `<<smelt/${MARKER_FORMAT_VERSION}: ${explanation} (${String(bytes)}B) — retrieve(\"${hash}\")>>`;\n\n/**\n * Line-comment leaders per language: the marker always lands as a comment in the\n * survivor's own syntax, because a bare marker line breaks the syntax of what remains\n * around it — in **every** grammar this was tested against, not just the indented ones.\n *\n * The failure classes, each verified by reparsing a bare-marker survivor:\n *\n * - **python** — significant indentation means a parse error does not stay local.\n * The ERROR node swallows the *neighbouring definitions too* — the survivor stops\n * being Python at all, not just at the marker line.\n * - **ruby** and **bash** — the marker *begins with* `<<`, which both languages read\n * as a heredoc operator. A bare marker line opens a heredoc whose terminator never\n * arrives, and everything after it — every kept declaration — is swallowed into a\n * string literal.\n * - **php** — `<<` is an operator here too: the kept function after a bare marker is\n * re-typed into an anonymous-function operand inside the marker's binary\n * expression. The kept declaration is no longer a declaration in the survivor.\n * - **kotlin**, **swift**, and the rest of the brace-delimited set (typescript, tsx,\n * javascript, rust, go, java, c, cpp, c_sharp) — the folk claim that braces keep a\n * parse error local is **empirically false**: reparsing each language's fixture\n * survivor with its own bundled grammar shows ERROR nodes spanning the kept\n * declarations (a C function's signature absorbed into an ERROR, fifteen cascading\n * ERRORs in swift, and so on). The survivor-reparse guard in\n * `test/guards/structural.test.ts` now asserts the opposite property for every\n * structural language: the survivor reparses with no new issues.\n *\n * Only `'unknown'` keeps the bare marker — lexical text has no syntax to break.\n *\n * This does **not** move the frozen wire surface. The `<<smelt/v1: … >>` core is\n * rendered by {@link defaultMarker}, byte-identical and still versioned in band; the\n * leader is part of the substituted marker text, so `outputRange` covers it and\n * reconstruction stays byte-exact. A comment leader in the survivor's own syntax is\n * the one wrapping that cannot change what a model reads out of the marker.\n *\n * A derived view: each leader is the `markerLeader` fact on the language's\n * {@link LanguageProfile} (`src/lang/`), collected here so marker construction keeps\n * one lookup table.\n */\nexport const MARKER_LINE_COMMENT_LEADERS: Readonly<Partial<Record<DetectedLanguage, string>>> =\n Object.fromEntries(\n Object.values(LANGUAGE_PROFILES)\n .filter((profile) => profile.markerLeader !== undefined)\n .map((profile) => [profile.id, profile.markerLeader]),\n );\n\n/**\n * The marker builder for a language: {@link defaultMarker}, wrapped in the language's\n * line-comment leader when {@link MARKER_LINE_COMMENT_LEADERS} names one — so a Python\n * survivor still parses as Python. Everything else gets `base` unchanged.\n */\nexport function markerForLanguage(\n language: DetectedLanguage,\n base: MarkerBuilder = defaultMarker,\n): MarkerBuilder {\n const leader = MARKER_LINE_COMMENT_LEADERS[language];\n if (leader === undefined) return base;\n return (info) => `${leader}${base(info)}`;\n}\n\n/**\n * A stand-in hash of the real length, so a marker can be priced before the cut that\n * would earn it exists. Marker cost depends on the hash's *length*, never its value.\n */\nconst PLACEHOLDER_HASH = '0'.repeat(HASH_LENGTH);\n\n/**\n * The one adapter behind the {@link MarkerPricing} seam.\n *\n * Marker cost is this module's fact: `applyPlan` renders the marker, so only this\n * module can price it without guessing. The pricing is built from the **exact builder\n * `applyPlan` will use** — the same resolution, in the same order: a caller-supplied\n * builder (`SmelterConfig.marker` / `ApplyOptions.marker`) wins wholesale, otherwise\n * the language's leader-wrapped default via {@link markerForLanguage}.\n *\n * The custom-builder leg is load-bearing, not a convenience: a caller who installs a\n * longer `MarkerBuilder` changes what every elision costs, and a planner still pricing\n * the *default* marker would keep planning elisions the real marker makes\n * unprofitable — cuts that grow the output, silently. Pricing with the builder's own\n * rendering closes that hole: `costBytes` measures the marker *that builder* would\n * emit, byte for byte.\n *\n * `createSmelter` (and through it, the CLI) calls this centrally, once per smelt call;\n * a caller driving `planLexical`/`planStructural` directly builds its own and puts it\n * on the {@link PlanInput}.\n */\nexport function markerPricing(\n language: DetectedLanguage = 'unknown',\n markerBuilder?: MarkerBuilder,\n): MarkerPricing {\n // The same resolution applyPlan performs: a supplied builder wins wholesale.\n const build = markerBuilder ?? markerForLanguage(language);\n return {\n costBytes: (reason, elidedBytes) =>\n Buffer.byteLength(\n build({\n hash: PLACEHOLDER_HASH,\n bytes: elidedBytes,\n rule: reason.rule,\n explanation: reason.explanation,\n }),\n 'utf8',\n ),\n };\n}\n\nexport interface ApplyOptions {\n /**\n * Overrides the marker builder. The default follows the *plan's* language —\n * {@link markerForLanguage} — so the documented composition\n * `planStructural → applyPlan` lands a `# `-led marker in python without the caller\n * wiring it, the same as `createSmelter` does. A bare {@link defaultMarker} in a\n * python survivor is exactly the parse-breaking failure the leader exists to prevent.\n */\n readonly marker?: MarkerBuilder;\n /** A consumer-supplied counter. See {@link Measure}; the budget stays in bytes. */\n readonly measure?: Measure;\n}\n\n/**\n * Turn a plan into text.\n *\n * This is the only function in smelt that removes anything, and it contains no\n * judgement at all: it validates the plan, stores every removed run, substitutes\n * markers, and records where each marker landed. All the deciding happens in a\n * {@link Planner}, which is why a plan can be reviewed before a byte moves.\n *\n * @throws {RangeOutOfBoundsError} if a range falls outside the input's UTF-8 bytes.\n * @throws {OverlappingElisionError} if two ranges overlap — applying both would\n * corrupt the output, and picking a winner would be a silent guess.\n */\nexport function applyPlan(\n text: string,\n plan: ElisionPlan,\n store: ElisionStore,\n options: ApplyOptions = {},\n): SmeltResult {\n const buildMarker = options.marker ?? markerForLanguage(plan.language);\n const input = Buffer.from(text, 'utf8');\n\n const ordered = plan.elisions.toSorted((a, b) => a.range.start - b.range.start);\n for (const { range } of ordered) assertInBounds(range, input.length);\n for (let i = 1; i < ordered.length; i += 1) {\n const previous = ordered[i - 1]!;\n const current = ordered[i]!;\n if (current.range.start < previous.range.end) {\n throw new OverlappingElisionError(\n `smelt: plan from \"${plan.planner}\" elides overlapping ranges ` +\n `[${previous.range.start},${previous.range.end}) and ` +\n `[${current.range.start},${current.range.end}). A plan must be a partition.`,\n );\n }\n }\n\n const pieces: Buffer[] = [];\n const applied: AppliedElision[] = [];\n let cursor = 0;\n let outputBytes = 0;\n\n for (const { range, reason } of ordered) {\n const kept = input.subarray(cursor, range.start);\n pieces.push(kept);\n outputBytes += kept.length;\n\n const removed = input.subarray(range.start, range.end);\n const removedText = removed.toString('utf8');\n const hash = store.put(removedText);\n const marker = buildMarker({\n hash,\n bytes: removed.length,\n rule: reason.rule,\n explanation: reason.explanation,\n });\n const markerBuffer = Buffer.from(marker, 'utf8');\n pieces.push(markerBuffer);\n\n applied.push({\n hash,\n range,\n outputRange: { start: outputBytes, end: outputBytes + markerBuffer.length },\n bytes: removed.length,\n reason,\n marker,\n });\n outputBytes += markerBuffer.length;\n cursor = range.end;\n }\n\n const tail = input.subarray(cursor);\n pieces.push(tail);\n outputBytes += tail.length;\n\n const output = Buffer.concat(pieces).toString('utf8');\n const measure = options.measure;\n\n return {\n text: output,\n inputBytes: input.length,\n outputBytes,\n planner: plan.planner,\n language: plan.language,\n elisions: applied,\n ...(measure === undefined\n ? {}\n : {\n measured: {\n measure: measure.id,\n unit: measure.unit,\n input: measure.count(text),\n output: measure.count(output),\n },\n }),\n };\n}\n\n/**\n * Put it all back. `reconstruct(smelt(x), store) === x`, byte for byte — this is Law 3\n * expressed as an executable equation, and `test/guards/reversibility.test.ts` asserts\n * it on every input the suite knows about.\n *\n * Reads through `peek`, **not** `retrieve`: `retrieveCalls` and the expansion rate\n * exist to count *the model asking for hidden material back* — the honest signal this\n * whole project sells. A caller reassembling the original (to diff it, to verify a\n * round trip, to write it to disk) is not that, and counting it would inflate the one\n * number that must never flatter. The guard in\n * `test/guards/expansion-counter.test.ts` pins this: reconstruction leaves every\n * counter exactly where it was.\n *\n * @throws {UnknownHashError} if the store no longer holds an elision's bytes.\n */\nexport function reconstruct(result: SmeltResult, store: ElisionStore): string {\n const output = Buffer.from(result.text, 'utf8');\n const ordered = result.elisions.toSorted((a, b) => a.outputRange.start - b.outputRange.start);\n const pieces: Buffer[] = [];\n let cursor = 0;\n\n for (const elision of ordered) {\n assertInBounds(elision.outputRange, output.length);\n const content = store.peek(elision.hash);\n if (content === undefined) throw new UnknownHashError(elision.hash);\n pieces.push(output.subarray(cursor, elision.outputRange.start));\n pieces.push(Buffer.from(content, 'utf8'));\n cursor = elision.outputRange.end;\n }\n pieces.push(output.subarray(cursor));\n\n return Buffer.concat(pieces).toString('utf8');\n}\n\nfunction assertInBounds(range: ByteRange, length: number): void {\n if (\n !Number.isInteger(range.start) ||\n !Number.isInteger(range.end) ||\n range.start < 0 ||\n range.end > length ||\n range.start >= range.end\n ) {\n throw new RangeOutOfBoundsError(\n `smelt: range [${range.start},${range.end}) is not a non-empty range inside ` +\n `${String(length)} bytes.`,\n );\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefix.d.ts","sourceRoot":"","sources":["../../src/cache/prefix.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B;IACvC,6EAA6E;aAC7E,MAAM,EAAE,kFAAkF;IAC1F;;;OAGG;aACH,WAAW,YAAG,OAAO,EAAE,QAAQ,EAAE,UAAU;IAC3C,qFAAqF;aACrF,YAAY,EAAE,wDAAwD;IACtE,gFAAgF;aAChF,8BAA8B,EAAE,IAAI;IACpC,uDAAuD;aACvD,mBAAmB,EAAE,CAAC;IACtB,yDAAyD;aACzD,iBAAiB,EAAE,CAAC;IACpB,6DAA6D;aAC7D,kBAAkB,EAAE,EAAE;IACtB,6EAA6E;aAC7E,qBAAqB,EAAE,IAAI;IAC3B,wEAAwE;aACxE,qBAAqB,EAAE,CAAC;IACxB,mFAAmF;aACnF,wBAAwB,EAAE,GAAG;CACrB,CAAC;AAEX;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAQD,mFAAmF;AACnF,eAAO,MAAM,mBAAmB;IAC9B,qDAAqD;aACrD,eAAe,EAAE,kBAAkB;IACnC,mCAAmC;aACnC,UAAU,EAAE,aAAa;IACzB,yEAAyE;aACzE,gBAAgB,EAAE,oBAAoB;IACtC,yDAAyD;aACzD,aAAa,EAAE,iBAAiB;CACxB,CAAC;AAEX;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,+FAA+F;IAC/F,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,uFAAuF;IACvF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAsBD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAuBjG;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,oDAAoD;IACpD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IACvC,8BAA8B;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AA4ND;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,eAAe,EACxB,QAAQ,CAAC,EAAE,eAAe,GACzB,SAAS,YAAY,EAAE,CASzB"}
|
|
1
|
+
{"version":3,"file":"prefix.d.ts","sourceRoot":"","sources":["../../src/cache/prefix.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B;IACvC,6EAA6E;aAC7E,MAAM,EAAE,kFAAkF;IAC1F;;;OAGG;aACH,WAAW,YAAG,OAAO,EAAE,QAAQ,EAAE,UAAU;IAC3C,qFAAqF;aACrF,YAAY,EAAE,wDAAwD;IACtE,gFAAgF;aAChF,8BAA8B,EAAE,IAAI;IACpC,uDAAuD;aACvD,mBAAmB,EAAE,CAAC;IACtB,yDAAyD;aACzD,iBAAiB,EAAE,CAAC;IACpB,6DAA6D;aAC7D,kBAAkB,EAAE,EAAE;IACtB,6EAA6E;aAC7E,qBAAqB,EAAE,IAAI;IAC3B,wEAAwE;aACxE,qBAAqB,EAAE,CAAC;IACxB,mFAAmF;aACnF,wBAAwB,EAAE,GAAG;CACrB,CAAC;AAEX;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAQD,mFAAmF;AACnF,eAAO,MAAM,mBAAmB;IAC9B,qDAAqD;aACrD,eAAe,EAAE,kBAAkB;IACnC,mCAAmC;aACnC,UAAU,EAAE,aAAa;IACzB,yEAAyE;aACzE,gBAAgB,EAAE,oBAAoB;IACtC,yDAAyD;aACzD,aAAa,EAAE,iBAAiB;CACxB,CAAC;AAEX;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,+FAA+F;IAC/F,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,uFAAuF;IACvF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAsBD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAuBjG;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,oDAAoD;IACpD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IACvC,8BAA8B;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AA4ND;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,eAAe,EACxB,QAAQ,CAAC,EAAE,eAAe,GACzB,SAAS,YAAY,EAAE,CASzB","sourcesContent":["/**\n * Cache-prefix hygiene — Slice 6. **Detect and warn, never rewrite.**\n *\n * **This module is a consumer-facing surface**, exported from the package\n * entrypoint and stable in intent: hand it your prompt structure, get warnings\n * back, decide for yourself. It composes with nothing else in smelt — no planner\n * calls it, no store holds its output — because cache hygiene is a property of the\n * *request* a consumer assembles, which smelt never sees or intercepts. Use it\n * from your own send path:\n *\n * ```ts\n * // Both functions come from the package entrypoint:\n * // detectCacheBreakers, findPrefixDivergence\n *\n * // Before sending: name the silent cache-breakers in what you are about to send.\n * const warnings = detectCacheBreakers(\n * { tools, system }, // this call\n * { tools: previousTools }, // optional: the previous call, for tool-set drift\n * );\n * // → [{ rule: 'system-timestamp', explanation: 'system prompt contains …' }, …]\n *\n * // Between calls: where exactly did the cached prefix stop matching, and what\n * // did the change cost? `undefined` means intact (identical, or a pure append).\n * const divergence = findPrefixDivergence(previousPrefix, nextPrefix);\n * // → { byteOffset, invalidatedBytes, description } | undefined\n * ```\n *\n * Nothing is ever rewritten on your behalf, and no rate is ever claimed — the\n * paragraphs below say why both refusals are load-bearing.\n *\n * Provider prompt caches match the request prefix byte for byte, so a context\n * optimizer that reorders or rewrites a prompt prefix to \"help\" a cache can cost\n * more than it saves — and worse, a reordering that changes model behaviour is an\n * unexplainable elision wearing a different hat. Headroom's CacheAligner made the\n * same call: it detects prefix volatility and warns; it never edits the prompt.\n * Neither does this module. Every function here is a pure read over its inputs:\n * nothing is mutated, and nothing \"fixed\" is ever returned. That is not a comment,\n * it is a guarantee — `test/guards/cache-hygiene.test.ts` asserts it on frozen\n * inputs, and `pnpm mutate` proves the guard goes red when a helpful in-place fix\n * appears.\n *\n * Warnings follow the `ElisionReason` two-field discipline from `types.ts`:\n * a stable `rule` id for counters, and an `explanation` a human reads. A warning\n * that cannot be written as a sentence is a rule nobody understands.\n */\n\n/**\n * Facts about Anthropic's prompt cache, as published in Anthropic's prompt-caching\n * documentation (docs.anthropic.com), verified 2026-09-01.\n *\n * These are *cited provider facts*, not smelt measurements — the only numbers Law 4\n * permits are someone else's, with their source and date named. In particular this\n * module claims nothing about how often anyone's cache hits: the price multipliers\n * below are prices, and a price is not a frequency.\n */\nexport const ANTHROPIC_PROMPT_CACHE_FACTS = {\n /** Where every number in this object comes from, and when it was checked. */\n source: 'Anthropic prompt-caching documentation (docs.anthropic.com), verified 2026-09-01',\n /**\n * The cached prefix is matched byte for byte over the request in this order.\n * A byte change in `tools` therefore invalidates `system` and `messages` too.\n */\n prefixOrder: ['tools', 'system', 'messages'],\n /** Any byte change invalidates the cache from that byte to the end of the prefix. */\n invalidation: 'byte-exact; any change invalidates everything after it',\n /** Minimum cacheable prefix, in tokens — approximately 1024 for most models. */\n minCacheablePrefixTokensApprox: 1024,\n /** Maximum number of cache breakpoints per request. */\n maxCacheBreakpoints: 4,\n /** Default time-to-live of a cache entry, in minutes. */\n defaultTtlMinutes: 5,\n /** Optional extended time-to-live, in minutes (one hour). */\n extendedTtlMinutes: 60,\n /** Writing a 5-minute cache entry costs 1.25x the base input-token price. */\n writeCostMultiplier5m: 1.25,\n /** Writing a 1-hour cache entry costs 2x the base input-token price. */\n writeCostMultiplier1h: 2,\n /** Reading a cached prefix costs approximately 0.1x the base input-token price. */\n readCostMultiplierApprox: 0.1,\n} as const;\n\n/**\n * A cache-hygiene warning. Deliberately the same two-field shape as\n * `ElisionReason` in `types.ts`, and for the same reason: a stable id for counters, a\n * sentence for humans, and nothing else — no patch, no replacement text, no\n * \"fixed\" prompt. Warning is all this module does.\n */\nexport interface CacheWarning {\n /** Stable machine id, e.g. `'system-timestamp'`. */\n readonly rule: string;\n /** Present tense, no trailing period, names the evidence. */\n readonly explanation: string;\n}\n\n/**\n * The provider's serialization order, rendered as prose from the cited constant\n * above — so the sentence in a warning can never drift from the fact it cites.\n */\nconst PREFIX_ORDER_PROSE = ANTHROPIC_PROMPT_CACHE_FACTS.prefixOrder.join(' → ');\n\n/** The stable rule ids this module can emit. Additive over time, never renamed. */\nexport const CACHE_BREAKER_RULES = {\n /** A timestamp-shaped value in the system prompt. */\n systemTimestamp: 'system-timestamp',\n /** A UUID in the system prompt. */\n systemUuid: 'system-uuid',\n /** An object in a tool definition whose keys are not in sorted order. */\n unsortedJsonKeys: 'unsorted-json-keys',\n /** The tool set changed between two successive calls. */\n toolSetVaries: 'tool-set-varies',\n} as const;\n\n/**\n * The first byte where two successive prompt prefixes diverge, with a description\n * of what changed around it. Offsets are UTF-8 **byte** offsets, because that is\n * the unit the provider's cache matches in — a code-unit index would be a lie for\n * any prompt containing a multi-byte character.\n */\nexport interface PrefixDivergence {\n /** UTF-8 byte offset of the first byte at which the two prefixes differ. */\n readonly byteOffset: number;\n /** Bytes of `previous` from the divergence to its end — the span a cache no longer matches. */\n readonly invalidatedBytes: number;\n /** What changed around the divergence. Excerpts never split a multi-byte character. */\n readonly description: string;\n}\n\n/** Code points around the code point containing `byteOffset` — never a split character. */\nfunction excerptAroundByte(text: string, byteOffset: number, radius = 16): string {\n const points = [...text];\n let index = points.length;\n let bytes = 0;\n for (const [i, point] of points.entries()) {\n const width = Buffer.byteLength(point, 'utf8');\n if (byteOffset < bytes + width) {\n index = i;\n break;\n }\n bytes += width;\n }\n const start = Math.max(0, index - radius);\n const end = Math.min(points.length, index + radius);\n const head = start > 0 ? '…' : '';\n const tail = end < points.length ? '…' : '';\n return head + points.slice(start, end).join('') + tail;\n}\n\n/**\n * Compare two successive prompt prefixes the way the provider's cache does: byte\n * for byte, in UTF-8.\n *\n * Returns `undefined` when `next` starts with every byte of `previous` — identical\n * prefixes, or a pure append, both of which leave a cached prefix intact. Anything\n * else returns the first divergent byte offset and a description of what changed\n * around it. This function reports; it does not repair.\n */\nexport function findPrefixDivergence(previous: string, next: string): PrefixDivergence | undefined {\n const previousBytes = Buffer.from(previous, 'utf8');\n const nextBytes = Buffer.from(next, 'utf8');\n const limit = Math.min(previousBytes.length, nextBytes.length);\n\n let byteOffset = 0;\n while (byteOffset < limit && previousBytes[byteOffset] === nextBytes[byteOffset]) {\n byteOffset += 1;\n }\n if (byteOffset === previousBytes.length) return undefined;\n\n const invalidatedBytes = previousBytes.length - byteOffset;\n const previousExcerpt = JSON.stringify(excerptAroundByte(previous, byteOffset));\n const description =\n byteOffset === nextBytes.length\n ? `next ends at byte ${String(byteOffset)}, where previous continued with ` +\n `${previousExcerpt}; the ${String(invalidatedBytes)} bytes previous carried from ` +\n `there are gone from the prefix`\n : `first divergence at byte ${String(byteOffset)}: previous has ${previousExcerpt} ` +\n `where next has ${JSON.stringify(excerptAroundByte(next, byteOffset))}; a byte-matched ` +\n `cache stops matching here, so the ${String(invalidatedBytes)} bytes after this ` +\n `point are re-sent cold`;\n return { byteOffset, invalidatedBytes, description };\n}\n\n/**\n * One tool definition as the caller sends it. Only `name` is required here —\n * the rest of the definition is scanned structurally, whatever shape it has.\n */\nexport interface PromptTool {\n readonly name: string;\n readonly [key: string]: unknown;\n}\n\n/**\n * The prompt structure smelt is shown for hygiene checks — the two parts that\n * serialize ahead of the messages in the cached prefix. smelt never sees, holds,\n * or edits the real request; the caller hands in a description and gets warnings\n * back.\n */\nexport interface PromptStructure {\n /** Tool definitions, in the order they are sent. */\n readonly tools?: readonly PromptTool[];\n /** The system prompt text. */\n readonly system?: string;\n}\n\n/** ISO-8601-shaped dates and datetimes — the values that move with the clock. */\nconst TIMESTAMP_PATTERN =\n /\\b\\d{4}-\\d{2}-\\d{2}(?:[T ]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?(?:Z|[+-]\\d{2}:?\\d{2})?)?\\b/g;\n\n/** RFC 4122 textual UUIDs — the values that are fresh per process or per call. */\nconst UUID_PATTERN =\n /\\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\\b/g;\n\nfunction byteOffsetOf(text: string, index: number): number {\n return Buffer.byteLength(text.slice(0, index), 'utf8');\n}\n\nfunction scanSystemPrompt(system: string): readonly CacheWarning[] {\n const warnings: CacheWarning[] = [];\n\n const timestamps = [...system.matchAll(TIMESTAMP_PATTERN)];\n const firstTimestamp = timestamps[0];\n if (firstTimestamp !== undefined) {\n warnings.push({\n rule: CACHE_BREAKER_RULES.systemTimestamp,\n explanation:\n `system prompt contains ${String(timestamps.length)} timestamp-shaped ` +\n `value${timestamps.length === 1 ? '' : 's'}, first \"${firstTimestamp[0]}\" at byte ` +\n `${String(byteOffsetOf(system, firstTimestamp.index))}; a value that moves with ` +\n `the clock changes bytes between calls and silently invalidates the cached ` +\n `prefix from that byte`,\n });\n }\n\n const uuids = [...system.matchAll(UUID_PATTERN)];\n const firstUuid = uuids[0];\n if (firstUuid !== undefined) {\n warnings.push({\n rule: CACHE_BREAKER_RULES.systemUuid,\n explanation:\n `system prompt contains ${String(uuids.length)} UUID${uuids.length === 1 ? '' : 's'}, ` +\n `first \"${firstUuid[0]}\" at byte ${String(byteOffsetOf(system, firstUuid.index))}; ` +\n `an id minted per session or per call changes bytes between calls and silently ` +\n `invalidates the cached prefix from that byte`,\n });\n }\n\n return warnings;\n}\n\ninterface UnsortedKeys {\n readonly path: string;\n readonly before: string;\n readonly after: string;\n}\n\n/**\n * Keys that JavaScript treats as array indices. Their enumeration position is\n * fixed by the language (ascending numeric, ahead of every string key), so they\n * are not \"reorderable\" in any sense a serializer could act on.\n */\nconst INTEGER_LIKE_KEY = /^(?:0|[1-9]\\d*)$/;\n\n/** The first object (depth-first) whose own keys are not in sorted order. Read-only walk. */\nfunction findUnsortedKeys(value: unknown, path: string): UnsortedKeys | undefined {\n if (Array.isArray(value)) {\n for (const [i, item] of value.entries()) {\n const found = findUnsortedKeys(item, `${path}[${String(i)}]`);\n if (found !== undefined) return found;\n }\n return undefined;\n }\n if (typeof value !== 'object' || value === null) return undefined;\n const record = value as Record<string, unknown>;\n const keys = Object.keys(record);\n // Array-index-like keys are excluded from the order check: JavaScript enumerates\n // them first, in ascending numeric order, regardless of how they were written or\n // inserted — so no serializer walking the object can emit them differently\n // between calls, and their fixed numeric order (\"2\" before \"10\") is exactly what\n // a lexicographic check would flag. A warning about an order the caller cannot\n // change would be a warning nobody can act on.\n const reorderable = keys.filter((key) => !INTEGER_LIKE_KEY.test(key));\n for (let i = 1; i < reorderable.length; i += 1) {\n if (reorderable[i - 1]! > reorderable[i]!) {\n return { path, before: reorderable[i - 1]!, after: reorderable[i]! };\n }\n }\n for (const key of keys) {\n const found = findUnsortedKeys(record[key], `${path}.${key}`);\n if (found !== undefined) return found;\n }\n return undefined;\n}\n\nfunction scanToolKeyOrder(tools: readonly PromptTool[]): readonly CacheWarning[] {\n const warnings: CacheWarning[] = [];\n for (const tool of tools) {\n const found = findUnsortedKeys(tool, `tool \"${tool.name}\"`);\n if (found !== undefined) {\n warnings.push({\n rule: CACHE_BREAKER_RULES.unsortedJsonKeys,\n explanation:\n `${found.path} has JSON keys out of sorted order (\"${found.before}\" before ` +\n `\"${found.after}\"); a serializer with no canonical key order can emit them ` +\n `differently between calls, changing bytes in the cached prefix with the ` +\n `content unchanged`,\n });\n }\n }\n return warnings;\n}\n\nconst quoteNames = (names: readonly string[]): string =>\n names.map((name) => `\"${name}\"`).join(', ');\n\n/**\n * Stable canonical serialization of one value, for comparing tool definitions\n * between calls the way the cache experiences them: by content. Keys are sorted at\n * every depth, so the comparison is independent of enumeration order — an\n * *ordering* a serializer might vary is `unsorted-json-keys`' concern — while any\n * content change at all (a description edit, a schema tweak, a new field) compares\n * different even when every tool name is unchanged.\n */\nfunction canonicalize(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map((item) => canonicalize(item)).join(',')}]`;\n if (typeof value === 'object' && value !== null) {\n const record = value as Record<string, unknown>;\n const body = Object.keys(record)\n .toSorted()\n .map((key) => `${JSON.stringify(key)}:${canonicalize(record[key])}`)\n .join(',');\n return `{${body}}`;\n }\n return JSON.stringify(value) ?? 'undefined';\n}\n\n/** How many times each tool name appears — duplicates are counted, not collapsed. */\nfunction countByName(tools: readonly PromptTool[]): Map<string, number> {\n const counts = new Map<string, number>();\n for (const tool of tools) counts.set(tool.name, (counts.get(tool.name) ?? 0) + 1);\n return counts;\n}\n\n/**\n * Describe a count change for one name truthfully. When the name also exists on\n * the other side, what changed is the number of copies — saying `added \"x\"` about\n * a duplicate, or claiming \"same tools\" when a duplicate went away, would\n * misstate what the cache saw.\n */\nfunction describeCopies(name: string, delta: number, otherSideHasIt: boolean): string {\n if (!otherSideHasIt) return delta === 1 ? `\"${name}\"` : `${String(delta)} copies of \"${name}\"`;\n return delta === 1 ? `a duplicate of \"${name}\"` : `${String(delta)} duplicates of \"${name}\"`;\n}\n\nfunction compareToolSets(\n previous: readonly PromptTool[],\n current: readonly PromptTool[],\n): CacheWarning | undefined {\n // Content comparison, not name comparison: the cache matches serialized bytes,\n // so a rewritten description under an unchanged name breaks it just as surely as\n // a renamed tool does.\n const previousCanonical = previous.map((tool) => canonicalize(tool));\n const currentCanonical = current.map((tool) => canonicalize(tool));\n const identical =\n previousCanonical.length === currentCanonical.length &&\n previousCanonical.every((tool, i) => tool === currentCanonical[i]);\n if (identical) return undefined;\n\n const previousCounts = countByName(previous);\n const currentCounts = countByName(current);\n const added: string[] = [];\n for (const [name, count] of currentCounts) {\n const delta = count - (previousCounts.get(name) ?? 0);\n if (delta > 0) added.push(describeCopies(name, delta, previousCounts.has(name)));\n }\n const removed: string[] = [];\n for (const [name, count] of previousCounts) {\n const delta = count - (currentCounts.get(name) ?? 0);\n if (delta > 0) removed.push(describeCopies(name, delta, currentCounts.has(name)));\n }\n\n const changes: string[] = [];\n if (added.length > 0) changes.push(`added ${added.join(', ')}`);\n if (removed.length > 0) changes.push(`removed ${removed.join(', ')}`);\n\n if (changes.length === 0) {\n // Every name appears the same number of times on both sides, so what differs\n // is order, content, or both.\n const sameNameOrder =\n previous.length === current.length &&\n previous.every((tool, i) => tool.name === current[i]!.name);\n const sameContent =\n previousCanonical.toSorted().join('\u0000') === currentCanonical.toSorted().join('\u0000');\n if (!sameNameOrder) {\n changes.push(\n sameContent\n ? 'same tools, different order'\n : 'same tool names in a different order, with definitions changed',\n );\n } else {\n const changed = [\n ...new Set(\n current\n .filter((_tool, i) => currentCanonical[i] !== previousCanonical[i])\n .map((tool) => tool.name),\n ),\n ];\n const plural = changed.length === 1 ? '' : 's';\n changes.push(\n `definition${plural} of ${quoteNames(changed)} changed with the name${plural} unchanged`,\n );\n }\n }\n\n return {\n rule: CACHE_BREAKER_RULES.toolSetVaries,\n explanation:\n `tool set changed between calls (${changes.join('; ')}); tools serialize first in ` +\n `the cached prefix (${PREFIX_ORDER_PROSE}), so this invalidates the entire ` +\n `cache including the system prompt and every message`,\n };\n}\n\n/**\n * Detect the named silent cache-breakers in a prompt structure.\n *\n * Three rules over `current` and, when `previous` is supplied, one across the pair:\n *\n * - `system-timestamp` — a timestamp-shaped value in the system prompt\n * - `system-uuid` — a UUID in the system prompt\n * - `unsorted-json-keys` — a tool-definition object whose keys are not sorted\n * - `tool-set-varies` — the tools differ from the previous call's, by content:\n * an added, removed or duplicated tool, a reorder, or a definition rewritten\n * under an unchanged name\n *\n * Pure and read-only: inputs are never mutated, and the return value is warnings\n * only — never a corrected prompt. Deciding what to do about a warning is the\n * caller's call, in the caller's code.\n */\nexport function detectCacheBreakers(\n current: PromptStructure,\n previous?: PromptStructure,\n): readonly CacheWarning[] {\n const warnings: CacheWarning[] = [];\n if (current.system !== undefined) warnings.push(...scanSystemPrompt(current.system));\n if (current.tools !== undefined) warnings.push(...scanToolKeyOrder(current.tools));\n if (previous !== undefined) {\n const varies = compareToolSets(previous.tools ?? [], current.tools ?? []);\n if (varies !== undefined) warnings.push(varies);\n }\n return warnings;\n}\n"]}
|
package/dist/cache/prefix.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefix.js","sourceRoot":"","sources":["../../src/cache/prefix.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"prefix.js","sourceRoot":"","sources":["../../src/cache/prefix.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,6EAA6E;IAC7E,MAAM,EAAE,kFAAkF;IAC1F;;;OAGG;IACH,WAAW,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC;IAC5C,qFAAqF;IACrF,YAAY,EAAE,wDAAwD;IACtE,gFAAgF;IAChF,8BAA8B,EAAE,IAAI;IACpC,uDAAuD;IACvD,mBAAmB,EAAE,CAAC;IACtB,yDAAyD;IACzD,iBAAiB,EAAE,CAAC;IACpB,6DAA6D;IAC7D,kBAAkB,EAAE,EAAE;IACtB,6EAA6E;IAC7E,qBAAqB,EAAE,IAAI;IAC3B,wEAAwE;IACxE,qBAAqB,EAAE,CAAC;IACxB,mFAAmF;IACnF,wBAAwB,EAAE,GAAG;CACrB,CAAC;AAeX;;;GAGG;AACH,MAAM,kBAAkB,GAAG,4BAA4B,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAEhF,mFAAmF;AACnF,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,qDAAqD;IACrD,eAAe,EAAE,kBAAkB;IACnC,mCAAmC;IACnC,UAAU,EAAE,aAAa;IACzB,yEAAyE;IACzE,gBAAgB,EAAE,oBAAoB;IACtC,yDAAyD;IACzD,aAAa,EAAE,iBAAiB;CACxB,CAAC;AAiBX,2FAA2F;AAC3F,SAAS,iBAAiB,CAAC,IAAY,EAAE,UAAkB,EAAE,MAAM,GAAG,EAAE;IACtE,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IACzB,IAAI,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;IAC1B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC/C,IAAI,UAAU,GAAG,KAAK,GAAG,KAAK,EAAE,CAAC;YAC/B,KAAK,GAAG,CAAC,CAAC;YACV,MAAM;QACR,CAAC;QACD,KAAK,IAAI,KAAK,CAAC;IACjB,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5C,OAAO,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;AACzD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAgB,EAAE,IAAY;IACjE,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IAE/D,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,OAAO,UAAU,GAAG,KAAK,IAAI,aAAa,CAAC,UAAU,CAAC,KAAK,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC;QACjF,UAAU,IAAI,CAAC,CAAC;IAClB,CAAC;IACD,IAAI,UAAU,KAAK,aAAa,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAE1D,MAAM,gBAAgB,GAAG,aAAa,CAAC,MAAM,GAAG,UAAU,CAAC;IAC3D,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC;IAChF,MAAM,WAAW,GACf,UAAU,KAAK,SAAS,CAAC,MAAM;QAC7B,CAAC,CAAC,qBAAqB,MAAM,CAAC,UAAU,CAAC,kCAAkC;YACzE,GAAG,eAAe,SAAS,MAAM,CAAC,gBAAgB,CAAC,+BAA+B;YAClF,gCAAgC;QAClC,CAAC,CAAC,4BAA4B,MAAM,CAAC,UAAU,CAAC,kBAAkB,eAAe,GAAG;YAClF,kBAAkB,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,mBAAmB;YACxF,qCAAqC,MAAM,CAAC,gBAAgB,CAAC,oBAAoB;YACjF,wBAAwB,CAAC;IAC/B,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,WAAW,EAAE,CAAC;AACvD,CAAC;AAwBD,iFAAiF;AACjF,MAAM,iBAAiB,GACrB,wFAAwF,CAAC;AAE3F,kFAAkF;AAClF,MAAM,YAAY,GAChB,kFAAkF,CAAC;AAErF,SAAS,YAAY,CAAC,IAAY,EAAE,KAAa;IAC/C,OAAO,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAc;IACtC,MAAM,QAAQ,GAAmB,EAAE,CAAC;IAEpC,MAAM,UAAU,GAAG,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAC3D,MAAM,cAAc,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IACrC,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,mBAAmB,CAAC,eAAe;YACzC,WAAW,EACT,0BAA0B,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,oBAAoB;gBACvE,QAAQ,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,YAAY,cAAc,CAAC,CAAC,CAAC,YAAY;gBACnF,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,4BAA4B;gBACjF,4EAA4E;gBAC5E,uBAAuB;SAC1B,CAAC,CAAC;IACL,CAAC;IAED,MAAM,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC;IACjD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,mBAAmB,CAAC,UAAU;YACpC,WAAW,EACT,0BAA0B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI;gBACvF,UAAU,SAAS,CAAC,CAAC,CAAC,aAAa,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI;gBACpF,gFAAgF;gBAChF,8CAA8C;SACjD,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAQD;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAE5C,6FAA6F;AAC7F,SAAS,gBAAgB,CAAC,KAAc,EAAE,IAAY;IACpD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YACxC,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAC9D,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,KAAK,CAAC;QACxC,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAClE,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,iFAAiF;IACjF,iFAAiF;IACjF,2EAA2E;IAC3E,iFAAiF;IACjF,+EAA+E;IAC/E,+CAA+C;IAC/C,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACtE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/C,IAAI,WAAW,CAAC,CAAC,GAAG,CAAC,CAAE,GAAG,WAAW,CAAC,CAAC,CAAE,EAAE,CAAC;YAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,GAAG,CAAC,CAAE,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAE,EAAE,CAAC;QACvE,CAAC;IACH,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC;QAC9D,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;IACxC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,gBAAgB,CAAC,KAA4B;IACpD,MAAM,QAAQ,GAAmB,EAAE,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,EAAE,SAAS,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QAC5D,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,mBAAmB,CAAC,gBAAgB;gBAC1C,WAAW,EACT,GAAG,KAAK,CAAC,IAAI,wCAAwC,KAAK,CAAC,MAAM,WAAW;oBAC5E,IAAI,KAAK,CAAC,KAAK,6DAA6D;oBAC5E,0EAA0E;oBAC1E,mBAAmB;aACtB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,KAAwB,EAAU,EAAE,CACtD,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAE9C;;;;;;;GAOG;AACH,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAC1F,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,MAAM,MAAM,GAAG,KAAgC,CAAC;QAChD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;aAC7B,QAAQ,EAAE;aACV,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;aACnE,IAAI,CAAC,GAAG,CAAC,CAAC;QACb,OAAO,IAAI,IAAI,GAAG,CAAC;IACrB,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC;AAC9C,CAAC;AAED,qFAAqF;AACrF,SAAS,WAAW,CAAC,KAA4B;IAC/C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAClF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,SAAS,cAAc,CAAC,IAAY,EAAE,KAAa,EAAE,cAAuB;IAC1E,IAAI,CAAC,cAAc;QAAE,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,GAAG,CAAC;IAC/F,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,mBAAmB,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,mBAAmB,IAAI,GAAG,CAAC;AAC/F,CAAC;AAED,SAAS,eAAe,CACtB,QAA+B,EAC/B,OAA8B;IAE9B,+EAA+E;IAC/E,iFAAiF;IACjF,uBAAuB;IACvB,MAAM,iBAAiB,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;IACrE,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;IACnE,MAAM,SAAS,GACb,iBAAiB,CAAC,MAAM,KAAK,gBAAgB,CAAC,MAAM;QACpD,iBAAiB,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC;IAEhC,MAAM,cAAc,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,aAAa,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,aAAa,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,KAAK,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACtD,IAAI,KAAK,GAAG,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACnF,CAAC;IACD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,cAAc,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,KAAK,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACrD,IAAI,KAAK,GAAG,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpF,CAAC;IAED,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,SAAS,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,WAAW,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEtE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,6EAA6E;QAC7E,8BAA8B;QAC9B,MAAM,aAAa,GACjB,QAAQ,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM;YAClC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC;QAC9D,MAAM,WAAW,GACf,iBAAiB,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,gBAAgB,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnF,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO,CAAC,IAAI,CACV,WAAW;gBACT,CAAC,CAAC,6BAA6B;gBAC/B,CAAC,CAAC,gEAAgE,CACrE,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,OAAO,GAAG;gBACd,GAAG,IAAI,GAAG,CACR,OAAO;qBACJ,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,iBAAiB,CAAC,CAAC,CAAC,CAAC;qBAClE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAC5B;aACF,CAAC;YACF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;YAC/C,OAAO,CAAC,IAAI,CACV,aAAa,MAAM,OAAO,UAAU,CAAC,OAAO,CAAC,yBAAyB,MAAM,YAAY,CACzF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI,EAAE,mBAAmB,CAAC,aAAa;QACvC,WAAW,EACT,mCAAmC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,8BAA8B;YACnF,sBAAsB,kBAAkB,oCAAoC;YAC5E,qDAAqD;KACxD,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAwB,EACxB,QAA0B;IAE1B,MAAM,QAAQ,GAAmB,EAAE,CAAC;IACpC,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;QAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IACrF,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACnF,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QAC1E,IAAI,MAAM,KAAK,SAAS;YAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC","sourcesContent":["/**\n * Cache-prefix hygiene — Slice 6. **Detect and warn, never rewrite.**\n *\n * **This module is a consumer-facing surface**, exported from the package\n * entrypoint and stable in intent: hand it your prompt structure, get warnings\n * back, decide for yourself. It composes with nothing else in smelt — no planner\n * calls it, no store holds its output — because cache hygiene is a property of the\n * *request* a consumer assembles, which smelt never sees or intercepts. Use it\n * from your own send path:\n *\n * ```ts\n * // Both functions come from the package entrypoint:\n * // detectCacheBreakers, findPrefixDivergence\n *\n * // Before sending: name the silent cache-breakers in what you are about to send.\n * const warnings = detectCacheBreakers(\n * { tools, system }, // this call\n * { tools: previousTools }, // optional: the previous call, for tool-set drift\n * );\n * // → [{ rule: 'system-timestamp', explanation: 'system prompt contains …' }, …]\n *\n * // Between calls: where exactly did the cached prefix stop matching, and what\n * // did the change cost? `undefined` means intact (identical, or a pure append).\n * const divergence = findPrefixDivergence(previousPrefix, nextPrefix);\n * // → { byteOffset, invalidatedBytes, description } | undefined\n * ```\n *\n * Nothing is ever rewritten on your behalf, and no rate is ever claimed — the\n * paragraphs below say why both refusals are load-bearing.\n *\n * Provider prompt caches match the request prefix byte for byte, so a context\n * optimizer that reorders or rewrites a prompt prefix to \"help\" a cache can cost\n * more than it saves — and worse, a reordering that changes model behaviour is an\n * unexplainable elision wearing a different hat. Headroom's CacheAligner made the\n * same call: it detects prefix volatility and warns; it never edits the prompt.\n * Neither does this module. Every function here is a pure read over its inputs:\n * nothing is mutated, and nothing \"fixed\" is ever returned. That is not a comment,\n * it is a guarantee — `test/guards/cache-hygiene.test.ts` asserts it on frozen\n * inputs, and `pnpm mutate` proves the guard goes red when a helpful in-place fix\n * appears.\n *\n * Warnings follow the `ElisionReason` two-field discipline from `types.ts`:\n * a stable `rule` id for counters, and an `explanation` a human reads. A warning\n * that cannot be written as a sentence is a rule nobody understands.\n */\n\n/**\n * Facts about Anthropic's prompt cache, as published in Anthropic's prompt-caching\n * documentation (docs.anthropic.com), verified 2026-09-01.\n *\n * These are *cited provider facts*, not smelt measurements — the only numbers Law 4\n * permits are someone else's, with their source and date named. In particular this\n * module claims nothing about how often anyone's cache hits: the price multipliers\n * below are prices, and a price is not a frequency.\n */\nexport const ANTHROPIC_PROMPT_CACHE_FACTS = {\n /** Where every number in this object comes from, and when it was checked. */\n source: 'Anthropic prompt-caching documentation (docs.anthropic.com), verified 2026-09-01',\n /**\n * The cached prefix is matched byte for byte over the request in this order.\n * A byte change in `tools` therefore invalidates `system` and `messages` too.\n */\n prefixOrder: ['tools', 'system', 'messages'],\n /** Any byte change invalidates the cache from that byte to the end of the prefix. */\n invalidation: 'byte-exact; any change invalidates everything after it',\n /** Minimum cacheable prefix, in tokens — approximately 1024 for most models. */\n minCacheablePrefixTokensApprox: 1024,\n /** Maximum number of cache breakpoints per request. */\n maxCacheBreakpoints: 4,\n /** Default time-to-live of a cache entry, in minutes. */\n defaultTtlMinutes: 5,\n /** Optional extended time-to-live, in minutes (one hour). */\n extendedTtlMinutes: 60,\n /** Writing a 5-minute cache entry costs 1.25x the base input-token price. */\n writeCostMultiplier5m: 1.25,\n /** Writing a 1-hour cache entry costs 2x the base input-token price. */\n writeCostMultiplier1h: 2,\n /** Reading a cached prefix costs approximately 0.1x the base input-token price. */\n readCostMultiplierApprox: 0.1,\n} as const;\n\n/**\n * A cache-hygiene warning. Deliberately the same two-field shape as\n * `ElisionReason` in `types.ts`, and for the same reason: a stable id for counters, a\n * sentence for humans, and nothing else — no patch, no replacement text, no\n * \"fixed\" prompt. Warning is all this module does.\n */\nexport interface CacheWarning {\n /** Stable machine id, e.g. `'system-timestamp'`. */\n readonly rule: string;\n /** Present tense, no trailing period, names the evidence. */\n readonly explanation: string;\n}\n\n/**\n * The provider's serialization order, rendered as prose from the cited constant\n * above — so the sentence in a warning can never drift from the fact it cites.\n */\nconst PREFIX_ORDER_PROSE = ANTHROPIC_PROMPT_CACHE_FACTS.prefixOrder.join(' → ');\n\n/** The stable rule ids this module can emit. Additive over time, never renamed. */\nexport const CACHE_BREAKER_RULES = {\n /** A timestamp-shaped value in the system prompt. */\n systemTimestamp: 'system-timestamp',\n /** A UUID in the system prompt. */\n systemUuid: 'system-uuid',\n /** An object in a tool definition whose keys are not in sorted order. */\n unsortedJsonKeys: 'unsorted-json-keys',\n /** The tool set changed between two successive calls. */\n toolSetVaries: 'tool-set-varies',\n} as const;\n\n/**\n * The first byte where two successive prompt prefixes diverge, with a description\n * of what changed around it. Offsets are UTF-8 **byte** offsets, because that is\n * the unit the provider's cache matches in — a code-unit index would be a lie for\n * any prompt containing a multi-byte character.\n */\nexport interface PrefixDivergence {\n /** UTF-8 byte offset of the first byte at which the two prefixes differ. */\n readonly byteOffset: number;\n /** Bytes of `previous` from the divergence to its end — the span a cache no longer matches. */\n readonly invalidatedBytes: number;\n /** What changed around the divergence. Excerpts never split a multi-byte character. */\n readonly description: string;\n}\n\n/** Code points around the code point containing `byteOffset` — never a split character. */\nfunction excerptAroundByte(text: string, byteOffset: number, radius = 16): string {\n const points = [...text];\n let index = points.length;\n let bytes = 0;\n for (const [i, point] of points.entries()) {\n const width = Buffer.byteLength(point, 'utf8');\n if (byteOffset < bytes + width) {\n index = i;\n break;\n }\n bytes += width;\n }\n const start = Math.max(0, index - radius);\n const end = Math.min(points.length, index + radius);\n const head = start > 0 ? '…' : '';\n const tail = end < points.length ? '…' : '';\n return head + points.slice(start, end).join('') + tail;\n}\n\n/**\n * Compare two successive prompt prefixes the way the provider's cache does: byte\n * for byte, in UTF-8.\n *\n * Returns `undefined` when `next` starts with every byte of `previous` — identical\n * prefixes, or a pure append, both of which leave a cached prefix intact. Anything\n * else returns the first divergent byte offset and a description of what changed\n * around it. This function reports; it does not repair.\n */\nexport function findPrefixDivergence(previous: string, next: string): PrefixDivergence | undefined {\n const previousBytes = Buffer.from(previous, 'utf8');\n const nextBytes = Buffer.from(next, 'utf8');\n const limit = Math.min(previousBytes.length, nextBytes.length);\n\n let byteOffset = 0;\n while (byteOffset < limit && previousBytes[byteOffset] === nextBytes[byteOffset]) {\n byteOffset += 1;\n }\n if (byteOffset === previousBytes.length) return undefined;\n\n const invalidatedBytes = previousBytes.length - byteOffset;\n const previousExcerpt = JSON.stringify(excerptAroundByte(previous, byteOffset));\n const description =\n byteOffset === nextBytes.length\n ? `next ends at byte ${String(byteOffset)}, where previous continued with ` +\n `${previousExcerpt}; the ${String(invalidatedBytes)} bytes previous carried from ` +\n `there are gone from the prefix`\n : `first divergence at byte ${String(byteOffset)}: previous has ${previousExcerpt} ` +\n `where next has ${JSON.stringify(excerptAroundByte(next, byteOffset))}; a byte-matched ` +\n `cache stops matching here, so the ${String(invalidatedBytes)} bytes after this ` +\n `point are re-sent cold`;\n return { byteOffset, invalidatedBytes, description };\n}\n\n/**\n * One tool definition as the caller sends it. Only `name` is required here —\n * the rest of the definition is scanned structurally, whatever shape it has.\n */\nexport interface PromptTool {\n readonly name: string;\n readonly [key: string]: unknown;\n}\n\n/**\n * The prompt structure smelt is shown for hygiene checks — the two parts that\n * serialize ahead of the messages in the cached prefix. smelt never sees, holds,\n * or edits the real request; the caller hands in a description and gets warnings\n * back.\n */\nexport interface PromptStructure {\n /** Tool definitions, in the order they are sent. */\n readonly tools?: readonly PromptTool[];\n /** The system prompt text. */\n readonly system?: string;\n}\n\n/** ISO-8601-shaped dates and datetimes — the values that move with the clock. */\nconst TIMESTAMP_PATTERN =\n /\\b\\d{4}-\\d{2}-\\d{2}(?:[T ]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?(?:Z|[+-]\\d{2}:?\\d{2})?)?\\b/g;\n\n/** RFC 4122 textual UUIDs — the values that are fresh per process or per call. */\nconst UUID_PATTERN =\n /\\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\\b/g;\n\nfunction byteOffsetOf(text: string, index: number): number {\n return Buffer.byteLength(text.slice(0, index), 'utf8');\n}\n\nfunction scanSystemPrompt(system: string): readonly CacheWarning[] {\n const warnings: CacheWarning[] = [];\n\n const timestamps = [...system.matchAll(TIMESTAMP_PATTERN)];\n const firstTimestamp = timestamps[0];\n if (firstTimestamp !== undefined) {\n warnings.push({\n rule: CACHE_BREAKER_RULES.systemTimestamp,\n explanation:\n `system prompt contains ${String(timestamps.length)} timestamp-shaped ` +\n `value${timestamps.length === 1 ? '' : 's'}, first \"${firstTimestamp[0]}\" at byte ` +\n `${String(byteOffsetOf(system, firstTimestamp.index))}; a value that moves with ` +\n `the clock changes bytes between calls and silently invalidates the cached ` +\n `prefix from that byte`,\n });\n }\n\n const uuids = [...system.matchAll(UUID_PATTERN)];\n const firstUuid = uuids[0];\n if (firstUuid !== undefined) {\n warnings.push({\n rule: CACHE_BREAKER_RULES.systemUuid,\n explanation:\n `system prompt contains ${String(uuids.length)} UUID${uuids.length === 1 ? '' : 's'}, ` +\n `first \"${firstUuid[0]}\" at byte ${String(byteOffsetOf(system, firstUuid.index))}; ` +\n `an id minted per session or per call changes bytes between calls and silently ` +\n `invalidates the cached prefix from that byte`,\n });\n }\n\n return warnings;\n}\n\ninterface UnsortedKeys {\n readonly path: string;\n readonly before: string;\n readonly after: string;\n}\n\n/**\n * Keys that JavaScript treats as array indices. Their enumeration position is\n * fixed by the language (ascending numeric, ahead of every string key), so they\n * are not \"reorderable\" in any sense a serializer could act on.\n */\nconst INTEGER_LIKE_KEY = /^(?:0|[1-9]\\d*)$/;\n\n/** The first object (depth-first) whose own keys are not in sorted order. Read-only walk. */\nfunction findUnsortedKeys(value: unknown, path: string): UnsortedKeys | undefined {\n if (Array.isArray(value)) {\n for (const [i, item] of value.entries()) {\n const found = findUnsortedKeys(item, `${path}[${String(i)}]`);\n if (found !== undefined) return found;\n }\n return undefined;\n }\n if (typeof value !== 'object' || value === null) return undefined;\n const record = value as Record<string, unknown>;\n const keys = Object.keys(record);\n // Array-index-like keys are excluded from the order check: JavaScript enumerates\n // them first, in ascending numeric order, regardless of how they were written or\n // inserted — so no serializer walking the object can emit them differently\n // between calls, and their fixed numeric order (\"2\" before \"10\") is exactly what\n // a lexicographic check would flag. A warning about an order the caller cannot\n // change would be a warning nobody can act on.\n const reorderable = keys.filter((key) => !INTEGER_LIKE_KEY.test(key));\n for (let i = 1; i < reorderable.length; i += 1) {\n if (reorderable[i - 1]! > reorderable[i]!) {\n return { path, before: reorderable[i - 1]!, after: reorderable[i]! };\n }\n }\n for (const key of keys) {\n const found = findUnsortedKeys(record[key], `${path}.${key}`);\n if (found !== undefined) return found;\n }\n return undefined;\n}\n\nfunction scanToolKeyOrder(tools: readonly PromptTool[]): readonly CacheWarning[] {\n const warnings: CacheWarning[] = [];\n for (const tool of tools) {\n const found = findUnsortedKeys(tool, `tool \"${tool.name}\"`);\n if (found !== undefined) {\n warnings.push({\n rule: CACHE_BREAKER_RULES.unsortedJsonKeys,\n explanation:\n `${found.path} has JSON keys out of sorted order (\"${found.before}\" before ` +\n `\"${found.after}\"); a serializer with no canonical key order can emit them ` +\n `differently between calls, changing bytes in the cached prefix with the ` +\n `content unchanged`,\n });\n }\n }\n return warnings;\n}\n\nconst quoteNames = (names: readonly string[]): string =>\n names.map((name) => `\"${name}\"`).join(', ');\n\n/**\n * Stable canonical serialization of one value, for comparing tool definitions\n * between calls the way the cache experiences them: by content. Keys are sorted at\n * every depth, so the comparison is independent of enumeration order — an\n * *ordering* a serializer might vary is `unsorted-json-keys`' concern — while any\n * content change at all (a description edit, a schema tweak, a new field) compares\n * different even when every tool name is unchanged.\n */\nfunction canonicalize(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map((item) => canonicalize(item)).join(',')}]`;\n if (typeof value === 'object' && value !== null) {\n const record = value as Record<string, unknown>;\n const body = Object.keys(record)\n .toSorted()\n .map((key) => `${JSON.stringify(key)}:${canonicalize(record[key])}`)\n .join(',');\n return `{${body}}`;\n }\n return JSON.stringify(value) ?? 'undefined';\n}\n\n/** How many times each tool name appears — duplicates are counted, not collapsed. */\nfunction countByName(tools: readonly PromptTool[]): Map<string, number> {\n const counts = new Map<string, number>();\n for (const tool of tools) counts.set(tool.name, (counts.get(tool.name) ?? 0) + 1);\n return counts;\n}\n\n/**\n * Describe a count change for one name truthfully. When the name also exists on\n * the other side, what changed is the number of copies — saying `added \"x\"` about\n * a duplicate, or claiming \"same tools\" when a duplicate went away, would\n * misstate what the cache saw.\n */\nfunction describeCopies(name: string, delta: number, otherSideHasIt: boolean): string {\n if (!otherSideHasIt) return delta === 1 ? `\"${name}\"` : `${String(delta)} copies of \"${name}\"`;\n return delta === 1 ? `a duplicate of \"${name}\"` : `${String(delta)} duplicates of \"${name}\"`;\n}\n\nfunction compareToolSets(\n previous: readonly PromptTool[],\n current: readonly PromptTool[],\n): CacheWarning | undefined {\n // Content comparison, not name comparison: the cache matches serialized bytes,\n // so a rewritten description under an unchanged name breaks it just as surely as\n // a renamed tool does.\n const previousCanonical = previous.map((tool) => canonicalize(tool));\n const currentCanonical = current.map((tool) => canonicalize(tool));\n const identical =\n previousCanonical.length === currentCanonical.length &&\n previousCanonical.every((tool, i) => tool === currentCanonical[i]);\n if (identical) return undefined;\n\n const previousCounts = countByName(previous);\n const currentCounts = countByName(current);\n const added: string[] = [];\n for (const [name, count] of currentCounts) {\n const delta = count - (previousCounts.get(name) ?? 0);\n if (delta > 0) added.push(describeCopies(name, delta, previousCounts.has(name)));\n }\n const removed: string[] = [];\n for (const [name, count] of previousCounts) {\n const delta = count - (currentCounts.get(name) ?? 0);\n if (delta > 0) removed.push(describeCopies(name, delta, currentCounts.has(name)));\n }\n\n const changes: string[] = [];\n if (added.length > 0) changes.push(`added ${added.join(', ')}`);\n if (removed.length > 0) changes.push(`removed ${removed.join(', ')}`);\n\n if (changes.length === 0) {\n // Every name appears the same number of times on both sides, so what differs\n // is order, content, or both.\n const sameNameOrder =\n previous.length === current.length &&\n previous.every((tool, i) => tool.name === current[i]!.name);\n const sameContent =\n previousCanonical.toSorted().join('\u0000') === currentCanonical.toSorted().join('\u0000');\n if (!sameNameOrder) {\n changes.push(\n sameContent\n ? 'same tools, different order'\n : 'same tool names in a different order, with definitions changed',\n );\n } else {\n const changed = [\n ...new Set(\n current\n .filter((_tool, i) => currentCanonical[i] !== previousCanonical[i])\n .map((tool) => tool.name),\n ),\n ];\n const plural = changed.length === 1 ? '' : 's';\n changes.push(\n `definition${plural} of ${quoteNames(changed)} changed with the name${plural} unchanged`,\n );\n }\n }\n\n return {\n rule: CACHE_BREAKER_RULES.toolSetVaries,\n explanation:\n `tool set changed between calls (${changes.join('; ')}); tools serialize first in ` +\n `the cached prefix (${PREFIX_ORDER_PROSE}), so this invalidates the entire ` +\n `cache including the system prompt and every message`,\n };\n}\n\n/**\n * Detect the named silent cache-breakers in a prompt structure.\n *\n * Three rules over `current` and, when `previous` is supplied, one across the pair:\n *\n * - `system-timestamp` — a timestamp-shaped value in the system prompt\n * - `system-uuid` — a UUID in the system prompt\n * - `unsorted-json-keys` — a tool-definition object whose keys are not sorted\n * - `tool-set-varies` — the tools differ from the previous call's, by content:\n * an added, removed or duplicated tool, a reorder, or a definition rewritten\n * under an unchanged name\n *\n * Pure and read-only: inputs are never mutated, and the return value is warnings\n * only — never a corrected prompt. Deciding what to do about a warning is the\n * caller's call, in the caller's code.\n */\nexport function detectCacheBreakers(\n current: PromptStructure,\n previous?: PromptStructure,\n): readonly CacheWarning[] {\n const warnings: CacheWarning[] = [];\n if (current.system !== undefined) warnings.push(...scanSystemPrompt(current.system));\n if (current.tools !== undefined) warnings.push(...scanToolKeyOrder(current.tools));\n if (previous !== undefined) {\n const varies = compareToolSets(previous.tools ?? [], current.tools ?? []);\n if (varies !== undefined) warnings.push(varies);\n }\n return warnings;\n}\n"]}
|
package/dist/cli/args.d.ts
CHANGED
|
@@ -1,71 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* UTF-8 bytes. `undefined` in `'smelt'` mode means the flag was not given; the
|
|
12
|
-
* runner then consults `smelt.config.json` and errors if that has no default either.
|
|
13
|
-
*/
|
|
14
|
-
readonly budgetBytes?: number;
|
|
15
|
-
readonly focus: readonly string[];
|
|
16
|
-
readonly language?: DetectedLanguage;
|
|
17
|
-
/** `undefined` means the flag was not given — the config default may apply. */
|
|
18
|
-
readonly strategy?: Strategy;
|
|
19
|
-
readonly json: boolean;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* `smelt map <dir>` — the repo-map subcommand, parsed. A separate shape rather than
|
|
23
|
-
* more optional fields on {@link SmeltInvocation}, because the two commands share
|
|
24
|
-
* almost nothing: a map has a directory instead of a file/stdin, an ignore list and
|
|
25
|
-
* a cache directory instead of a language and a strategy.
|
|
26
|
-
*/
|
|
27
|
-
export interface MapInvocation {
|
|
28
|
-
readonly mode: 'map';
|
|
29
|
-
/** The repository root to map. Always present — `map` without a directory is a usage error. */
|
|
30
|
-
readonly dir: string;
|
|
31
|
-
/** `undefined` means the flag was not given — the config default may apply. */
|
|
32
|
-
readonly budgetBytes?: number;
|
|
33
|
-
readonly focus: readonly string[];
|
|
34
|
-
/** `--ignore` entries, replacing the built-in default list when non-empty. */
|
|
35
|
-
readonly ignore: readonly string[];
|
|
36
|
-
/** `--cache <dir>`: only when given does the map write to disk. */
|
|
37
|
-
readonly cacheDir?: string;
|
|
38
|
-
readonly json: boolean;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* `smelt retrieve <hash>` — the marker's `retrieve("hash")` as a real command, parsed.
|
|
42
|
-
* A {@link MapInvocation}-style sibling: nothing but the hash, because the command's
|
|
43
|
-
* whole contract is "hash in, exact bytes out" — the same contract as the
|
|
44
|
-
* `smelt_retrieve` tool, reachable from a shell.
|
|
45
|
-
*/
|
|
46
|
-
export interface RetrieveInvocation {
|
|
47
|
-
readonly mode: 'retrieve';
|
|
48
|
-
/** The hash exactly as the marker printed it. Validated by the store, not here. */
|
|
49
|
-
readonly hash: string;
|
|
50
|
-
}
|
|
51
|
-
/** `smelt stats` — the store's counters, read without touching them. */
|
|
52
|
-
export interface StatsInvocation {
|
|
53
|
-
readonly mode: 'stats';
|
|
54
|
-
readonly json: boolean;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* `smelt hooks install` / `smelt hooks remove` — the harness-hooks installer. Like
|
|
58
|
-
* `init`, the subcommand is interactive; the only flag is `--harness`, which skips
|
|
59
|
-
* the selection step. Validation of the id happens in `cli/hooks.ts`, where the
|
|
60
|
-
* harness registry lives.
|
|
61
|
-
*/
|
|
62
|
-
export interface HooksInvocation {
|
|
63
|
-
readonly mode: 'hooks';
|
|
64
|
-
readonly action: 'install' | 'remove';
|
|
65
|
-
readonly harness?: string;
|
|
66
|
-
}
|
|
67
|
-
/** Everything `parseSmeltArgs` can return. Narrow on `mode`. */
|
|
68
|
-
export type CliInvocation = SmeltInvocation | MapInvocation | RetrieveInvocation | StatsInvocation | HooksInvocation;
|
|
1
|
+
export { CLI_NAME } from './shell.ts';
|
|
2
|
+
export { cliUsage } from './usage.ts';
|
|
3
|
+
export type { CliInvocation } from './subcommands/registry.ts';
|
|
4
|
+
export type { HooksInvocation } from './subcommands/hooks.ts';
|
|
5
|
+
export type { InitInvocation } from './subcommands/init.ts';
|
|
6
|
+
export type { MapInvocation } from './subcommands/map.ts';
|
|
7
|
+
export type { RetrieveInvocation } from './subcommands/retrieve.ts';
|
|
8
|
+
export type { SmeltInvocation } from './subcommands/smelt.ts';
|
|
9
|
+
export type { StatsInvocation } from './subcommands/stats.ts';
|
|
10
|
+
import type { CliInvocation } from './subcommands/registry.ts';
|
|
69
11
|
/**
|
|
70
12
|
* Argument parsing on `node:util.parseArgs` — stable since Node 20, which `engines`
|
|
71
13
|
* already requires.
|
|
@@ -74,9 +16,13 @@ export type CliInvocation = SmeltInvocation | MapInvocation | RetrieveInvocation
|
|
|
74
16
|
* import is the reason that is free: it adds no dependency, so the argument the second
|
|
75
17
|
* package existed to win — keeping the library's dependency tree small — is already won.
|
|
76
18
|
*
|
|
19
|
+
* The shape of this function is the shape of the subcommand seam: split the argv into
|
|
20
|
+
* flags and positionals, answer the two global flags, **look the verb up in
|
|
21
|
+
* `SUBCOMMANDS`**, refuse every flag that verb does not own with one generated
|
|
22
|
+
* message, and let the verb do its own validation. There is no per-verb branching left
|
|
23
|
+
* here — a seventh verb is a seventh file, not a seventh `if`.
|
|
24
|
+
*
|
|
77
25
|
* @throws {CliUsageError} on anything the user got wrong. Never guesses.
|
|
78
26
|
*/
|
|
79
27
|
export declare function parseSmeltArgs(argv: readonly string[]): CliInvocation;
|
|
80
|
-
/** The help text. Also the closest thing the CLI has to documentation. */
|
|
81
|
-
export declare function cliUsage(): string;
|
|
82
28
|
//# sourceMappingURL=args.d.ts.map
|
package/dist/cli/args.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,aAAa,CA0BrE","sourcesContent":["import { parseArgs } from 'node:util';\n\nimport { CliUsageError } from '../errors.ts';\n\nimport { CLI_NAME } from './shell.ts';\nimport { CLI_FLAGS } from './subcommands/flags.ts';\nimport { refuseForeignFlags, subcommandFor } from './subcommands/registry.ts';\n\nexport { CLI_NAME } from './shell.ts';\nexport { cliUsage } from './usage.ts';\nexport type { CliInvocation } from './subcommands/registry.ts';\nexport type { HooksInvocation } from './subcommands/hooks.ts';\nexport type { InitInvocation } from './subcommands/init.ts';\nexport type { MapInvocation } from './subcommands/map.ts';\nexport type { RetrieveInvocation } from './subcommands/retrieve.ts';\nexport type { SmeltInvocation } from './subcommands/smelt.ts';\nexport type { StatsInvocation } from './subcommands/stats.ts';\n\nimport type { CliInvocation } from './subcommands/registry.ts';\n\n/**\n * Argument parsing on `node:util.parseArgs` — stable since Node 20, which `engines`\n * already requires.\n *\n * The CLI ships as a `bin` on the library rather than as a second package, and this\n * import is the reason that is free: it adds no dependency, so the argument the second\n * package existed to win — keeping the library's dependency tree small — is already won.\n *\n * The shape of this function is the shape of the subcommand seam: split the argv into\n * flags and positionals, answer the two global flags, **look the verb up in\n * `SUBCOMMANDS`**, refuse every flag that verb does not own with one generated\n * message, and let the verb do its own validation. There is no per-verb branching left\n * here — a seventh verb is a seventh file, not a seventh `if`.\n *\n * @throws {CliUsageError} on anything the user got wrong. Never guesses.\n */\nexport function parseSmeltArgs(argv: readonly string[]): CliInvocation {\n let parsed;\n try {\n parsed = parseArgs({\n args: [...argv],\n allowPositionals: true,\n strict: true,\n options: CLI_FLAGS,\n });\n } catch (cause) {\n throw new CliUsageError(\n `${CLI_NAME}: ${cause instanceof Error ? cause.message : String(cause)}\\n` +\n `Run \\`${CLI_NAME} --help\\`.`,\n );\n }\n\n const { values, positionals } = parsed;\n\n // Answered before any verb, so `smelt map --help` prints the help rather than being\n // refused for a flag `map` does not own. No verb may claim these two.\n if (values.help === true) return { mode: 'help', focus: [], json: false };\n if (values.version === true) return { mode: 'version', focus: [], json: false };\n\n const command = subcommandFor(positionals);\n refuseForeignFlags(command, values);\n return command.parse(values, positionals);\n}\n"]}
|