tokenfold 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,19 +1,48 @@
1
- # tokenfold for Node.js
1
+ # tokenfold for Node.js
2
2
 
3
- Zero-runtime-dependency TypeScript bindings for the tokenfold Rust CLI.
3
+ Zero-runtime-dependency TypeScript bindings for the Tokenfold Rust CLI.
4
4
 
5
5
  ```sh
6
6
  npm install tokenfold
7
7
  ```
8
8
 
9
9
  ```ts
10
- import { compress } from "tokenfold";
10
+ import { compress, decode, inspect } from "tokenfold";
11
11
 
12
- const { payload, report } = await compress(input, {
12
+ const receipt = await inspect(input, { format: "json", preset: "balanced" });
13
+ const { payload, report, text } = await compress(input, {
13
14
  format: "json",
14
- mode: "balanced",
15
+ preset: "balanced",
15
16
  });
16
17
  ```
17
18
 
19
+ Payloads are `Uint8Array`; the `text` convenience getter decodes UTF-8 strictly.
20
+ `inspect` returns only the side-effect-free receipt.
21
+
22
+ Recoverable pruning is explicit and generic-JSON-only:
23
+
24
+ ```ts
25
+ import { compress, retrieve } from "tokenfold";
26
+
27
+ const result = await compress(feed, {
28
+ format: "json",
29
+ targetTokens: 2_000,
30
+ pruning: {
31
+ keepRatio: 0.35,
32
+ preservePaths: ["meta"],
33
+ retrievalStore: ".tokenfold/retrieve",
34
+ },
35
+ });
36
+ const marker = JSON.parse(result.text).items.find((item) => item.$tf_ref);
37
+ const original = await retrieve(marker, { retrievalStore: ".tokenfold/retrieve" });
38
+ ```
39
+
40
+ Explicit TOON output is verified before emission and restored with `decode`:
41
+
42
+ ```ts
43
+ const encoded = await compress(input, { format: "json", encoding: "toon" });
44
+ const jsonBytes = await decode(encoded.payload, { from: "toon" });
45
+ ```
46
+
18
47
  Requires Node.js 22 or newer. The matching native CLI is installed through an
19
48
  optional platform package; set `TOKENFOLD_BINARY_PATH` to use a custom binary.
package/dist/index.d.ts CHANGED
@@ -3,17 +3,23 @@ import { TokenFoldProcessError } from "./errors.js";
3
3
  import { run, type Input, type ProcessResult, type RunOptions } from "./process.js";
4
4
  export { binaryPath, run, TokenFoldProcessError };
5
5
  export type { Input, ProcessResult, RunOptions };
6
- export type CompressionMode = "conservative" | "balanced" | "aggressive";
7
- export type TaskScope = "all" | "general" | "code_review" | "change_summary" | "debugging" | "generation" | "api_overview" | "retrieval_qa" | "agent_history";
6
+ export type Preset = "conservative" | "balanced" | "aggressive";
7
+ export type OutputEncoding = "json" | "toon";
8
+ export type InputFormat = "auto" | "openai" | "anthropic" | "json" | "text" | "command" | "diff";
9
+ export type DecodeFormat = "auto" | "json" | "toon" | "text";
10
+ export interface PruningPolicy {
11
+ keepRatio?: number;
12
+ preservePaths?: readonly string[];
13
+ retrievalStore?: string;
14
+ retrievalNamespace?: string;
15
+ }
8
16
  export interface CompressionOptions {
9
- format?: "auto" | "openai" | "anthropic" | "json" | "text" | "command" | "diff";
10
- mode?: CompressionMode;
17
+ format?: InputFormat;
18
+ preset?: Preset;
11
19
  targetTokens?: number;
12
- disable?: readonly string[];
13
- taskScope?: TaskScope;
14
- experimental?: boolean;
15
- storeOriginals?: boolean;
16
- retrieveNamespace?: string;
20
+ requireTarget?: boolean;
21
+ encoding?: OutputEncoding;
22
+ pruning?: PruningPolicy;
17
23
  configPath?: string;
18
24
  signal?: AbortSignal;
19
25
  }
@@ -22,19 +28,6 @@ export interface EstimatorInfo {
22
28
  model: string | null;
23
29
  is_exact: boolean;
24
30
  }
25
- export interface BudgetReport {
26
- target_tokens: number | null;
27
- protected_floor: number;
28
- achieved_tokens: number;
29
- }
30
- export interface QualityReport {
31
- eval_profile_id: string;
32
- task_scope: string;
33
- validated_ratio_band: string | null;
34
- quality_retention: number;
35
- contrastive_failure_rate: number;
36
- gate_passed: boolean;
37
- }
38
31
  export interface Warning {
39
32
  code: string;
40
33
  severity: "info" | "warn" | "critical";
@@ -53,12 +46,39 @@ export interface TransformReport {
53
46
  skipped_reason: string | null;
54
47
  warnings: readonly Warning[];
55
48
  }
56
- export interface CacheReport {
57
- boundary_kind: string | null;
58
- protected_bytes: number;
59
- prefix_byte_identical: boolean;
49
+ export interface BudgetReport {
50
+ status: "not_requested" | "met" | "best_effort" | "unreachable";
51
+ target_tokens: number | null;
52
+ protected_floor: number;
53
+ achieved_tokens: number;
54
+ }
55
+ export interface EncodingReport {
56
+ codec: string;
57
+ version: string;
58
+ roundtrip_verified: boolean;
59
+ tokens_before: number;
60
+ tokens_after: number;
61
+ token_delta: number;
60
62
  warnings: readonly Warning[];
61
63
  }
64
+ export interface PruningReport {
65
+ requested: boolean;
66
+ applied: boolean;
67
+ preview: boolean;
68
+ candidate_items: number;
69
+ retained_items: number;
70
+ pruned_items: number;
71
+ evidence_refs: number;
72
+ preserve_paths: readonly string[];
73
+ }
74
+ export interface QualityReport {
75
+ eval_profile_id: string;
76
+ task_scope: string;
77
+ validated_ratio_band: string | null;
78
+ quality_retention: number | null;
79
+ contrastive_failure_rate: number | null;
80
+ gate_passed: boolean;
81
+ }
62
82
  export interface RetrievalReport {
63
83
  store_namespace: string;
64
84
  hash_algorithm: string;
@@ -67,64 +87,81 @@ export interface RetrievalReport {
67
87
  persisted_original_bytes: number;
68
88
  skipped_original_bytes: number;
69
89
  }
70
- export interface OutputSavingsReport {
71
- profile: string;
72
- estimated_output_tokens_saved: number | null;
73
- measured_output_tokens_saved: number | null;
74
- provenance: string;
75
- }
76
- export interface BypassReport {
77
- reason: string;
78
- source: string;
79
- }
80
- export interface CommandReport {
81
- command_family: string | null;
82
- child_exit_code: number | null;
83
- duration_ms: number;
84
- raw_output_bytes: number;
85
- stdout_bytes: number;
86
- stderr_bytes: number;
87
- stderr_mode: string;
88
- stderr_truncated: boolean;
89
- compressed_output_bytes: number;
90
- filter_pack_id: string | null;
91
- filter_version: string | null;
92
- never_worse_applied: boolean;
90
+ export interface PipelineStageReport {
91
+ id: string;
92
+ version: string | null;
93
+ input_bytes: number | null;
94
+ output_bytes: number | null;
95
+ saved_bytes: number | null;
96
+ input_tokens: number | null;
97
+ output_tokens: number | null;
98
+ saved_tokens: number | null;
99
+ estimator: EstimatorInfo | null;
100
+ status: string;
101
+ duration_ms: number | null;
93
102
  bypass_reason: string | null;
103
+ provenance: string;
104
+ recoverability: string;
105
+ evidence_ref: string | null;
94
106
  }
95
- export interface LedgerReport {
96
- recorded: boolean;
97
- scope: string | null;
98
- project_hash: string | null;
99
- record_id: string | null;
107
+ export interface PipelineReport {
108
+ raw_input_bytes: number | null;
109
+ raw_input_tokens: number | null;
110
+ final_output_bytes: number;
111
+ final_output_tokens: number;
112
+ total_saved_tokens: number | null;
113
+ raw_capture: string;
114
+ upstream_recoverability: string;
115
+ stages: readonly PipelineStageReport[];
100
116
  }
101
- export interface CompressionReport {
117
+ export interface CompressionReceipt {
102
118
  schema_version: string;
119
+ status: "compressed" | "passthrough";
103
120
  original_tokens: number;
104
121
  compressed_tokens: number;
105
122
  saved_tokens: number;
106
123
  savings_ratio: number;
107
124
  savings_pct: number;
108
125
  estimator: EstimatorInfo;
109
- status: "compressed" | "passthrough" | "best_effort" | "unreachable_target";
110
- mode: string;
126
+ preset: Preset;
111
127
  format: string;
128
+ output_encoding: string;
112
129
  task_scope: string;
113
130
  request_id: string | null;
131
+ pipeline: PipelineReport | null;
114
132
  quality: QualityReport | null;
115
133
  budget: BudgetReport | null;
116
- cache: CacheReport | null;
134
+ encoding: EncodingReport | null;
135
+ pruning: PruningReport | null;
117
136
  retrieval: RetrievalReport | null;
118
- output_savings: OutputSavingsReport | null;
119
- bypass: BypassReport | null;
120
- command: CommandReport | null;
121
- ledger: LedgerReport | null;
122
137
  transforms: readonly TransformReport[];
123
138
  warnings: readonly Warning[];
139
+ cache: unknown;
140
+ output_savings: unknown;
141
+ bypass: unknown;
142
+ command: unknown;
143
+ ledger: unknown;
124
144
  }
145
+ export type CompressionReport = CompressionReceipt;
125
146
  export interface CompressionResult {
126
147
  payload: Uint8Array;
127
- report: CompressionReport;
148
+ readonly text: string;
149
+ report: CompressionReceipt;
150
+ }
151
+ export declare class BudgetUnmetError extends Error {
152
+ readonly receipt: CompressionReceipt;
153
+ constructor(receipt: CompressionReceipt);
128
154
  }
129
155
  export declare function compress(input: Input, options?: CompressionOptions): Promise<CompressionResult>;
130
- export declare function inspect(input: Input, options?: CompressionOptions): Promise<CompressionResult>;
156
+ export declare function inspect(input: Input, options?: CompressionOptions): Promise<CompressionReceipt>;
157
+ export declare function decode(input: Input, options?: {
158
+ from?: DecodeFormat;
159
+ signal?: AbortSignal;
160
+ }): Promise<Uint8Array>;
161
+ export interface RetrieveOptions {
162
+ retrievalStore?: string;
163
+ namespace?: string;
164
+ configPath?: string;
165
+ signal?: AbortSignal;
166
+ }
167
+ export declare function retrieve(reference: string | Record<string, unknown>, options?: RetrieveOptions): Promise<Uint8Array>;
package/dist/index.js CHANGED
@@ -2,68 +2,98 @@ import { binaryPath } from "./binary.js";
2
2
  import { TokenFoldProcessError } from "./errors.js";
3
3
  import { run } from "./process.js";
4
4
  export { binaryPath, run, TokenFoldProcessError };
5
+ export class BudgetUnmetError extends Error {
6
+ receipt;
7
+ constructor(receipt) { super(`token budget unmet: achieved ${receipt.compressed_tokens} tokens`); this.name = "BudgetUnmetError"; this.receipt = receipt; }
8
+ }
5
9
  function argumentsFor(command, options) {
6
- const args = [command, "--json"];
10
+ const args = [command, "--receipt-format", "json"];
7
11
  if (options.format)
8
12
  args.push("--format", options.format);
9
- if (options.mode)
10
- args.push("--mode", options.mode);
13
+ if (options.preset)
14
+ args.push("--preset", options.preset);
11
15
  if (options.targetTokens !== undefined)
12
16
  args.push("--target-tokens", String(options.targetTokens));
13
- if (options.disable?.length && command === "compress")
14
- args.push("--disable", options.disable.join(","));
15
- if (options.taskScope)
16
- args.push("--task-scope", options.taskScope);
17
- if (options.experimental)
18
- args.push("--experimental");
19
- if (options.storeOriginals && command === "compress")
20
- args.push("--store-originals");
21
- if (options.retrieveNamespace && command === "compress") {
22
- args.push("--retrieve-namespace", options.retrieveNamespace);
17
+ if (options.requireTarget)
18
+ args.push("--require-target");
19
+ if (options.encoding)
20
+ args.push("--encoding", options.encoding);
21
+ if (options.pruning) {
22
+ args.push("--prune");
23
+ if (options.pruning.keepRatio !== undefined)
24
+ args.push("--keep-ratio", String(options.pruning.keepRatio));
25
+ for (const path of options.pruning.preservePaths ?? [])
26
+ args.push("--preserve", path);
27
+ if (options.pruning.retrievalStore)
28
+ args.push("--retrieval-store", options.pruning.retrievalStore);
29
+ if (options.pruning.retrievalNamespace)
30
+ args.push("--retrieval-namespace", options.pruning.retrievalNamespace);
23
31
  }
24
32
  if (options.configPath)
25
33
  args.push("--config", options.configPath);
26
34
  return args;
27
35
  }
28
- function parseReport(bytes, result) {
36
+ function parseReceipt(bytes, result) {
37
+ const text = Buffer.from(bytes).toString("utf8");
38
+ const json = text.split("\ntokenfold:", 1)[0] ?? "";
29
39
  try {
30
- return JSON.parse(Buffer.from(bytes).toString("utf8"));
40
+ return JSON.parse(json);
31
41
  }
32
42
  catch (cause) {
33
- throw new TokenFoldProcessError("tokenfold returned an invalid JSON report", {
34
- code: "invalid_report",
35
- exitCode: result.exitCode,
36
- signal: result.signal,
37
- stderr: result.stderr,
38
- cause,
39
- });
43
+ throw new TokenFoldProcessError("tokenfold returned an invalid JSON receipt", { code: "invalid_report", exitCode: result.exitCode, signal: result.signal, stderr: result.stderr, cause });
40
44
  }
41
45
  }
42
- async function execute(command, input, options) {
43
- const runOptions = {
44
- stdin: input,
45
- env: { TOKENFOLD_ANALYTICS_ENABLED: "false" },
46
- };
47
- if (options.signal)
48
- runOptions.signal = options.signal;
49
- const result = await run(argumentsFor(command, options), runOptions);
50
- if (result.exitCode !== 0) {
51
- throw new TokenFoldProcessError(`tokenfold exited with status ${result.exitCode ?? result.signal}`, {
52
- code: "tokenfold_exit",
53
- exitCode: result.exitCode,
54
- signal: result.signal,
55
- stderr: result.stderr,
56
- });
57
- }
58
- const reportBytes = command === "compress" ? result.stderr : result.stdout;
59
- return {
60
- payload: command === "compress" ? result.stdout : Uint8Array.from(Buffer.from(input)),
61
- report: parseReport(reportBytes, result),
62
- };
46
+ function optionsFor(input, signal) {
47
+ const options = { env: { TOKENFOLD_ANALYTICS_ENABLED: "false" } };
48
+ if (input !== undefined)
49
+ options.stdin = input;
50
+ if (signal)
51
+ options.signal = signal;
52
+ return options;
53
+ }
54
+ function withText(payload, report) {
55
+ return { payload, report, get text() { return new TextDecoder("utf-8", { fatal: true }).decode(payload); } };
56
+ }
57
+ export async function compress(input, options = {}) {
58
+ const result = await run(argumentsFor("compress", options), optionsFor(input, options.signal));
59
+ if (result.exitCode !== 0 && result.exitCode !== 7)
60
+ throwProcess(result);
61
+ const receipt = parseReceipt(result.stderr, result);
62
+ if (result.exitCode === 7)
63
+ throw new BudgetUnmetError(receipt);
64
+ return withText(result.stdout, receipt);
63
65
  }
64
- export function compress(input, options = {}) {
65
- return execute("compress", input, options);
66
+ export async function inspect(input, options = {}) {
67
+ const result = await run(argumentsFor("inspect", options), optionsFor(input, options.signal));
68
+ if (result.exitCode !== 0 && result.exitCode !== 7)
69
+ throwProcess(result);
70
+ const receipt = parseReceipt(result.stdout, result);
71
+ if (result.exitCode === 7)
72
+ throw new BudgetUnmetError(receipt);
73
+ return receipt;
74
+ }
75
+ export async function decode(input, options = {}) {
76
+ const args = ["decode"];
77
+ if (options.from)
78
+ args.push("--from", options.from);
79
+ const result = await run(args, optionsFor(input, options.signal));
80
+ if (result.exitCode !== 0)
81
+ throwProcess(result);
82
+ return result.stdout;
83
+ }
84
+ export async function retrieve(reference, options = {}) {
85
+ const args = ["retrieve", typeof reference === "string" ? reference : JSON.stringify(reference)];
86
+ if (options.retrievalStore)
87
+ args.push("--retrieval-store", options.retrievalStore);
88
+ if (options.namespace)
89
+ args.push("--retrieval-namespace", options.namespace);
90
+ if (options.configPath)
91
+ args.push("--config", options.configPath);
92
+ const result = await run(args, optionsFor(undefined, options.signal));
93
+ if (result.exitCode !== 0)
94
+ throwProcess(result);
95
+ return result.stdout;
66
96
  }
67
- export function inspect(input, options = {}) {
68
- return execute("inspect", input, options);
97
+ function throwProcess(result) {
98
+ throw new TokenFoldProcessError(`tokenfold exited with status ${result.exitCode ?? result.signal}`, { code: "tokenfold_exit", exitCode: result.exitCode, signal: result.signal, stderr: result.stderr });
69
99
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokenfold",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Token-aware compression for LLM payloads, backed by the tokenfold Rust CLI.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -49,11 +49,11 @@
49
49
  "prepack": "npm run build"
50
50
  },
51
51
  "optionalDependencies": {
52
- "@tokenfold/cli-darwin-x64": "0.4.0",
53
- "@tokenfold/cli-darwin-arm64": "0.4.0",
54
- "@tokenfold/cli-linux-x64": "0.4.0",
55
- "@tokenfold/cli-linux-arm64": "0.4.0",
56
- "@tokenfold/cli-win32-x64": "0.4.0"
52
+ "@tokenfold/cli-darwin-x64": "0.5.0",
53
+ "@tokenfold/cli-darwin-arm64": "0.5.0",
54
+ "@tokenfold/cli-linux-x64": "0.5.0",
55
+ "@tokenfold/cli-linux-arm64": "0.5.0",
56
+ "@tokenfold/cli-win32-x64": "0.5.0"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@types/node": "^24.0.0",