@tangleai/context 0.21.1 → 0.24.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # @tangleai/context
2
2
 
3
+ ## 0.24.1
4
+
5
+ ### Patch Changes
6
+
7
+ - @tangleai/models@0.24.1
8
+
9
+ ## 0.24.0
10
+
11
+ ### Patch Changes
12
+
13
+ - @tangleai/models@0.24.0
14
+
15
+ ## 0.23.0
16
+
17
+ ### Patch Changes
18
+
19
+ - @tangleai/models@0.23.0
20
+
21
+ ## 0.22.0
22
+
23
+ ### Patch Changes
24
+
25
+ - Convert the migrated source, tests, benchmarks and hosts to strict TypeScript,
26
+ with JavaScript and declarations emitted through one release build. Move the
27
+ program pen from `@tangleai/jaren/program` and the Jaren integration barrel to
28
+ `@tangleai/linq/program`, preserving its JSON format and phantom binding types.
29
+ The new `@tangleai/linq` root exposes the program namespace and shared build error.
30
+
31
+ Match the embedder declarations to unknown widths before the first response,
32
+ retain precise ledger result variants, and enforce the refinement-pressure
33
+ instrument's stated 60-second deadline through the chat client's abort signal.
34
+ - Updated dependencies
35
+ - @tangleai/models@0.22.0
36
+
3
37
  ## 0.21.1
4
38
 
5
39
  ### Patch Changes
package/README.md CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  Evidence-backed ledgers, bounded environments, recall, retention and storage contracts.
4
4
 
5
- This package keeps its JS/JSDoc implementation and deterministic tests. Inject
5
+ The implementation and deterministic tests use strict TypeScript. Published
6
+ packages contain ESM JavaScript and declarations emitted from that source. Inject
6
7
  fetch, storage and compiler services at the existing seams. The public source
7
8
  exports and emitted npm JavaScript share one implementation.
8
9
 
@@ -310,7 +311,7 @@ the single exception and it makes the caller state a budget, because a design wh
310
311
  is as easy as peeking is a design that ends up back in the transcript.
311
312
 
312
313
  **The root view does not grow with the corpus.** Sweeping a corpus across three orders of
313
- magnitude (10 kB → 10 MB, `test/context/environment-scale.test.js`), the root request stays
314
+ magnitude (10 kB → 10 MB, `test/context/environment-scale.test.ts`), the root request stays
314
315
  inside a 3 000-character band and moves by *tens* of characters between decades — the extra
315
316
  digits in a chunk's index, and nothing else. The digest lists at most twelve slots and
316
317
  reports how many it did not list; a cap that hid the difference would let a model conclude
@@ -338,7 +339,7 @@ so the request stops growing with the conversation, and an earlier round is reac
338
339
  anything else is: `env_grep` for it, `env_read` at the offset it reports. Over forty
339
340
  gathering rounds the request stays under 3 000 characters, and a value that a
340
341
  6 000-character `historyBudget` run no longer carries comes back from a 600-character read
341
- (`test/agents/transcript-slot.test.js`).
342
+ (`test/agents/transcript-slot.test.ts`).
342
343
 
343
344
  This is the alternative to `historyBudget` rather than a tuning of it: there is no budget to
344
345
  exceed when the history is addressed instead of resent. `historyBudget` keeps working
@@ -350,7 +351,7 @@ this existed — and which to reach for is the choice, not a migration.
350
351
 
351
352
  This injected adapter stores the complete ledger contract in one collection.
352
353
 
353
- ```js
354
+ ```ts
354
355
  import { openStore } from '@jarenjs/db';
355
356
  import { nodeDriver } from '@jarenjs/db/node';
356
357
 
@@ -361,7 +362,7 @@ import { nodeDriver } from '@jarenjs/db/node';
361
362
  * untyped: the ledger stores objects, strings and arrays under the same
362
363
  * contract, and only the vector member has to be declared.
363
364
  */
364
- const ledgerModel = (dims) => ({
365
+ const ledgerModel = (dims: any) => ({
365
366
  $model: '0.1',
366
367
  collections: {
367
368
  slots: {
@@ -381,7 +382,7 @@ const ledgerModel = (dims) => ({
381
382
  });
382
383
 
383
384
  /** A collection answer as a list — `execute` returns the bare item for one. */
384
- const many = (result) => (Array.isArray(result) ? result : result === undefined ? [] : [result]);
385
+ const many = (result: any) => (Array.isArray(result) ? result : result === undefined ? [] : [result]);
385
386
 
386
387
  /**
387
388
  * A durable ledger storage adapter over one `@jarenjs/db` collection:
@@ -395,20 +396,20 @@ const many = (result) => (Array.isArray(result) ? result : result === undefined
395
396
  * range scan over the key column. The ledger asks for a handful of
396
397
  * distinct prefixes, so the documents are built once each and cached.
397
398
  */
398
- export async function createDbStorage({ path = ':memory:', dims } = {}) {
399
+ export async function createDbStorage({ path = ':memory:', dims }: { path?: string; dims?: number; } = {}) {
399
400
  const store = await openStore(ledgerModel(dims), { driver: nodeDriver(), path });
400
- const slots = store.collection('slots');
401
+ const slots = store.collection<{ key: string; value: any; }>('slots');
401
402
  const documents = new Map();
402
403
 
403
404
  /** Every query document one prefix needs, built once. */
404
- const forPrefix = (prefix) => {
405
+ const forPrefix = (prefix: any) => {
405
406
  let built = documents.get(prefix);
406
407
  if (built !== undefined) return built;
407
408
  const under = { '$starts-with': ['$r.key', prefix] };
408
409
  const score = { $similarity: ['$r.value.embedding', '$q'] };
409
410
  const mine = [{ $eq: ['$r.value.embeddedBy.model', '$model'] },
410
- { $eq: ['$r.value.embeddedBy.dims', '$dims'] }];
411
- const counted = (where) => ({ $count: { $for: { r: '$[*]' }, $where: where, $return: '$r' } });
411
+ { $eq: ['$r.value.embeddedBy.dims', '$dims'] }];
412
+ const counted = (where: any) => ({ $count: { $for: { r: '$[*]' }, $where: where, $return: '$r' } });
412
413
  const ranked = {
413
414
  $for: { r: '$[*]' },
414
415
  $where: { $and: [under, ...mine] },
@@ -416,13 +417,13 @@ export async function createDbStorage({ path = ':memory:', dims } = {}) {
416
417
  // ordering only has to agree with its tie-break: score, then
417
418
  // newest, then the key
418
419
  $orderby: [{ $key: score, $dir: 'desc', $empty: 'least' },
419
- { $key: '$r.value.at', $dir: 'desc' }, '$r.key'],
420
+ { $key: '$r.value.at', $dir: 'desc' }, '$r.key'],
420
421
  $return: { key: '$r.key', score },
421
422
  };
422
423
  built = {
423
424
  keys: { $for: { r: '$[*]' }, $where: under, $orderby: ['$r.key'], $return: '$r.key' },
424
425
  ranked,
425
- window: (limit) => ({ $subsequence: [ranked, 0, limit] }),
426
+ window: (limit: any) => ({ $subsequence: [ranked, 0, limit] }),
426
427
  skipped: counted({ $and: [under, { $not: { $exists: '$r.value.embedding' } }] }),
427
428
  held: counted({ $and: [under, { $exists: '$r.value.embedding' }] }),
428
429
  ours: counted({ $and: [under, { $exists: '$r.value.embedding' }, ...mine] }),
@@ -433,12 +434,12 @@ export async function createDbStorage({ path = ':memory:', dims } = {}) {
433
434
  };
434
435
 
435
436
  return {
436
- mutate: async (prefix, transform) => store.transaction((tx) => {
437
- const rows = tx.sync.collection('slots');
437
+ mutate: async (prefix: any, transform: any) => store.transaction((tx) => {
438
+ const rows = tx.sync!.collection<{ key: string; value: any; }>('slots');
438
439
  const prefixes = typeof prefix === 'string' ? [prefix] : prefix.prefixes ?? [];
439
- const keys = [...new Set([...(prefix.keys ?? []), ...prefixes.flatMap((part) => many(rows.execute(forPrefix(part).keys)))])].sort();
440
- const matches = (key) => (prefix.keys ?? []).includes(key) || prefixes.some((part) => key.startsWith(part));
441
- const current = Object.fromEntries(keys.map((key) => [key, rows.get(key)?.value]));
440
+ const keys = [...new Set([...(prefix.keys ?? []), ...prefixes.flatMap((part: any) => many(rows.execute(forPrefix(part).keys)))])].sort();
441
+ const matches = (key: any) => (prefix.keys ?? []).includes(key) || prefixes.some((part: any) => key.startsWith(part));
442
+ const current = Object.fromEntries(keys.map((key: any) => [key, rows.get(key)?.value]));
442
443
  for (const key of Object.keys(current)) if (current[key] === undefined) delete current[key];
443
444
  const outcome = transform(current);
444
445
  if (!outcome || typeof outcome.then === 'function') throw new TypeError('mutate callback must be synchronous');
@@ -450,9 +451,9 @@ export async function createDbStorage({ path = ':memory:', dims } = {}) {
450
451
  }
451
452
  return outcome.result;
452
453
  }, { mode: 'immediate' }),
453
- get: async (key) => (await slots.get(key))?.value,
454
- set: async (key, value) => { await slots.put({ key, value }); },
455
- delete: async (key) => { await slots.delete(key); },
454
+ get: async (key: any) => (await slots.get(key))?.value,
455
+ set: async (key: any, value: any) => { await slots.put({ key, value }); },
456
+ delete: async (key: any) => { await slots.delete(key); },
456
457
  // sorted, because the ledger reads listings, the goal archive and a
457
458
  // snapshot's entries in key order and its zero-padded sequences
458
459
  // exist so that order is chronological
@@ -465,12 +466,12 @@ export async function createDbStorage({ path = ':memory:', dims } = {}) {
465
466
  * paid only when the counts prove a mixture, which is the one case
466
467
  * that is about to refuse anyway.
467
468
  */
468
- rank: async ({ prefix, vector, model, dims: width, limit }) => {
469
+ rank: async ({ prefix, vector, model, dims: width, limit }: { prefix?: any; vector?: any; model?: any; dims?: any; limit?: any; }) => {
469
470
  const docs = forPrefix(prefix);
470
471
  const externals = { q: vector, model, dims: width };
471
472
  const hits = many(await slots.execute(
472
473
  limit === undefined ? docs.ranked : docs.window(limit), { externals }));
473
- const skipped = await slots.execute(docs.skipped);
474
+ const skipped = await slots.execute(docs.skipped) as number;
474
475
  const held = await slots.execute(docs.held);
475
476
  const ours = await slots.execute(docs.ours, { externals });
476
477
  const identities = held === ours
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangleai/context",
3
- "version": "0.21.1",
3
+ "version": "0.24.1",
4
4
  "description": "Evidence-backed ledgers, bounded environments, recall, retention and storage contracts.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -74,9 +74,9 @@
74
74
  },
75
75
  "sideEffects": false,
76
76
  "dependencies": {
77
- "@jarenjs/core": "0.84.3",
78
- "@jarenjs/validate": "0.84.3",
79
- "@tangleai/models": "^0.21.1"
77
+ "@jarenjs/core": "0.86.0",
78
+ "@jarenjs/validate": "0.86.0",
79
+ "@tangleai/models": "^0.24.1"
80
80
  },
81
81
  "private": false,
82
82
  "files": [
package/src/archive.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  /** Exact bytes of the archive sub-map, including durable loss metadata. */
2
- export function archiveFootprint(records: any): {
2
+ export declare function archiveFootprint(records: any): {
3
3
  items: number;
4
4
  bytes: number;
5
5
  };
6
6
  /** Plan deterministic oldest-unreferenced eviction, or refuse without mutation. */
7
- export function retainArchives(current: any, added: any, limits: any, options?: {}): {
7
+ export declare function retainArchives(current: Record<string, any>, added: any, limits: any, options?: Record<string, any>): {
8
8
  error: string;
9
9
  code: string;
10
10
  retention?: undefined;
@@ -30,7 +30,7 @@ export function retainArchives(current: any, added: any, limits: any, options?:
30
30
  report?: undefined;
31
31
  footprint?: undefined;
32
32
  } | {
33
- next: any;
33
+ next: Record<string, any>;
34
34
  report: {
35
35
  version: number;
36
36
  policy: string;
package/src/archive.js CHANGED
@@ -1,53 +1,58 @@
1
- //@ts-check
2
- import { jsonBytes } from './retention.js';
1
+ import { jsonBytes } from "./retention.js";
3
2
  const SLOT = 'ai/state/slot/', CONTENT = 'ai/state/slot-content/';
4
3
  const EVICTED = 'ai/state/evicted/', REPORT = 'ai/state/retention/archive';
5
4
  const archived = (value) => value?.kind === 'agent-round' || value?.kind === 'agent-round-index';
6
-
7
5
  /** Exact bytes of the archive sub-map, including durable loss metadata. */
8
6
  export function archiveFootprint(records) {
9
- const names = Object.keys(records).filter((key) => key.startsWith(SLOT) && archived(records[key]));
10
- const keys = new Set(names.flatMap((key) => [key, `${CONTENT}${records[key].name}`]));
11
- for (const key of Object.keys(records)) if (key.startsWith(EVICTED) || key === REPORT) keys.add(key);
12
- return { items: names.length, bytes: jsonBytes(Object.fromEntries([...keys].sort().map((key) => [key, records[key]]))) };
7
+ const names = Object.keys(records).filter((key) => key.startsWith(SLOT) && archived(records[key]));
8
+ const keys = new Set(names.flatMap((key) => [key, `${CONTENT}${records[key].name}`]));
9
+ for (const key of Object.keys(records))
10
+ if (key.startsWith(EVICTED) || key === REPORT)
11
+ keys.add(key);
12
+ return { items: names.length, bytes: jsonBytes(Object.fromEntries([...keys].sort().map((key) => [key, records[key]]))) };
13
13
  }
14
-
15
14
  /** Plan deterministic oldest-unreferenced eviction, or refuse without mutation. */
16
15
  export function retainArchives(current, added, limits, options = {}) {
17
- const next = structuredClone(current);
18
- const protectedNames = new Set([...(options.protectedNames ?? []), ...added.map((entry) => entry.slot.name)]);
19
- const references = JSON.stringify(Object.entries(current).filter(([key]) =>
20
- key.startsWith('ai/state/memory/') || key.startsWith('ai/state/goal/')))
21
- + (options.referenceText ?? '');
22
- for (const { slot, text } of added) {
23
- if (options.immutable === true && Object.hasOwn(next, `${CONTENT}${slot.name}`)
24
- && next[`${CONTENT}${slot.name}`] !== text)
25
- return { error: `archive address collision at '${slot.name}'`, code: 'ARCHIVE_COLLISION' };
26
- next[`${SLOT}${slot.name}`] = slot;
27
- next[`${CONTENT}${slot.name}`] = text;
28
- delete next[`${EVICTED}${slot.name}`];
29
- }
30
- const evicted = [];
31
- const report = { version: 1, policy: 'oldest-unreferenced', evicted,
32
- written: added.map((entry) => entry.slot.name) };
33
- next[REPORT] = report;
34
- const candidates = Object.entries(next).filter(([key, slot]) => key.startsWith(SLOT) && archived(slot)
35
- && !slot.pinned && !protectedNames.has(slot.name) && !references.includes(slot.name))
36
- .sort(([, a], [, b]) => a.at < b.at ? -1 : a.at > b.at ? 1 : a.name < b.name ? -1 : 1);
37
- const fits = () => {
38
- const footprint = archiveFootprint(next);
39
- return footprint.items <= (limits.maxItems ?? Infinity) && footprint.bytes <= (limits.maxBytes ?? Infinity);
40
- };
41
- for (const [, slot] of candidates) {
42
- if (fits()) break;
43
- const keys = [`${SLOT}${slot.name}`, `${CONTENT}${slot.name}`];
44
- const bytes = jsonBytes(Object.fromEntries(keys.map((key) => [key, next[key]])));
45
- const tombstone = { version: 1, name: slot.name, status: 'evicted', bytes, reason: 'archive-budget' };
46
- for (const key of keys) delete next[key];
47
- next[`${EVICTED}${slot.name}`] = tombstone;
48
- evicted.push(tombstone);
49
- }
50
- if (!fits()) return { error: 'archive budget cannot preserve protected addresses and loss metadata',
51
- code: 'ARCHIVE_BUDGET', retention: { refused: true, limits, current: archiveFootprint(current), attempted: archiveFootprint(next) } };
52
- return { next, report, footprint: archiveFootprint(next) };
16
+ const next = structuredClone(current);
17
+ const protectedNames = new Set([...(options.protectedNames ?? []), ...added.map((entry) => entry.slot.name)]);
18
+ const references = JSON.stringify(Object.entries(current).filter(([key]) => key.startsWith('ai/state/memory/') || key.startsWith('ai/state/goal/')))
19
+ + (options.referenceText ?? '');
20
+ for (const { slot, text } of added) {
21
+ if (options.immutable === true && Object.hasOwn(next, `${CONTENT}${slot.name}`)
22
+ && next[`${CONTENT}${slot.name}`] !== text)
23
+ return { error: `archive address collision at '${slot.name}'`, code: 'ARCHIVE_COLLISION' };
24
+ next[`${SLOT}${slot.name}`] = slot;
25
+ next[`${CONTENT}${slot.name}`] = text;
26
+ delete next[`${EVICTED}${slot.name}`];
27
+ }
28
+ const evicted = [];
29
+ const report = {
30
+ version: 1, policy: 'oldest-unreferenced', evicted,
31
+ written: added.map((entry) => entry.slot.name)
32
+ };
33
+ next[REPORT] = report;
34
+ const candidates = Object.entries(next).filter(([key, slot]) => key.startsWith(SLOT) && archived(slot)
35
+ && !slot.pinned && !protectedNames.has(slot.name) && !references.includes(slot.name))
36
+ .sort(([, a], [, b]) => a.at < b.at ? -1 : a.at > b.at ? 1 : a.name < b.name ? -1 : 1);
37
+ const fits = () => {
38
+ const footprint = archiveFootprint(next);
39
+ return footprint.items <= (limits.maxItems ?? Infinity) && footprint.bytes <= (limits.maxBytes ?? Infinity);
40
+ };
41
+ for (const [, slot] of candidates) {
42
+ if (fits())
43
+ break;
44
+ const keys = [`${SLOT}${slot.name}`, `${CONTENT}${slot.name}`];
45
+ const bytes = jsonBytes(Object.fromEntries(keys.map((key) => [key, next[key]])));
46
+ const tombstone = { version: 1, name: slot.name, status: 'evicted', bytes, reason: 'archive-budget' };
47
+ for (const key of keys)
48
+ delete next[key];
49
+ next[`${EVICTED}${slot.name}`] = tombstone;
50
+ evicted.push(tombstone);
51
+ }
52
+ if (!fits())
53
+ return {
54
+ error: 'archive budget cannot preserve protected addresses and loss metadata',
55
+ code: 'ARCHIVE_BUDGET', retention: { refused: true, limits, current: archiveFootprint(current), attempted: archiveFootprint(next) }
56
+ };
57
+ return { next, report, footprint: archiveFootprint(next) };
53
58
  }
@@ -1,28 +1,60 @@
1
+ /**
2
+ * The environment: a corpus the agent works ON rather than reads.
3
+ *
4
+ * Everything before this file tried to make a long context fit. The
5
+ * ledger stopped compaction destroying what it cut, and that moved the
6
+ * needle question from 2.5% to fully recoverable — but a question over
7
+ * *everything at once* stayed unanswerable, because the answer was never
8
+ * about how well the transcript was summarised. It was about the corpus
9
+ * being in the transcript at all.
10
+ *
11
+ * So it is not. Content lives in named slots; the root model sees a
12
+ * **digest** — name, kind, size, count, one line of excerpt — and works
13
+ * by naming slots in operations. Five of them, and they are the ones the
14
+ * RLM paper reports its models discovering for themselves:
15
+ *
16
+ * peek metadata plus a head excerpt — the default view of anything
17
+ * chunk split a slot into addressable pieces, deterministically
18
+ * grep scan for a pattern, answer with ADDRESSES and match lines
19
+ * select run a query over a structured slot, store the result
20
+ * stat counts, sizes, shape — answers that need no model at all
21
+ *
22
+ * Three rules hold without exception, and the tests assert each one:
23
+ *
24
+ * - **No operation returns bulk content** (D2). Every result is capped
25
+ * by construction — excerpts, match counts, listed slots — so a
26
+ * result is the same size whether the slot holds 10 kB or 10 MB. The
27
+ * one call that returns content is `read`, which requires an explicit
28
+ * character budget, and it exists for a sub-call payload or a user
29
+ * asking, not for the root's convenience.
30
+ * - **The root view is constant-size.** The digest lists at most
31
+ * `digestSlots` slots and says how many it did not list. That cap is
32
+ * what makes the claim true rather than clever: a corpus three orders
33
+ * of magnitude larger produces the same-size root request, and the
34
+ * model narrows with `grep` and `stat` instead of with a longer list.
35
+ * - **Addressing is derived, never stored.** A chunk's name is a pure
36
+ * function of its parent, the strategy and its index, so chunking the
37
+ * same slot twice writes the same slots instead of a second copy.
38
+ *
39
+ * Storage and the query compiler are injected (D3) — the same seams the
40
+ * ledger takes, because this IS the ledger's slot kind with operations
41
+ * over it rather than a second store.
42
+ */
43
+ /** The kind a chunk slot is written under, so a digest can group them. */
44
+ export declare const CHUNK_KIND = "chunk";
1
45
  /**
2
46
  * The address of one chunk: parent, strategy, size, index. Derived, so
3
47
  * the same split always names the same slots — that is what makes
4
48
  * re-chunking idempotent rather than duplicating, and it is why nothing
5
49
  * here keeps a mapping from a parent to its pieces.
6
- * @param {string} parent
7
- * @param {string} strategy
8
- * @param {number} size
9
- * @param {number} index
10
- * @returns {string}
11
50
  */
12
- export function chunkSlotName(parent: string, strategy: string, size: number, index: number): string;
51
+ export declare function chunkSlotName(parent: string, strategy: string, size: number, index: number): string;
13
52
  /** The prefix every chunk of one split shares — a family, addressable as one. */
14
- export function chunkFamily(parent: any, strategy: any, size: any): string;
53
+ export declare function chunkFamily(parent: any, strategy: any, size: any): string;
15
54
  /**
16
55
  * Create an environment over a slot store.
17
56
  *
18
- * @param {{ ledger?: any,
19
- * storage?: { get: (key: string) => Promise<any>,
20
- * set: (key: string, value: any) => Promise<void>,
21
- * delete: (key: string) => Promise<void>,
22
- * keys: (prefix?: string) => Promise<string[]> },
23
- * compileQuery?: (document: any) => (data: any) => any,
24
- * excerptChars?: number, digestSlots?: number, matchLimit?: number,
25
- * chunkSize?: number, now?: () => string }} [options]
57
+ * @param [options]
26
58
  * - `ledger` shares an existing ledger — the normal case, because the
27
59
  * agent's archived rounds and the corpus then live in one store and
28
60
  * one `recall` reaches both. Given `storage` instead, a ledger is
@@ -30,9 +62,9 @@ export function chunkFamily(parent: any, strategy: any, size: any): string;
30
62
  * - `compileQuery` is the `select` seam (`compileJsonQuery` from
31
63
  * `@jarenjs/json/query`). Absent, `select` declines with a stated
32
64
  * reason and every other operation is unaffected.
33
- * @returns {any}
34
65
  */
35
- export function createEnvironment(options?: {
66
+ export declare function createEnvironment(options?: {
67
+ scope?: string;
36
68
  ledger?: any;
37
69
  storage?: {
38
70
  get: (key: string) => Promise<any>;
@@ -58,9 +90,7 @@ export function createEnvironment(options?: {
58
90
  * Every schema is deliberately small — one required string, optional
59
91
  * numbers — because the tier this package targets gets a tool call right
60
92
  * in proportion to how few decisions it has to make.
61
- * @param {any} environment - from {@link createEnvironment}
62
- * @returns {any[]} tool definitions
93
+ * @param environment - from {@link createEnvironment}
94
+ * @returns tool definitions
63
95
  */
64
- export function environmentTools(environment: any): any[];
65
- /** The kind a chunk slot is written under, so a digest can group them. */
66
- export const CHUNK_KIND: "chunk";
96
+ export declare function environmentTools(environment: any): any[];