@tokz/ai-sdk 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +86 -12
- package/dist/index.js +123 -44
- package/dist/index.js.map +1 -1
- package/package.json +17 -12
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { TokzErrorInfo, CircuitBreaker } from '@tokz/sdk';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* The wire contract. Every SDK, API response, and customer integration binds to
|
|
3
5
|
* these shapes. Versioned; changes must be additive.
|
|
@@ -113,6 +115,46 @@ type ExpandOpts = {
|
|
|
113
115
|
*/
|
|
114
116
|
declare function expand(source: string | Uint8Array, spanMap: SpanMap, opts?: ExpandOpts): string;
|
|
115
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Where compression comes from.
|
|
120
|
+
*
|
|
121
|
+
* This package never bundles the engine. `@tokz/engine` is private and lives in
|
|
122
|
+
* the hosted API, so the only compression a customer's process can reach is the
|
|
123
|
+
* one over HTTP — and the SDK is what talks to it. Injecting the function rather
|
|
124
|
+
* than importing an engine also lets tests (and, later, a local CLI build) supply
|
|
125
|
+
* their own without this file knowing which.
|
|
126
|
+
*
|
|
127
|
+
* The rendered text comes back alongside the result because the SDK already
|
|
128
|
+
* assembled and hash-verified it; recomputing it here would repeat that work.
|
|
129
|
+
*/
|
|
130
|
+
type CompressedView = {
|
|
131
|
+
result: CompressionResult;
|
|
132
|
+
text: string;
|
|
133
|
+
};
|
|
134
|
+
type Compressor = (text: string, opts: CompressOpts) => Promise<CompressedView>;
|
|
135
|
+
type ApiCompressorOptions = {
|
|
136
|
+
apiKey?: string;
|
|
137
|
+
/** Defaults to https://api.tokz.dev. Point at http://localhost:PORT in dev. */
|
|
138
|
+
baseUrl?: string;
|
|
139
|
+
fetch?: typeof fetch;
|
|
140
|
+
/** Per-attempt timeout in ms. Default 5000 (enforced by @tokz/sdk). */
|
|
141
|
+
timeout?: number;
|
|
142
|
+
/** Retries after the first attempt, on retryable failures. Default 2. */
|
|
143
|
+
retries?: number;
|
|
144
|
+
/** Base backoff in ms for retries. Default 500. */
|
|
145
|
+
retryDelay?: number;
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Compress against the hosted API.
|
|
149
|
+
*
|
|
150
|
+
* The client is constructed on first use, not here: `generateText` resolves a
|
|
151
|
+
* compressor on every call, and a caller who injects their own must not need a
|
|
152
|
+
* key to exist for a client they will never touch.
|
|
153
|
+
*/
|
|
154
|
+
declare function apiCompressor(options?: ApiCompressorOptions): Compressor;
|
|
155
|
+
/** What the model should see for a result, whichever path produced it. */
|
|
156
|
+
declare function renderResult(result: CompressionResult, source: string): string;
|
|
157
|
+
|
|
116
158
|
/**
|
|
117
159
|
* The Vercel AI SDK surface we bind to, restated structurally.
|
|
118
160
|
*
|
|
@@ -194,6 +236,14 @@ type GenerateTextResultLike = {
|
|
|
194
236
|
};
|
|
195
237
|
type AutoExpandMode = "on-reference" | "never";
|
|
196
238
|
type TokzOptions = {
|
|
239
|
+
/** Key for the hosted compression API. Falls back to `TOKZ_API_KEY`. Not needed
|
|
240
|
+
* when `compressor` is supplied. */
|
|
241
|
+
apiKey?: string;
|
|
242
|
+
/** API base URL. Falls back to `TOKZ_BASE_URL`, then https://api.tokz.dev. */
|
|
243
|
+
baseUrl?: string;
|
|
244
|
+
/** Override where compression comes from. Injected in tests and by a future
|
|
245
|
+
* local build; otherwise an HTTP compressor over `@tokz/sdk` is used. */
|
|
246
|
+
compressor?: Compressor;
|
|
197
247
|
/** Share of source bytes to keep. Defaults to `DEFAULT_TARGET_RATIO` (0.45). */
|
|
198
248
|
targetRatio?: number;
|
|
199
249
|
/** Expand elisions the model's answer refers to, and re-prompt once. Default `never`. */
|
|
@@ -207,6 +257,19 @@ type TokzOptions = {
|
|
|
207
257
|
store?: RetentionStore;
|
|
208
258
|
/** Underlying `ai.generateText`. Injected in tests; otherwise imported from `ai`. */
|
|
209
259
|
delegate?: GenerateTextFn;
|
|
260
|
+
/** Per-attempt timeout in ms handed to the tokz client. Default 5000. */
|
|
261
|
+
timeout?: number;
|
|
262
|
+
/** Retries after the first attempt, on retryable failures. Default 2. */
|
|
263
|
+
retries?: number;
|
|
264
|
+
/** Base backoff in ms for retries. Default 500. */
|
|
265
|
+
retryDelay?: number;
|
|
266
|
+
/** Consecutive failures that open the circuit breaker. Default 5. */
|
|
267
|
+
failureThreshold?: number;
|
|
268
|
+
/** Ms the breaker stays open before a trial. Default 30000. */
|
|
269
|
+
resetTimeout?: number;
|
|
270
|
+
/** Fired when a compress call fails and the tool result falls back to uncompressed
|
|
271
|
+
* text. Without it, the failure is swallowed. */
|
|
272
|
+
onError?: (info: TokzErrorInfo) => void;
|
|
210
273
|
};
|
|
211
274
|
/**
|
|
212
275
|
* The original text plus its map, held next to the compressed render. This pair is
|
|
@@ -284,23 +347,38 @@ type TokzGenerateTextResult = GenerateTextResultLike & {
|
|
|
284
347
|
*/
|
|
285
348
|
declare function generateText(options: TokzGenerateTextOptions): Promise<TokzGenerateTextResult>;
|
|
286
349
|
|
|
350
|
+
type CompactContext = {
|
|
351
|
+
compress: Compressor;
|
|
352
|
+
store?: RetentionStore;
|
|
353
|
+
/** Shared with tool-result compression so one dead API opens the same circuit. */
|
|
354
|
+
breaker?: CircuitBreaker;
|
|
355
|
+
onError?: (info: TokzErrorInfo) => void;
|
|
356
|
+
};
|
|
287
357
|
/**
|
|
288
358
|
* Re-compress retained tool results at a tighter budget.
|
|
289
359
|
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
360
|
+
* The store holds each original next to its map, so a smaller view of an old tool
|
|
361
|
+
* result costs no tool re-execution and no model inference — it is the same
|
|
362
|
+
* deterministic pass that produced the first view, re-run at a lower ratio. That
|
|
363
|
+
* is the property a wrapper which discarded the original cannot have: it would
|
|
364
|
+
* have to ask a model to summarise history, which costs more than it saves.
|
|
365
|
+
*
|
|
366
|
+
* It is not free of *our* API, though: compression runs server-side, so this is
|
|
367
|
+
* one `/compress` call per retained result that actually appears in `messages`.
|
|
368
|
+
* Cheap and deterministic, but not zero — do not describe it as offline.
|
|
296
369
|
*
|
|
297
370
|
* Input messages are not mutated; changed messages are returned as new objects.
|
|
298
371
|
*/
|
|
299
|
-
declare function compactHistory(messages: readonly ModelMessageLike[], targetRatio: number,
|
|
372
|
+
declare function compactHistory(messages: readonly ModelMessageLike[], targetRatio: number, ctx: CompactContext): Promise<ModelMessageLike[]>;
|
|
300
373
|
|
|
301
374
|
type WrapContext = {
|
|
302
375
|
store: RetentionStore;
|
|
303
376
|
targetRatio: number;
|
|
377
|
+
compress: Compressor;
|
|
378
|
+
/** Shared across every tool call from one generateText wrapper. Omitted only by
|
|
379
|
+
* callers that build a context by hand; a fresh, always-closed breaker is used. */
|
|
380
|
+
breaker?: CircuitBreaker;
|
|
381
|
+
onError?: (info: TokzErrorInfo) => void;
|
|
304
382
|
};
|
|
305
383
|
/** Tool output as text the compressor can work on. Pretty-printed because the
|
|
306
384
|
* structural detector reads JSON shape, and key order follows the object, so the
|
|
@@ -335,8 +413,4 @@ declare class MapRetentionStore implements RetentionStore {
|
|
|
335
413
|
*/
|
|
336
414
|
declare const sharedStore: RetentionStore;
|
|
337
415
|
|
|
338
|
-
|
|
339
|
-
/** What the model should see for a result, whichever path produced it. */
|
|
340
|
-
declare function renderResult(result: CompressionResult, source: string): string;
|
|
341
|
-
|
|
342
|
-
export { type AutoExpandMode, type DetectOptions, type GenerateTextFn, type GenerateTextResultLike, MapRetentionStore, type ModelMessageLike, type ReferencedElision, type RetentionRecord, type RetentionStore, type TokzGenerateTextOptions, type TokzGenerateTextResult, type TokzOptions, type TokzReport, type ToolLike, type ToolResultOutput, type ToolResultPart, type ToolSetLike, type WrapContext, compactHistory, compressText, detectReferencedElisions, expand, expansionMessage, generateText, renderResult, sharedStore, stringifyToolResult, wrapTool, wrapTools };
|
|
416
|
+
export { type ApiCompressorOptions, type AutoExpandMode, type CompactContext, type CompressedView, type Compressor, type DetectOptions, type GenerateTextFn, type GenerateTextResultLike, MapRetentionStore, type ModelMessageLike, type ReferencedElision, type RetentionRecord, type RetentionStore, type TokzGenerateTextOptions, type TokzGenerateTextResult, type TokzOptions, type TokzReport, type ToolLike, type ToolResultOutput, type ToolResultPart, type ToolSetLike, type WrapContext, apiCompressor, compactHistory, detectReferencedElisions, expand, expansionMessage, generateText, renderResult, sharedStore, stringifyToolResult, wrapTool, wrapTools };
|
package/dist/index.js
CHANGED
|
@@ -48,24 +48,49 @@ var DEFAULT_TARGET_RATIO = 0.45;
|
|
|
48
48
|
var MAX_PAYLOAD_BYTES = 1024 * 1024;
|
|
49
49
|
var LATENCY_BUDGET_PAYLOAD_BYTES = 64 * 1024;
|
|
50
50
|
|
|
51
|
-
// src/
|
|
52
|
-
import
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
// src/generate.ts
|
|
52
|
+
import { CircuitBreaker as CircuitBreaker3 } from "@tokz/sdk";
|
|
53
|
+
|
|
54
|
+
// src/compressor.ts
|
|
55
|
+
import { Tokz } from "@tokz/sdk";
|
|
56
|
+
function apiCompressor(options = {}) {
|
|
57
|
+
let client;
|
|
58
|
+
return async (text, opts) => {
|
|
59
|
+
if (!client) {
|
|
60
|
+
const apiKey = options.apiKey ?? readEnv("TOKZ_API_KEY");
|
|
61
|
+
if (!apiKey) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
"tokz: no API key. Pass `tokz: { apiKey }`, set TOKZ_API_KEY, or inject `tokz: { compressor }`."
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
const init = { apiKey };
|
|
67
|
+
const baseUrl = options.baseUrl ?? readEnv("TOKZ_BASE_URL");
|
|
68
|
+
if (baseUrl) init.baseUrl = baseUrl;
|
|
69
|
+
if (options.fetch) init.fetch = options.fetch;
|
|
70
|
+
client = new Tokz(init);
|
|
71
|
+
}
|
|
72
|
+
const withTransport = {
|
|
73
|
+
...opts,
|
|
74
|
+
...options.timeout !== void 0 ? { timeout: options.timeout } : {},
|
|
75
|
+
...options.retries !== void 0 ? { retries: options.retries } : {},
|
|
76
|
+
...options.retryDelay !== void 0 ? { retryDelay: options.retryDelay } : {}
|
|
77
|
+
};
|
|
78
|
+
const { text: rendered, ...rest } = await client.compress(text, withTransport);
|
|
79
|
+
return { result: rest, text: rendered };
|
|
80
|
+
};
|
|
56
81
|
}
|
|
57
82
|
function renderResult(result, source) {
|
|
58
|
-
if (typeof engine.render === "function") return engine.render(result, source);
|
|
59
83
|
if (result.method === "semantic") return result.compressedText;
|
|
60
84
|
return assemble(toBytes(source), result.spanMap.spans);
|
|
61
85
|
}
|
|
62
|
-
function
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const untouched = spanMap.elisions.length === 0 && spanMap.stats.keptBytes === spanMap.srcBytes;
|
|
66
|
-
return untouched ? { method: "passthrough", spanMap } : { method: "structural", spanMap };
|
|
86
|
+
function readEnv(name) {
|
|
87
|
+
const env = globalThis.process?.env;
|
|
88
|
+
return env?.[name];
|
|
67
89
|
}
|
|
68
90
|
|
|
91
|
+
// src/compact.ts
|
|
92
|
+
import { emitError as emitError2 } from "@tokz/sdk";
|
|
93
|
+
|
|
69
94
|
// src/store.ts
|
|
70
95
|
var MapRetentionStore = class {
|
|
71
96
|
#byId = /* @__PURE__ */ new Map();
|
|
@@ -85,6 +110,7 @@ var MapRetentionStore = class {
|
|
|
85
110
|
var sharedStore = new MapRetentionStore();
|
|
86
111
|
|
|
87
112
|
// src/wrap.ts
|
|
113
|
+
import { emitError } from "@tokz/sdk";
|
|
88
114
|
function stringifyToolResult(value) {
|
|
89
115
|
if (typeof value === "string") return value;
|
|
90
116
|
return JSON.stringify(value ?? null, null, 2);
|
|
@@ -98,19 +124,29 @@ function wrapTool(toolName, tool, ctx) {
|
|
|
98
124
|
const raw = await inner.call(tool, input, options);
|
|
99
125
|
if (isAsyncIterable(raw)) return raw;
|
|
100
126
|
const originalText = stringifyToolResult(raw);
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
127
|
+
const breaker = ctx.breaker;
|
|
128
|
+
if (breaker?.shouldSkip()) return originalText;
|
|
129
|
+
try {
|
|
130
|
+
const { result, text: rendered } = await ctx.compress(originalText, {
|
|
131
|
+
targetRatio: ctx.targetRatio
|
|
132
|
+
});
|
|
133
|
+
breaker?.recordSuccess();
|
|
134
|
+
const toolCallId = options.toolCallId ?? contentAddressedId(toolName, originalText);
|
|
135
|
+
const record = {
|
|
136
|
+
toolName,
|
|
137
|
+
toolCallId,
|
|
138
|
+
originalText,
|
|
139
|
+
result,
|
|
140
|
+
rendered,
|
|
141
|
+
targetRatio: ctx.targetRatio
|
|
142
|
+
};
|
|
143
|
+
ctx.store.set(record);
|
|
144
|
+
return rendered;
|
|
145
|
+
} catch (err) {
|
|
146
|
+
breaker?.recordFailure();
|
|
147
|
+
emitError(ctx.onError, err, "tokz compression failed, falling back to uncompressed");
|
|
148
|
+
return originalText;
|
|
149
|
+
}
|
|
114
150
|
},
|
|
115
151
|
toModelOutput: ({ toolCallId, output }) => {
|
|
116
152
|
const record = ctx.store.get(toolCallId);
|
|
@@ -138,23 +174,39 @@ function contentAddressedId(toolName, originalText) {
|
|
|
138
174
|
}
|
|
139
175
|
|
|
140
176
|
// src/compact.ts
|
|
141
|
-
function compactHistory(messages, targetRatio,
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
177
|
+
async function compactHistory(messages, targetRatio, ctx) {
|
|
178
|
+
const store = ctx.store ?? sharedStore;
|
|
179
|
+
return Promise.all(
|
|
180
|
+
messages.map(async (message) => {
|
|
181
|
+
if (!Array.isArray(message.content)) return message;
|
|
182
|
+
let changed = false;
|
|
183
|
+
const content = await Promise.all(
|
|
184
|
+
message.content.map(async (part) => {
|
|
185
|
+
if (!isToolResultPart(part)) return part;
|
|
186
|
+
const record = store.get(part.toolCallId);
|
|
187
|
+
if (!record) return part;
|
|
188
|
+
const breaker = ctx.breaker;
|
|
189
|
+
if (breaker?.shouldSkip()) return part;
|
|
190
|
+
let tighter, rendered;
|
|
191
|
+
try {
|
|
192
|
+
({ result: tighter, text: rendered } = await ctx.compress(record.originalText, {
|
|
193
|
+
targetRatio
|
|
194
|
+
}));
|
|
195
|
+
breaker?.recordSuccess();
|
|
196
|
+
} catch (err) {
|
|
197
|
+
breaker?.recordFailure();
|
|
198
|
+
emitError2(ctx.onError, err, "tokz compaction failed, keeping the current render");
|
|
199
|
+
return part;
|
|
200
|
+
}
|
|
201
|
+
if (rendered === currentValue(part)) return part;
|
|
202
|
+
store.set({ ...record, result: tighter, rendered, targetRatio });
|
|
203
|
+
changed = true;
|
|
204
|
+
return part.output !== void 0 ? { ...part, output: { type: "text", value: rendered } } : { ...part, result: rendered };
|
|
205
|
+
})
|
|
206
|
+
);
|
|
207
|
+
return changed ? { ...message, content } : message;
|
|
208
|
+
})
|
|
209
|
+
);
|
|
158
210
|
}
|
|
159
211
|
function currentValue(part) {
|
|
160
212
|
if (part.output && "value" in part.output && typeof part.output.value === "string") {
|
|
@@ -288,14 +340,31 @@ async function generateText(options) {
|
|
|
288
340
|
const { tokz = {}, ...rest } = options;
|
|
289
341
|
const store = tokz.store ?? sharedStore;
|
|
290
342
|
const targetRatio = resolveTargetRatio(tokz.targetRatio);
|
|
343
|
+
const compress = resolveCompressor(tokz);
|
|
291
344
|
const delegate = tokz.delegate ?? await loadDelegate();
|
|
345
|
+
const breaker = new CircuitBreaker3({
|
|
346
|
+
...tokz.failureThreshold !== void 0 ? { failureThreshold: tokz.failureThreshold } : {},
|
|
347
|
+
...tokz.resetTimeout !== void 0 ? { resetTimeout: tokz.resetTimeout } : {}
|
|
348
|
+
});
|
|
349
|
+
const onError = tokz.onError;
|
|
292
350
|
const forwarded = { ...rest };
|
|
293
351
|
if (isToolSet(rest["tools"])) {
|
|
294
|
-
forwarded["tools"] = wrapTools(rest["tools"], {
|
|
352
|
+
forwarded["tools"] = wrapTools(rest["tools"], {
|
|
353
|
+
store,
|
|
354
|
+
targetRatio,
|
|
355
|
+
compress,
|
|
356
|
+
breaker,
|
|
357
|
+
...onError !== void 0 ? { onError } : {}
|
|
358
|
+
});
|
|
295
359
|
}
|
|
296
360
|
if (tokz.conversationCompaction && Array.isArray(rest["messages"])) {
|
|
297
361
|
const ratio = tokz.compactionRatio ?? targetRatio * 0.6;
|
|
298
|
-
forwarded["messages"] = compactHistory(rest["messages"], ratio,
|
|
362
|
+
forwarded["messages"] = await compactHistory(rest["messages"], ratio, {
|
|
363
|
+
compress,
|
|
364
|
+
store,
|
|
365
|
+
breaker,
|
|
366
|
+
...onError !== void 0 ? { onError } : {}
|
|
367
|
+
});
|
|
299
368
|
}
|
|
300
369
|
const before = snapshotBytes(store);
|
|
301
370
|
let result = await delegate(forwarded);
|
|
@@ -326,6 +395,16 @@ async function generateText(options) {
|
|
|
326
395
|
}
|
|
327
396
|
};
|
|
328
397
|
}
|
|
398
|
+
function resolveCompressor(tokz) {
|
|
399
|
+
if (tokz.compressor) return tokz.compressor;
|
|
400
|
+
const opts = {};
|
|
401
|
+
if (tokz.apiKey !== void 0) opts.apiKey = tokz.apiKey;
|
|
402
|
+
if (tokz.baseUrl !== void 0) opts.baseUrl = tokz.baseUrl;
|
|
403
|
+
if (tokz.timeout !== void 0) opts.timeout = tokz.timeout;
|
|
404
|
+
if (tokz.retries !== void 0) opts.retries = tokz.retries;
|
|
405
|
+
if (tokz.retryDelay !== void 0) opts.retryDelay = tokz.retryDelay;
|
|
406
|
+
return apiCompressor(opts);
|
|
407
|
+
}
|
|
329
408
|
async function loadDelegate() {
|
|
330
409
|
const specifier = "ai";
|
|
331
410
|
const mod = await import(specifier);
|
|
@@ -352,8 +431,8 @@ function deltaBytes(store, before) {
|
|
|
352
431
|
}
|
|
353
432
|
export {
|
|
354
433
|
MapRetentionStore,
|
|
434
|
+
apiCompressor,
|
|
355
435
|
compactHistory,
|
|
356
|
-
compressText,
|
|
357
436
|
detectReferencedElisions,
|
|
358
437
|
expand,
|
|
359
438
|
expansionMessage,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../core/src/bytes.ts","../../core/src/expand.ts","../../core/src/constants.ts","../src/engine.ts","../src/store.ts","../src/wrap.ts","../src/compact.ts","../src/autoexpand.ts","../src/generate.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { Span, SpanMap, Elision } from \"./types.js\";\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder(\"utf-8\");\n\nexport function sha256hex(bytes: Uint8Array): string {\n return createHash(\"sha256\").update(bytes).digest(\"hex\");\n}\n\nexport function toBytes(text: string): Uint8Array {\n return enc.encode(text);\n}\n\nexport function fromBytes(bytes: Uint8Array): string {\n return dec.decode(bytes);\n}\n\nexport function byteLength(text: string): number {\n return enc.encode(text).length;\n}\n\nexport function isAscii(text: string): boolean {\n for (let i = 0; i < text.length; i++) {\n if (text.charCodeAt(i) > 127) return false;\n }\n return true;\n}\n\n/**\n * Concatenate the source bytes covered by `spans` and decode as UTF-8. This is\n * exactly the compressed text: only surviving bytes, in source order, verbatim.\n */\nexport function assemble(src: Uint8Array, spans: Span[]): string {\n let len = 0;\n for (const sp of spans) len += sp.e - sp.s;\n const out = new Uint8Array(len);\n let o = 0;\n for (const sp of spans) {\n out.set(src.subarray(sp.s, sp.e), o);\n o += sp.e - sp.s;\n }\n return dec.decode(out);\n}\n\n/** Byte range in the source that an elision dropped. */\nexport function elisionRange(spanMap: SpanMap, elision: Elision): { start: number; end: number } {\n const start = elision.after < 0 ? 0 : spanMap.spans[elision.after]!.e;\n return { start, end: start + elision.bytes };\n}\n\n/**\n * jsonc-parser and most tokenizers report offsets in UTF-16 code units; the span\n * map speaks UTF-8 bytes. Build a converter from string index to byte offset.\n *\n * For ASCII — which most tool output is — the two are the same number, so the\n * prefix table is skipped entirely rather than allocating 4 bytes per character\n * to answer `i => i`.\n */\nexport function byteMapper(text: string, ascii?: boolean): (strIdx: number) => number {\n if (ascii ?? isAscii(text)) {\n const len = text.length;\n return (strIdx: number) => Math.max(0, Math.min(strIdx, len));\n }\n\n const prefix = new Int32Array(text.length + 1);\n let bytes = 0;\n let i = 0;\n const buf = new Uint8Array(4);\n for (const cp of text) {\n const units = cp.length; // 1 or 2 UTF-16 units\n const n = enc.encodeInto(cp, buf).written ?? 0;\n // Every UTF-16 index inside a code point maps to the byte offset at its start.\n for (let u = 0; u < units; u++) {\n prefix[i] = bytes;\n i++;\n }\n bytes += n;\n }\n prefix[i] = bytes;\n return (strIdx: number) => prefix[Math.max(0, Math.min(strIdx, text.length))]!;\n}\n","import type { SpanMap, ExpandOpts } from \"./types.js\";\nimport { sha256hex, toBytes, fromBytes, elisionRange } from \"./bytes.js\";\n\n/**\n * Local, network-free reconstruction from the caller's own source plus a span\n * map. This is what lets a stateless API offer recovery at all: we hold nothing,\n * because the caller already has everything.\n *\n * - no opts -> the full original text, after verifying `srcSha256`.\n * - {elision: i} -> just the bytes elision `i` dropped, recovered verbatim.\n *\n * Throws if the map was not built from this source, rather than returning\n * plausible-looking misaligned output.\n */\nexport function expand(source: string | Uint8Array, spanMap: SpanMap, opts: ExpandOpts = {}): string {\n const src = typeof source === \"string\" ? toBytes(source) : source;\n if (sha256hex(src) !== spanMap.srcSha256) {\n throw new Error(\n \"tokz: srcSha256 mismatch — this span map was not built from the given source (was the text edited?)\",\n );\n }\n if (opts.elision !== undefined) {\n const el = spanMap.elisions[opts.elision];\n if (!el) throw new Error(`tokz: no elision at index ${opts.elision}`);\n const { start, end } = elisionRange(spanMap, el);\n return fromBytes(src.subarray(start, end));\n }\n return fromBytes(src);\n}\n","/** Default share of source bytes to keep when the caller does not say. */\nexport const DEFAULT_TARGET_RATIO = 0.45;\n\n/** Max request body the hosted API accepts. Compression cost is linear in input\n * size, so this is where predictable latency stops, not an arbitrary number. */\nexport const MAX_PAYLOAD_BYTES = 1024 * 1024;\n\n/**\n * Nesting past this is declined rather than compressed. Parsers and the\n * selection walk both recurse per level, so a deeply nested document overflows\n * the stack — and `\"[\".repeat(6000)` reaches that in ~12 KB, well inside any\n * request body limit. Declining costs the caller nothing; throwing would hand a\n * remote caller a way to kill a request slot.\n */\nexport const MAX_NESTING_DEPTH = 200;\n\n/** Latency budgets from the design doc, used by the eval kill gates. */\nexport const STRUCTURAL_BUDGET_MS = 10;\nexport const SEMANTIC_BUDGET_MS = 30;\n\n/** Payload size the structural latency budget is quoted against. Cost is linear\n * above this, so a latency number without a size attached means little. */\nexport const LATENCY_BUDGET_PAYLOAD_BYTES = 64 * 1024;\n\n/** API key format: `tokz_live_<id8>_<secret>`. The id is public and indexed; only\n * a SHA-256 of the secret is stored. */\nexport const KEY_PREFIX = \"tokz_live_\";\n","import * as engineModule from \"@tokz/engine\";\nimport { assemble, toBytes, type CompressionResult, type CompressOpts, type SpanMap } from \"@tokz/core\";\n\n/**\n * `@tokz/engine` is mid-migration from `compress() -> { text, spanMap }` to the\n * unified `CompressionResult` union, and `render` lands with it. Binding through a\n * declared surface with one cast keeps this package compiling and running against\n * either shape instead of pinning it to whichever version happens to be built in\n * `dist/` at the moment.\n */\ntype EngineSurface = {\n compress: (text: string, opts?: CompressOpts) => CompressionResult | LegacyCompressResult;\n render?: (result: CompressionResult, source: string) => string;\n};\n\ntype LegacyCompressResult = { text: string; spanMap: SpanMap };\n\nconst engine = engineModule as unknown as EngineSurface;\n\nexport function compressText(text: string, opts: CompressOpts): CompressionResult {\n return normalize(engine.compress(text, opts));\n}\n\n/** What the model should see for a result, whichever path produced it. */\nexport function renderResult(result: CompressionResult, source: string): string {\n if (typeof engine.render === \"function\") return engine.render(result, source);\n if (result.method === \"semantic\") return result.compressedText;\n return assemble(toBytes(source), result.spanMap.spans);\n}\n\nfunction normalize(raw: CompressionResult | LegacyCompressResult): CompressionResult {\n if (\"method\" in raw) return raw;\n const { spanMap } = raw;\n // A map that kept everything and dropped nothing is the honest passthrough case,\n // which the union spells out and the legacy shape did not.\n const untouched = spanMap.elisions.length === 0 && spanMap.stats.keptBytes === spanMap.srcBytes;\n return untouched ? { method: \"passthrough\", spanMap } : { method: \"structural\", spanMap };\n}\n","import type { RetentionRecord, RetentionStore } from \"./types.js\";\n\n/** Insertion-ordered so `records()` reads back in call order — history compaction\n * and any future oldest-first budget both depend on that order being stable. */\nexport class MapRetentionStore implements RetentionStore {\n readonly #byId = new Map<string, RetentionRecord>();\n\n get(toolCallId: string): RetentionRecord | undefined {\n return this.#byId.get(toolCallId);\n }\n\n set(record: RetentionRecord): void {\n this.#byId.set(record.toolCallId, record);\n }\n\n records(): RetentionRecord[] {\n return [...this.#byId.values()];\n }\n\n clear(): void {\n this.#byId.clear();\n }\n}\n\n/**\n * Process-wide default so `compactHistory(messages, ratio)` matches the spec'd\n * two-argument signature and still finds the originals a previous `generateText`\n * retained. Pass an explicit store per conversation when one process serves many.\n */\nexport const sharedStore: RetentionStore = new MapRetentionStore();\n","import { DEFAULT_TARGET_RATIO, sha256hex, toBytes } from \"@tokz/core\";\nimport { compressText, renderResult } from \"./engine.js\";\nimport type {\n RetentionRecord,\n RetentionStore,\n ToolExecuteOptions,\n ToolLike,\n ToolSetLike,\n} from \"./types.js\";\n\nexport type WrapContext = {\n store: RetentionStore;\n targetRatio: number;\n};\n\n/** Tool output as text the compressor can work on. Pretty-printed because the\n * structural detector reads JSON shape, and key order follows the object, so the\n * same result always serialises to the same bytes. */\nexport function stringifyToolResult(value: unknown): string {\n if (typeof value === \"string\") return value;\n return JSON.stringify(value ?? null, null, 2);\n}\n\n/**\n * Wrap one tool so the model receives the compressed render while the original text\n * and its span map stay in the store.\n *\n * `execute` returns the render rather than the raw value, and `toModelOutput` maps\n * to the same string. Doing both is deliberate: `toModelOutput` is the documented\n * tool-to-model hook, but returning the render from `execute` means the original\n * cannot reach a message even under an SDK version that ignores the hook. The\n * original is one `store.get(toolCallId)` away for the caller.\n */\nexport function wrapTool(toolName: string, tool: ToolLike, ctx: WrapContext): ToolLike {\n const inner = tool.execute;\n // Client-side and provider-executed tools have no server-side execute to intercept.\n if (typeof inner !== \"function\") return tool;\n\n const wrapped: ToolLike = {\n ...tool,\n execute: async (input: never, options: ToolExecuteOptions): Promise<unknown> => {\n const raw = await inner.call(tool, input, options);\n // A tool may return an AsyncIterable to stream its output. Compressing that\n // would mean buffering it, which changes the tool's semantics, so it passes\n // through uncompressed rather than silently becoming \"{}\".\n if (isAsyncIterable(raw)) return raw;\n const originalText = stringifyToolResult(raw);\n const result = compressText(originalText, { targetRatio: ctx.targetRatio });\n const rendered = renderResult(result, originalText);\n const toolCallId = options.toolCallId ?? contentAddressedId(toolName, originalText);\n const record: RetentionRecord = {\n toolName,\n toolCallId,\n originalText,\n result,\n rendered,\n targetRatio: ctx.targetRatio,\n };\n ctx.store.set(record);\n return rendered;\n },\n toModelOutput: ({ toolCallId, output }) => {\n const record = ctx.store.get(toolCallId);\n return { type: \"text\", value: record ? record.rendered : stringifyToolResult(output) };\n },\n };\n return wrapped;\n}\n\nexport function wrapTools(tools: ToolSetLike, ctx: WrapContext): ToolSetLike {\n const out: ToolSetLike = {};\n for (const name of Object.keys(tools)) {\n const tool = tools[name];\n if (tool) out[name] = wrapTool(name, tool, ctx);\n }\n return out;\n}\n\nexport function resolveTargetRatio(ratio: number | undefined): number {\n return ratio ?? DEFAULT_TARGET_RATIO;\n}\n\nfunction isAsyncIterable(value: unknown): value is AsyncIterable<unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as AsyncIterable<unknown>)[Symbol.asyncIterator] === \"function\"\n );\n}\n\n/** Content-addressed, not counter- or clock-based: two runs of the same tool call\n * must key the same record, or a customer's prompt cache stops hitting. */\nfunction contentAddressedId(toolName: string, originalText: string): string {\n return `tokz-${sha256hex(toBytes(`${toolName}\u0000${originalText}`)).slice(0, 16)}`;\n}\n","import { compressText, renderResult } from \"./engine.js\";\nimport { sharedStore } from \"./store.js\";\nimport { stringifyToolResult } from \"./wrap.js\";\nimport type { ModelMessageLike, RetentionStore, ToolResultPart } from \"./types.js\";\n\n/**\n * Re-compress retained tool results at a tighter budget.\n *\n * This is a pure local function: the original text and its span map are already in\n * the store, so producing a smaller view of an old tool result costs zero API calls\n * and zero inference — it is the same deterministic CPU pass that produced the first\n * view. That property is the whole point of retaining the pair; a wrapper that threw\n * the original away would have to re-ask a model to shrink history, which costs more\n * than the tokens it saves.\n *\n * Input messages are not mutated; changed messages are returned as new objects.\n */\nexport function compactHistory(\n messages: readonly ModelMessageLike[],\n targetRatio: number,\n store: RetentionStore = sharedStore,\n): ModelMessageLike[] {\n return messages.map((message) => {\n if (!Array.isArray(message.content)) return message;\n\n let changed = false;\n const content = (message.content as unknown[]).map((part) => {\n if (!isToolResultPart(part)) return part;\n const record = store.get(part.toolCallId);\n if (!record) return part;\n\n const tighter = compressText(record.originalText, { targetRatio });\n const rendered = renderResult(tighter, record.originalText);\n if (rendered === currentValue(part)) return part;\n\n store.set({ ...record, result: tighter, rendered, targetRatio });\n changed = true;\n return part.output !== undefined\n ? { ...part, output: { type: \"text\" as const, value: rendered } }\n : { ...part, result: rendered };\n });\n\n return changed ? { ...message, content } : message;\n });\n}\n\nfunction currentValue(part: ToolResultPart): string | undefined {\n if (part.output && \"value\" in part.output && typeof part.output.value === \"string\") {\n return part.output.value;\n }\n if (part.result !== undefined) return stringifyToolResult(part.result);\n return undefined;\n}\n\nfunction isToolResultPart(part: unknown): part is ToolResultPart {\n if (typeof part !== \"object\" || part === null) return false;\n const p = part as { type?: unknown; toolCallId?: unknown };\n return p.type === \"tool-result\" && typeof p.toolCallId === \"string\";\n}\n","import { expand } from \"@tokz/core\";\nimport { sharedStore } from \"./store.js\";\nimport type { ModelMessageLike, RetentionStore } from \"./types.js\";\n\nexport type ReferencedElision = {\n toolCallId: string;\n toolName: string;\n elisionIndex: number;\n /** Terms from the elided bytes that appear in the model's response. */\n matchedTerms: string[];\n /** The elided bytes, recovered verbatim from the caller's own text. */\n text: string;\n note?: string;\n};\n\nexport type DetectOptions = {\n /** Shortest token considered a reference. Below 4 characters the match rate is\n * dominated by English words that happen to appear in JSON. */\n minTermLength?: number;\n /** Cap on terms sampled per elision, to bound work on multi-megabyte drops. */\n maxTermsPerElision?: number;\n};\n\n/**\n * HEURISTIC. There is no reliable signal that a model \"meant\" an elided field, so\n * this matches vocabulary: recover each elided run locally, take the identifiers it\n * contains, and flag the elision when the response mentions one of them. It over-\n * fires on generic words (mitigated by a stopword list and a length floor) and\n * under-fires when the model paraphrases without naming anything. Treat a hit as a\n * cheap prompt to re-supply context, never as proof of a reference.\n *\n * Recovery itself is exact: `expand` verifies `srcSha256` and returns the dropped\n * bytes verbatim, with no network call.\n */\nexport function detectReferencedElisions(\n responseText: string,\n store: RetentionStore = sharedStore,\n opts: DetectOptions = {},\n): ReferencedElision[] {\n const minTermLength = opts.minTermLength ?? 4;\n const maxTerms = opts.maxTermsPerElision ?? 256;\n const haystack = responseText.toLowerCase();\n const hits: ReferencedElision[] = [];\n\n for (const record of store.records()) {\n // Semantic results carry a provenance map, not recoverable byte ranges.\n if (record.result.method === \"semantic\") continue;\n const spanMap = record.result.spanMap;\n\n for (let i = 0; i < spanMap.elisions.length; i++) {\n const elision = spanMap.elisions[i];\n if (!elision) continue;\n const text = expand(record.originalText, spanMap, { elision: i });\n const matchedTerms = candidateTerms(text, minTermLength, maxTerms).filter((term) =>\n containsWord(haystack, term),\n );\n if (matchedTerms.length === 0) continue;\n hits.push({\n toolCallId: record.toolCallId,\n toolName: record.toolName,\n elisionIndex: i,\n matchedTerms,\n text,\n ...(elision.note === undefined ? {} : { note: elision.note }),\n });\n }\n }\n return hits;\n}\n\n/** A user-role message re-supplying the recovered bytes, ready to append before a\n * follow-up turn. Separate from detection so callers can inspect first. */\nexport function expansionMessage(expansions: readonly ReferencedElision[]): ModelMessageLike {\n const blocks = expansions.map(\n (e) =>\n `Recovered from the elided portion of tool result \"${e.toolName}\"` +\n `${e.note ? ` (${e.note})` : \"\"}:\\n${e.text}`,\n );\n return { role: \"user\", content: blocks.join(\"\\n\\n\") };\n}\n\nconst STOPWORDS = new Set([\n \"true\",\n \"false\",\n \"null\",\n \"http\",\n \"https\",\n \"this\",\n \"that\",\n \"with\",\n \"from\",\n \"have\",\n \"been\",\n \"were\",\n \"they\",\n \"there\",\n \"then\",\n \"than\",\n \"your\",\n \"when\",\n \"what\",\n \"which\",\n \"would\",\n \"could\",\n \"should\",\n \"about\",\n \"into\",\n \"more\",\n \"some\",\n \"only\",\n \"also\",\n \"each\",\n \"over\",\n \"just\",\n \"like\",\n \"them\",\n \"these\",\n \"those\",\n]);\n\nconst JSON_KEY = /\"([^\"\\\\]+)\"\\s*:/g;\n/** Hyphens and dots are inside the token, not between tokens: the identifiers worth\n * matching are `api-5`, `node-2`, `registry.example.com`, not their fragments. */\nconst WORD = /[A-Za-z][A-Za-z0-9_.-]*[A-Za-z0-9]/g;\n\nfunction candidateTerms(text: string, minLength: number, cap: number): string[] {\n const terms: string[] = [];\n const seen = new Set<string>();\n const add = (term: string): void => {\n if (term.length < minLength || STOPWORDS.has(term) || seen.has(term)) return;\n seen.add(term);\n terms.push(term);\n };\n const push = (raw: string): void => {\n add(raw.toLowerCase());\n // A model asked about `restartCount` usually writes \"restart count\", so the\n // separated spelling is a term in its own right.\n const spaced = raw.replace(/([a-z0-9])([A-Z])/g, \"$1 $2\").replace(/[_-]+/g, \" \").toLowerCase();\n if (spaced !== raw.toLowerCase()) add(spaced);\n };\n\n // Keys first: a model asking about elided data almost always names the field.\n for (const m of text.matchAll(JSON_KEY)) {\n if (terms.length >= cap) return terms;\n if (m[1]) push(m[1]);\n }\n for (const m of text.matchAll(WORD)) {\n if (terms.length >= cap) return terms;\n push(m[0]);\n }\n return terms;\n}\n\nfunction containsWord(haystack: string, term: string): boolean {\n let from = 0;\n for (;;) {\n const at = haystack.indexOf(term, from);\n if (at === -1) return false;\n const before = at === 0 ? \"\" : haystack[at - 1]!;\n const after = haystack[at + term.length] ?? \"\";\n if (!isWordChar(before) && !isWordChar(after)) return true;\n from = at + 1;\n }\n}\n\nfunction isWordChar(ch: string): boolean {\n return ch !== \"\" && /[A-Za-z0-9_]/.test(ch);\n}\n","import { byteLength } from \"@tokz/core\";\nimport { compactHistory } from \"./compact.js\";\nimport { detectReferencedElisions, expansionMessage, type ReferencedElision } from \"./autoexpand.js\";\nimport { sharedStore } from \"./store.js\";\nimport { resolveTargetRatio, wrapTools } from \"./wrap.js\";\nimport type {\n GenerateTextFn,\n GenerateTextResultLike,\n ModelMessageLike,\n RetentionStore,\n TokzOptions,\n ToolSetLike,\n} from \"./types.js\";\n\nexport type TokzGenerateTextOptions = Record<string, unknown> & { tokz?: TokzOptions };\n\nexport type TokzReport = {\n store: RetentionStore;\n targetRatio: number;\n /** Bytes of tool output before and after compression, this call only. */\n originalBytes: number;\n renderedBytes: number;\n expansions: ReferencedElision[];\n /** Whether a second delegate call was made to re-supply expanded content. */\n reprompted: boolean;\n};\n\nexport type TokzGenerateTextResult = GenerateTextResultLike & { tokz: TokzReport };\n\n/**\n * Drop-in replacement for `ai.generateText`. Every option is forwarded untouched\n * except `tools`, which is wrapped so results reach the model compressed, and\n * `messages`, which is optionally compacted first.\n */\nexport async function generateText(\n options: TokzGenerateTextOptions,\n): Promise<TokzGenerateTextResult> {\n const { tokz = {}, ...rest } = options;\n const store = tokz.store ?? sharedStore;\n const targetRatio = resolveTargetRatio(tokz.targetRatio);\n const delegate = tokz.delegate ?? (await loadDelegate());\n\n const forwarded: Record<string, unknown> = { ...rest };\n\n if (isToolSet(rest[\"tools\"])) {\n forwarded[\"tools\"] = wrapTools(rest[\"tools\"], { store, targetRatio });\n }\n\n if (tokz.conversationCompaction && Array.isArray(rest[\"messages\"])) {\n const ratio = tokz.compactionRatio ?? targetRatio * 0.6;\n forwarded[\"messages\"] = compactHistory(rest[\"messages\"] as ModelMessageLike[], ratio, store);\n }\n\n const before = snapshotBytes(store);\n let result = await delegate(forwarded);\n const usage = deltaBytes(store, before);\n\n let expansions: ReferencedElision[] = [];\n let reprompted = false;\n if (tokz.autoExpand === \"on-reference\" && typeof result.text === \"string\") {\n expansions = detectReferencedElisions(result.text, store);\n if (expansions.length > 0 && Array.isArray(forwarded[\"messages\"])) {\n // One re-prompt only. The expansion is local and free, but the extra turn is\n // not, and a loop that re-expands on its own answer never terminates.\n const messages = [\n ...(forwarded[\"messages\"] as ModelMessageLike[]),\n { role: \"assistant\", content: result.text },\n expansionMessage(expansions),\n ];\n result = await delegate({ ...forwarded, messages });\n reprompted = true;\n }\n }\n\n return {\n ...result,\n tokz: {\n store,\n targetRatio,\n originalBytes: usage.original,\n renderedBytes: usage.rendered,\n expansions,\n reprompted,\n },\n };\n}\n\n/**\n * `ai` is an optional peer, so the specifier is held in a variable: a literal would\n * make TypeScript resolve the module at build time and fail in a workspace that has\n * not installed it, which is exactly the install this package must still compile in.\n */\nasync function loadDelegate(): Promise<GenerateTextFn> {\n const specifier = \"ai\";\n const mod = (await import(specifier)) as { generateText?: GenerateTextFn };\n if (typeof mod.generateText !== \"function\") {\n throw new Error(\"tokz: the `ai` package is required unless `tokz.delegate` is supplied\");\n }\n return mod.generateText;\n}\n\nfunction isToolSet(value: unknown): value is ToolSetLike {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction snapshotBytes(store: RetentionStore): Set<string> {\n return new Set(store.records().map((r) => r.toolCallId));\n}\n\nfunction deltaBytes(\n store: RetentionStore,\n before: Set<string>,\n): { original: number; rendered: number } {\n let original = 0;\n let rendered = 0;\n for (const record of store.records()) {\n if (before.has(record.toolCallId)) continue;\n original += byteLength(record.originalText);\n rendered += byteLength(record.rendered);\n }\n return { original, rendered };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../core/src/bytes.ts","../../core/src/expand.ts","../../core/src/constants.ts","../src/generate.ts","../src/compressor.ts","../src/compact.ts","../src/store.ts","../src/wrap.ts","../src/autoexpand.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { Span, SpanMap, Elision } from \"./types.js\";\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder(\"utf-8\");\n\nexport function sha256hex(bytes: Uint8Array): string {\n return createHash(\"sha256\").update(bytes).digest(\"hex\");\n}\n\nexport function toBytes(text: string): Uint8Array {\n return enc.encode(text);\n}\n\nexport function fromBytes(bytes: Uint8Array): string {\n return dec.decode(bytes);\n}\n\nexport function byteLength(text: string): number {\n return enc.encode(text).length;\n}\n\nexport function isAscii(text: string): boolean {\n for (let i = 0; i < text.length; i++) {\n if (text.charCodeAt(i) > 127) return false;\n }\n return true;\n}\n\n/**\n * Concatenate the source bytes covered by `spans` and decode as UTF-8. This is\n * exactly the compressed text: only surviving bytes, in source order, verbatim.\n */\nexport function assemble(src: Uint8Array, spans: Span[]): string {\n let len = 0;\n for (const sp of spans) len += sp.e - sp.s;\n const out = new Uint8Array(len);\n let o = 0;\n for (const sp of spans) {\n out.set(src.subarray(sp.s, sp.e), o);\n o += sp.e - sp.s;\n }\n return dec.decode(out);\n}\n\n/** Byte range in the source that an elision dropped. */\nexport function elisionRange(spanMap: SpanMap, elision: Elision): { start: number; end: number } {\n const start = elision.after < 0 ? 0 : spanMap.spans[elision.after]!.e;\n return { start, end: start + elision.bytes };\n}\n\n/**\n * jsonc-parser and most tokenizers report offsets in UTF-16 code units; the span\n * map speaks UTF-8 bytes. Build a converter from string index to byte offset.\n *\n * For ASCII — which most tool output is — the two are the same number, so the\n * prefix table is skipped entirely rather than allocating 4 bytes per character\n * to answer `i => i`.\n */\nexport function byteMapper(text: string, ascii?: boolean): (strIdx: number) => number {\n if (ascii ?? isAscii(text)) {\n const len = text.length;\n return (strIdx: number) => Math.max(0, Math.min(strIdx, len));\n }\n\n const prefix = new Int32Array(text.length + 1);\n let bytes = 0;\n let i = 0;\n const buf = new Uint8Array(4);\n for (const cp of text) {\n const units = cp.length; // 1 or 2 UTF-16 units\n const n = enc.encodeInto(cp, buf).written ?? 0;\n // Every UTF-16 index inside a code point maps to the byte offset at its start.\n for (let u = 0; u < units; u++) {\n prefix[i] = bytes;\n i++;\n }\n bytes += n;\n }\n prefix[i] = bytes;\n return (strIdx: number) => prefix[Math.max(0, Math.min(strIdx, text.length))]!;\n}\n","import type { SpanMap, ExpandOpts } from \"./types.js\";\nimport { sha256hex, toBytes, fromBytes, elisionRange } from \"./bytes.js\";\n\n/**\n * Local, network-free reconstruction from the caller's own source plus a span\n * map. This is what lets a stateless API offer recovery at all: we hold nothing,\n * because the caller already has everything.\n *\n * - no opts -> the full original text, after verifying `srcSha256`.\n * - {elision: i} -> just the bytes elision `i` dropped, recovered verbatim.\n *\n * Throws if the map was not built from this source, rather than returning\n * plausible-looking misaligned output.\n */\nexport function expand(source: string | Uint8Array, spanMap: SpanMap, opts: ExpandOpts = {}): string {\n const src = typeof source === \"string\" ? toBytes(source) : source;\n if (sha256hex(src) !== spanMap.srcSha256) {\n throw new Error(\n \"tokz: srcSha256 mismatch — this span map was not built from the given source (was the text edited?)\",\n );\n }\n if (opts.elision !== undefined) {\n const el = spanMap.elisions[opts.elision];\n if (!el) throw new Error(`tokz: no elision at index ${opts.elision}`);\n const { start, end } = elisionRange(spanMap, el);\n return fromBytes(src.subarray(start, end));\n }\n return fromBytes(src);\n}\n","/** Default share of source bytes to keep when the caller does not say. */\nexport const DEFAULT_TARGET_RATIO = 0.45;\n\n/** Max request body the hosted API accepts. Compression cost is linear in input\n * size, so this is where predictable latency stops, not an arbitrary number. */\nexport const MAX_PAYLOAD_BYTES = 1024 * 1024;\n\n/**\n * Nesting past this is declined rather than compressed. Parsers and the\n * selection walk both recurse per level, so a deeply nested document overflows\n * the stack — and `\"[\".repeat(6000)` reaches that in ~12 KB, well inside any\n * request body limit. Declining costs the caller nothing; throwing would hand a\n * remote caller a way to kill a request slot.\n */\nexport const MAX_NESTING_DEPTH = 200;\n\n/** Latency budgets from the design doc, used by the eval kill gates. */\nexport const STRUCTURAL_BUDGET_MS = 10;\nexport const SEMANTIC_BUDGET_MS = 30;\n\n/** Payload size the structural latency budget is quoted against. Cost is linear\n * above this, so a latency number without a size attached means little. */\nexport const LATENCY_BUDGET_PAYLOAD_BYTES = 64 * 1024;\n\n/** API key format: `tokz_live_<id8>_<secret>`. The id is public and indexed; only\n * a SHA-256 of the secret is stored. */\nexport const KEY_PREFIX = \"tokz_live_\";\n","import { byteLength } from \"@tokz/core\";\r\nimport { CircuitBreaker } from \"@tokz/sdk\";\r\nimport { apiCompressor, type Compressor } from \"./compressor.js\";\r\nimport { compactHistory } from \"./compact.js\";\r\nimport { detectReferencedElisions, expansionMessage, type ReferencedElision } from \"./autoexpand.js\";\r\nimport { sharedStore } from \"./store.js\";\r\nimport { resolveTargetRatio, wrapTools } from \"./wrap.js\";\r\nimport type {\r\n GenerateTextFn,\r\n GenerateTextResultLike,\r\n ModelMessageLike,\r\n RetentionStore,\r\n TokzOptions,\r\n ToolSetLike,\r\n} from \"./types.js\";\r\n\r\nexport type TokzGenerateTextOptions = Record<string, unknown> & { tokz?: TokzOptions };\r\n\r\nexport type TokzReport = {\r\n store: RetentionStore;\r\n targetRatio: number;\r\n /** Bytes of tool output before and after compression, this call only. */\r\n originalBytes: number;\r\n renderedBytes: number;\r\n expansions: ReferencedElision[];\r\n /** Whether a second delegate call was made to re-supply expanded content. */\r\n reprompted: boolean;\r\n};\r\n\r\nexport type TokzGenerateTextResult = GenerateTextResultLike & { tokz: TokzReport };\r\n\r\n/**\r\n * Drop-in replacement for `ai.generateText`. Every option is forwarded untouched\r\n * except `tools`, which is wrapped so results reach the model compressed, and\r\n * `messages`, which is optionally compacted first.\r\n */\r\nexport async function generateText(\r\n options: TokzGenerateTextOptions,\r\n): Promise<TokzGenerateTextResult> {\r\n const { tokz = {}, ...rest } = options;\r\n const store = tokz.store ?? sharedStore;\r\n const targetRatio = resolveTargetRatio(tokz.targetRatio);\r\n const compress = resolveCompressor(tokz);\r\n const delegate = tokz.delegate ?? (await loadDelegate());\r\n\r\n // One breaker per generateText call, shared by tool-result compression and\r\n // compaction: a dead API trips both onto the fallback path together.\r\n const breaker = new CircuitBreaker({\r\n ...(tokz.failureThreshold !== undefined ? { failureThreshold: tokz.failureThreshold } : {}),\r\n ...(tokz.resetTimeout !== undefined ? { resetTimeout: tokz.resetTimeout } : {}),\r\n });\r\n const onError = tokz.onError;\r\n\r\n const forwarded: Record<string, unknown> = { ...rest };\r\n\r\n if (isToolSet(rest[\"tools\"])) {\r\n forwarded[\"tools\"] = wrapTools(rest[\"tools\"], {\r\n store,\r\n targetRatio,\r\n compress,\r\n breaker,\r\n ...(onError !== undefined ? { onError } : {}),\r\n });\r\n }\r\n\r\n if (tokz.conversationCompaction && Array.isArray(rest[\"messages\"])) {\r\n const ratio = tokz.compactionRatio ?? targetRatio * 0.6;\r\n forwarded[\"messages\"] = await compactHistory(rest[\"messages\"] as ModelMessageLike[], ratio, {\r\n compress,\r\n store,\r\n breaker,\r\n ...(onError !== undefined ? { onError } : {}),\r\n });\r\n }\r\n\r\n const before = snapshotBytes(store);\r\n let result = await delegate(forwarded);\r\n const usage = deltaBytes(store, before);\r\n\r\n let expansions: ReferencedElision[] = [];\r\n let reprompted = false;\r\n if (tokz.autoExpand === \"on-reference\" && typeof result.text === \"string\") {\r\n expansions = detectReferencedElisions(result.text, store);\r\n if (expansions.length > 0 && Array.isArray(forwarded[\"messages\"])) {\r\n // One re-prompt only. The expansion is local and free, but the extra turn is\r\n // not, and a loop that re-expands on its own answer never terminates.\r\n const messages = [\r\n ...(forwarded[\"messages\"] as ModelMessageLike[]),\r\n { role: \"assistant\", content: result.text },\r\n expansionMessage(expansions),\r\n ];\r\n result = await delegate({ ...forwarded, messages });\r\n reprompted = true;\r\n }\r\n }\r\n\r\n return {\r\n ...result,\r\n tokz: {\r\n store,\r\n targetRatio,\r\n originalBytes: usage.original,\r\n renderedBytes: usage.rendered,\r\n expansions,\r\n reprompted,\r\n },\r\n };\r\n}\r\n\r\nfunction resolveCompressor(tokz: TokzOptions): Compressor {\r\n if (tokz.compressor) return tokz.compressor;\r\n const opts: Parameters<typeof apiCompressor>[0] = {};\r\n if (tokz.apiKey !== undefined) opts.apiKey = tokz.apiKey;\r\n if (tokz.baseUrl !== undefined) opts.baseUrl = tokz.baseUrl;\r\n if (tokz.timeout !== undefined) opts.timeout = tokz.timeout;\r\n if (tokz.retries !== undefined) opts.retries = tokz.retries;\r\n if (tokz.retryDelay !== undefined) opts.retryDelay = tokz.retryDelay;\r\n return apiCompressor(opts);\r\n}\r\n\r\n/**\r\n * `ai` is an optional peer, so the specifier is held in a variable: a literal would\r\n * make TypeScript resolve the module at build time and fail in a workspace that has\r\n * not installed it, which is exactly the install this package must still compile in.\r\n */\r\nasync function loadDelegate(): Promise<GenerateTextFn> {\r\n const specifier = \"ai\";\r\n const mod = (await import(specifier)) as { generateText?: GenerateTextFn };\r\n if (typeof mod.generateText !== \"function\") {\r\n throw new Error(\"tokz: the `ai` package is required unless `tokz.delegate` is supplied\");\r\n }\r\n return mod.generateText;\r\n}\r\n\r\nfunction isToolSet(value: unknown): value is ToolSetLike {\r\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\r\n}\r\n\r\nfunction snapshotBytes(store: RetentionStore): Set<string> {\r\n return new Set(store.records().map((r) => r.toolCallId));\r\n}\r\n\r\nfunction deltaBytes(\r\n store: RetentionStore,\r\n before: Set<string>,\r\n): { original: number; rendered: number } {\r\n let original = 0;\r\n let rendered = 0;\r\n for (const record of store.records()) {\r\n if (before.has(record.toolCallId)) continue;\r\n original += byteLength(record.originalText);\r\n rendered += byteLength(record.rendered);\r\n }\r\n return { original, rendered };\r\n}\r\n","import { assemble, toBytes, type CompressionResult, type CompressOpts } from \"@tokz/core\";\r\nimport { Tokz } from \"@tokz/sdk\";\r\n\r\n/**\r\n * Where compression comes from.\r\n *\r\n * This package never bundles the engine. `@tokz/engine` is private and lives in\r\n * the hosted API, so the only compression a customer's process can reach is the\r\n * one over HTTP — and the SDK is what talks to it. Injecting the function rather\r\n * than importing an engine also lets tests (and, later, a local CLI build) supply\r\n * their own without this file knowing which.\r\n *\r\n * The rendered text comes back alongside the result because the SDK already\r\n * assembled and hash-verified it; recomputing it here would repeat that work.\r\n */\r\nexport type CompressedView = { result: CompressionResult; text: string };\r\n\r\nexport type Compressor = (text: string, opts: CompressOpts) => Promise<CompressedView>;\r\n\r\nexport type ApiCompressorOptions = {\r\n apiKey?: string;\r\n /** Defaults to https://api.tokz.dev. Point at http://localhost:PORT in dev. */\r\n baseUrl?: string;\r\n fetch?: typeof fetch;\r\n /** Per-attempt timeout in ms. Default 5000 (enforced by @tokz/sdk). */\r\n timeout?: number;\r\n /** Retries after the first attempt, on retryable failures. Default 2. */\r\n retries?: number;\r\n /** Base backoff in ms for retries. Default 500. */\r\n retryDelay?: number;\r\n};\r\n\r\n/**\r\n * Compress against the hosted API.\r\n *\r\n * The client is constructed on first use, not here: `generateText` resolves a\r\n * compressor on every call, and a caller who injects their own must not need a\r\n * key to exist for a client they will never touch.\r\n */\r\nexport function apiCompressor(options: ApiCompressorOptions = {}): Compressor {\r\n let client: Tokz | undefined;\r\n\r\n return async (text, opts) => {\r\n if (!client) {\r\n const apiKey = options.apiKey ?? readEnv(\"TOKZ_API_KEY\");\r\n if (!apiKey) {\r\n throw new Error(\r\n \"tokz: no API key. Pass `tokz: { apiKey }`, set TOKZ_API_KEY, or inject `tokz: { compressor }`.\",\r\n );\r\n }\r\n const init: ConstructorParameters<typeof Tokz>[0] = { apiKey };\r\n const baseUrl = options.baseUrl ?? readEnv(\"TOKZ_BASE_URL\");\r\n if (baseUrl) init.baseUrl = baseUrl;\r\n if (options.fetch) init.fetch = options.fetch;\r\n client = new Tokz(init);\r\n }\r\n\r\n // Resilience knobs are set on the client per call, not baked into the injected\r\n // `CompressOpts` (which is the wire contract and carries no transport concerns).\r\n const withTransport = {\r\n ...opts,\r\n ...(options.timeout !== undefined ? { timeout: options.timeout } : {}),\r\n ...(options.retries !== undefined ? { retries: options.retries } : {}),\r\n ...(options.retryDelay !== undefined ? { retryDelay: options.retryDelay } : {}),\r\n };\r\n // `TokzResult` is `CompressionResult & { text }`; the extra field is peeled off\r\n // so what lands in the retention store (and any customer's log of it) is the\r\n // wire result and nothing else. Spreading a union widens it, hence the cast.\r\n const { text: rendered, ...rest } = await client.compress(text, withTransport);\r\n return { result: rest as CompressionResult, text: rendered };\r\n };\r\n}\r\n\r\n/** What the model should see for a result, whichever path produced it. */\r\nexport function renderResult(result: CompressionResult, source: string): string {\r\n if (result.method === \"semantic\") return result.compressedText;\r\n return assemble(toBytes(source), result.spanMap.spans);\r\n}\r\n\r\nfunction readEnv(name: string): string | undefined {\r\n const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process\r\n ?.env;\r\n return env?.[name];\r\n}\r\n","import { CircuitBreaker, emitError, type TokzErrorInfo } from \"@tokz/sdk\";\r\nimport type { Compressor } from \"./compressor.js\";\r\nimport { sharedStore } from \"./store.js\";\r\nimport { stringifyToolResult } from \"./wrap.js\";\r\nimport type { ModelMessageLike, RetentionStore, ToolResultPart } from \"./types.js\";\r\n\r\nexport type CompactContext = {\r\n compress: Compressor;\r\n store?: RetentionStore;\r\n /** Shared with tool-result compression so one dead API opens the same circuit. */\r\n breaker?: CircuitBreaker;\r\n onError?: (info: TokzErrorInfo) => void;\r\n};\r\n\r\n/**\r\n * Re-compress retained tool results at a tighter budget.\r\n *\r\n * The store holds each original next to its map, so a smaller view of an old tool\r\n * result costs no tool re-execution and no model inference — it is the same\r\n * deterministic pass that produced the first view, re-run at a lower ratio. That\r\n * is the property a wrapper which discarded the original cannot have: it would\r\n * have to ask a model to summarise history, which costs more than it saves.\r\n *\r\n * It is not free of *our* API, though: compression runs server-side, so this is\r\n * one `/compress` call per retained result that actually appears in `messages`.\r\n * Cheap and deterministic, but not zero — do not describe it as offline.\r\n *\r\n * Input messages are not mutated; changed messages are returned as new objects.\r\n */\r\nexport async function compactHistory(\r\n messages: readonly ModelMessageLike[],\r\n targetRatio: number,\r\n ctx: CompactContext,\r\n): Promise<ModelMessageLike[]> {\r\n const store = ctx.store ?? sharedStore;\r\n\r\n return Promise.all(\r\n messages.map(async (message) => {\r\n if (!Array.isArray(message.content)) return message;\r\n\r\n let changed = false;\r\n const content = await Promise.all(\r\n (message.content as unknown[]).map(async (part) => {\r\n if (!isToolResultPart(part)) return part;\r\n const record = store.get(part.toolCallId);\r\n if (!record) return part;\r\n\r\n // Compaction is best-effort: a failing tokz leaves the already-rendered part\r\n // in place rather than failing the turn.\r\n const breaker = ctx.breaker;\r\n if (breaker?.shouldSkip()) return part;\r\n let tighter, rendered;\r\n try {\r\n ({ result: tighter, text: rendered } = await ctx.compress(record.originalText, {\r\n targetRatio,\r\n }));\r\n breaker?.recordSuccess();\r\n } catch (err) {\r\n breaker?.recordFailure();\r\n emitError(ctx.onError, err, \"tokz compaction failed, keeping the current render\");\r\n return part;\r\n }\r\n if (rendered === currentValue(part)) return part;\r\n\r\n store.set({ ...record, result: tighter, rendered, targetRatio });\r\n changed = true;\r\n return part.output !== undefined\r\n ? { ...part, output: { type: \"text\" as const, value: rendered } }\r\n : { ...part, result: rendered };\r\n }),\r\n );\r\n\r\n return changed ? { ...message, content } : message;\r\n }),\r\n );\r\n}\r\n\r\nfunction currentValue(part: ToolResultPart): string | undefined {\r\n if (part.output && \"value\" in part.output && typeof part.output.value === \"string\") {\r\n return part.output.value;\r\n }\r\n if (part.result !== undefined) return stringifyToolResult(part.result);\r\n return undefined;\r\n}\r\n\r\nfunction isToolResultPart(part: unknown): part is ToolResultPart {\r\n if (typeof part !== \"object\" || part === null) return false;\r\n const p = part as { type?: unknown; toolCallId?: unknown };\r\n return p.type === \"tool-result\" && typeof p.toolCallId === \"string\";\r\n}\r\n","import type { RetentionRecord, RetentionStore } from \"./types.js\";\n\n/** Insertion-ordered so `records()` reads back in call order — history compaction\n * and any future oldest-first budget both depend on that order being stable. */\nexport class MapRetentionStore implements RetentionStore {\n readonly #byId = new Map<string, RetentionRecord>();\n\n get(toolCallId: string): RetentionRecord | undefined {\n return this.#byId.get(toolCallId);\n }\n\n set(record: RetentionRecord): void {\n this.#byId.set(record.toolCallId, record);\n }\n\n records(): RetentionRecord[] {\n return [...this.#byId.values()];\n }\n\n clear(): void {\n this.#byId.clear();\n }\n}\n\n/**\n * Process-wide default so `compactHistory(messages, ratio)` matches the spec'd\n * two-argument signature and still finds the originals a previous `generateText`\n * retained. Pass an explicit store per conversation when one process serves many.\n */\nexport const sharedStore: RetentionStore = new MapRetentionStore();\n","import { DEFAULT_TARGET_RATIO, sha256hex, toBytes } from \"@tokz/core\";\nimport { CircuitBreaker, emitError, type TokzErrorInfo } from \"@tokz/sdk\";\nimport type { Compressor } from \"./compressor.js\";\nimport type {\n RetentionRecord,\n RetentionStore,\n ToolExecuteOptions,\n ToolLike,\n ToolSetLike,\n} from \"./types.js\";\n\nexport type WrapContext = {\n store: RetentionStore;\n targetRatio: number;\n compress: Compressor;\n /** Shared across every tool call from one generateText wrapper. Omitted only by\n * callers that build a context by hand; a fresh, always-closed breaker is used. */\n breaker?: CircuitBreaker;\n onError?: (info: TokzErrorInfo) => void;\n};\n\n/** Tool output as text the compressor can work on. Pretty-printed because the\n * structural detector reads JSON shape, and key order follows the object, so the\n * same result always serialises to the same bytes. */\nexport function stringifyToolResult(value: unknown): string {\n if (typeof value === \"string\") return value;\n return JSON.stringify(value ?? null, null, 2);\n}\n\n/**\n * Wrap one tool so the model receives the compressed render while the original text\n * and its span map stay in the store.\n *\n * `execute` returns the render rather than the raw value, and `toModelOutput` maps\n * to the same string. Doing both is deliberate: `toModelOutput` is the documented\n * tool-to-model hook, but returning the render from `execute` means the original\n * cannot reach a message even under an SDK version that ignores the hook. The\n * original is one `store.get(toolCallId)` away for the caller.\n */\nexport function wrapTool(toolName: string, tool: ToolLike, ctx: WrapContext): ToolLike {\n const inner = tool.execute;\n // Client-side and provider-executed tools have no server-side execute to intercept.\n if (typeof inner !== \"function\") return tool;\n\n const wrapped: ToolLike = {\n ...tool,\n execute: async (input: never, options: ToolExecuteOptions): Promise<unknown> => {\n const raw = await inner.call(tool, input, options);\n // A tool may return an AsyncIterable to stream its output. Compressing that\n // would mean buffering it, which changes the tool's semantics, so it passes\n // through uncompressed rather than silently becoming \"{}\".\n if (isAsyncIterable(raw)) return raw;\n const originalText = stringifyToolResult(raw);\n\n // Resilience: a failing tokz must never fail the tool, and therefore never the\n // model turn. On any error — or while the breaker is open — the model sees the\n // original text and no record is retained (there is nothing to expand).\n const breaker = ctx.breaker;\n if (breaker?.shouldSkip()) return originalText;\n try {\n const { result, text: rendered } = await ctx.compress(originalText, {\n targetRatio: ctx.targetRatio,\n });\n breaker?.recordSuccess();\n const toolCallId = options.toolCallId ?? contentAddressedId(toolName, originalText);\n const record: RetentionRecord = {\n toolName,\n toolCallId,\n originalText,\n result,\n rendered,\n targetRatio: ctx.targetRatio,\n };\n ctx.store.set(record);\n return rendered;\n } catch (err) {\n breaker?.recordFailure();\n emitError(ctx.onError, err, \"tokz compression failed, falling back to uncompressed\");\n return originalText;\n }\n },\n toModelOutput: ({ toolCallId, output }) => {\n const record = ctx.store.get(toolCallId);\n return { type: \"text\", value: record ? record.rendered : stringifyToolResult(output) };\n },\n };\n return wrapped;\n}\n\nexport function wrapTools(tools: ToolSetLike, ctx: WrapContext): ToolSetLike {\n const out: ToolSetLike = {};\n for (const name of Object.keys(tools)) {\n const tool = tools[name];\n if (tool) out[name] = wrapTool(name, tool, ctx);\n }\n return out;\n}\n\nexport function resolveTargetRatio(ratio: number | undefined): number {\n return ratio ?? DEFAULT_TARGET_RATIO;\n}\n\nfunction isAsyncIterable(value: unknown): value is AsyncIterable<unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as AsyncIterable<unknown>)[Symbol.asyncIterator] === \"function\"\n );\n}\n\n/** Content-addressed, not counter- or clock-based: two runs of the same tool call\n * must key the same record, or a customer's prompt cache stops hitting. */\nfunction contentAddressedId(toolName: string, originalText: string): string {\n return `tokz-${sha256hex(toBytes(`${toolName}\u0000${originalText}`)).slice(0, 16)}`;\n}\n","import { expand } from \"@tokz/core\";\nimport { sharedStore } from \"./store.js\";\nimport type { ModelMessageLike, RetentionStore } from \"./types.js\";\n\nexport type ReferencedElision = {\n toolCallId: string;\n toolName: string;\n elisionIndex: number;\n /** Terms from the elided bytes that appear in the model's response. */\n matchedTerms: string[];\n /** The elided bytes, recovered verbatim from the caller's own text. */\n text: string;\n note?: string;\n};\n\nexport type DetectOptions = {\n /** Shortest token considered a reference. Below 4 characters the match rate is\n * dominated by English words that happen to appear in JSON. */\n minTermLength?: number;\n /** Cap on terms sampled per elision, to bound work on multi-megabyte drops. */\n maxTermsPerElision?: number;\n};\n\n/**\n * HEURISTIC. There is no reliable signal that a model \"meant\" an elided field, so\n * this matches vocabulary: recover each elided run locally, take the identifiers it\n * contains, and flag the elision when the response mentions one of them. It over-\n * fires on generic words (mitigated by a stopword list and a length floor) and\n * under-fires when the model paraphrases without naming anything. Treat a hit as a\n * cheap prompt to re-supply context, never as proof of a reference.\n *\n * Recovery itself is exact: `expand` verifies `srcSha256` and returns the dropped\n * bytes verbatim, with no network call.\n */\nexport function detectReferencedElisions(\n responseText: string,\n store: RetentionStore = sharedStore,\n opts: DetectOptions = {},\n): ReferencedElision[] {\n const minTermLength = opts.minTermLength ?? 4;\n const maxTerms = opts.maxTermsPerElision ?? 256;\n const haystack = responseText.toLowerCase();\n const hits: ReferencedElision[] = [];\n\n for (const record of store.records()) {\n // Semantic results carry a provenance map, not recoverable byte ranges.\n if (record.result.method === \"semantic\") continue;\n const spanMap = record.result.spanMap;\n\n for (let i = 0; i < spanMap.elisions.length; i++) {\n const elision = spanMap.elisions[i];\n if (!elision) continue;\n const text = expand(record.originalText, spanMap, { elision: i });\n const matchedTerms = candidateTerms(text, minTermLength, maxTerms).filter((term) =>\n containsWord(haystack, term),\n );\n if (matchedTerms.length === 0) continue;\n hits.push({\n toolCallId: record.toolCallId,\n toolName: record.toolName,\n elisionIndex: i,\n matchedTerms,\n text,\n ...(elision.note === undefined ? {} : { note: elision.note }),\n });\n }\n }\n return hits;\n}\n\n/** A user-role message re-supplying the recovered bytes, ready to append before a\n * follow-up turn. Separate from detection so callers can inspect first. */\nexport function expansionMessage(expansions: readonly ReferencedElision[]): ModelMessageLike {\n const blocks = expansions.map(\n (e) =>\n `Recovered from the elided portion of tool result \"${e.toolName}\"` +\n `${e.note ? ` (${e.note})` : \"\"}:\\n${e.text}`,\n );\n return { role: \"user\", content: blocks.join(\"\\n\\n\") };\n}\n\nconst STOPWORDS = new Set([\n \"true\",\n \"false\",\n \"null\",\n \"http\",\n \"https\",\n \"this\",\n \"that\",\n \"with\",\n \"from\",\n \"have\",\n \"been\",\n \"were\",\n \"they\",\n \"there\",\n \"then\",\n \"than\",\n \"your\",\n \"when\",\n \"what\",\n \"which\",\n \"would\",\n \"could\",\n \"should\",\n \"about\",\n \"into\",\n \"more\",\n \"some\",\n \"only\",\n \"also\",\n \"each\",\n \"over\",\n \"just\",\n \"like\",\n \"them\",\n \"these\",\n \"those\",\n]);\n\nconst JSON_KEY = /\"([^\"\\\\]+)\"\\s*:/g;\n/** Hyphens and dots are inside the token, not between tokens: the identifiers worth\n * matching are `api-5`, `node-2`, `registry.example.com`, not their fragments. */\nconst WORD = /[A-Za-z][A-Za-z0-9_.-]*[A-Za-z0-9]/g;\n\nfunction candidateTerms(text: string, minLength: number, cap: number): string[] {\n const terms: string[] = [];\n const seen = new Set<string>();\n const add = (term: string): void => {\n if (term.length < minLength || STOPWORDS.has(term) || seen.has(term)) return;\n seen.add(term);\n terms.push(term);\n };\n const push = (raw: string): void => {\n add(raw.toLowerCase());\n // A model asked about `restartCount` usually writes \"restart count\", so the\n // separated spelling is a term in its own right.\n const spaced = raw.replace(/([a-z0-9])([A-Z])/g, \"$1 $2\").replace(/[_-]+/g, \" \").toLowerCase();\n if (spaced !== raw.toLowerCase()) add(spaced);\n };\n\n // Keys first: a model asking about elided data almost always names the field.\n for (const m of text.matchAll(JSON_KEY)) {\n if (terms.length >= cap) return terms;\n if (m[1]) push(m[1]);\n }\n for (const m of text.matchAll(WORD)) {\n if (terms.length >= cap) return terms;\n push(m[0]);\n }\n return terms;\n}\n\nfunction containsWord(haystack: string, term: string): boolean {\n let from = 0;\n for (;;) {\n const at = haystack.indexOf(term, from);\n if (at === -1) return false;\n const before = at === 0 ? \"\" : haystack[at - 1]!;\n const after = haystack[at + term.length] ?? \"\";\n if (!isWordChar(before) && !isWordChar(after)) return true;\n from = at + 1;\n }\n}\n\nfunction isWordChar(ch: string): boolean {\n return ch !== \"\" && /[A-Za-z0-9_]/.test(ch);\n}\n"],"mappings":"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","names":["CircuitBreaker","emitError","emitError","CircuitBreaker"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokz/ai-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Vercel AI SDK wrapper for tokz: compresses tool results in place, compacts history, and expands on reference.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
"main": "./dist/index.js",
|
|
18
18
|
"types": "./dist/index.d.ts",
|
|
19
19
|
"exports": {
|
|
20
|
-
".": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
}
|
|
21
24
|
},
|
|
22
25
|
"files": [
|
|
23
26
|
"dist"
|
|
@@ -29,24 +32,26 @@
|
|
|
29
32
|
"publishConfig": {
|
|
30
33
|
"access": "public"
|
|
31
34
|
},
|
|
32
|
-
"scripts": {
|
|
33
|
-
"build": "tsup",
|
|
34
|
-
"test": "vitest run",
|
|
35
|
-
"prepublishOnly": "pnpm build"
|
|
36
|
-
},
|
|
37
35
|
"dependencies": {
|
|
38
|
-
"@tokz/
|
|
36
|
+
"@tokz/sdk": "0.2.0"
|
|
39
37
|
},
|
|
40
38
|
"peerDependencies": {
|
|
41
39
|
"ai": ">=5"
|
|
42
40
|
},
|
|
43
41
|
"peerDependenciesMeta": {
|
|
44
|
-
"ai": {
|
|
42
|
+
"ai": {
|
|
43
|
+
"optional": true
|
|
44
|
+
}
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@tokz/core": "workspace:*",
|
|
48
47
|
"tsup": "^8.3.5",
|
|
49
48
|
"typescript": "^5.7.2",
|
|
50
|
-
"vitest": "^2.1.8"
|
|
49
|
+
"vitest": "^2.1.8",
|
|
50
|
+
"@tokz/core": "0.1.0",
|
|
51
|
+
"@tokz/engine": "0.1.0"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsup",
|
|
55
|
+
"test": "vitest run"
|
|
51
56
|
}
|
|
52
|
-
}
|
|
57
|
+
}
|