@avantf/dsh-mem 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -28
- package/cordis.patch.yml +17 -0
- package/lib/client.js +155 -14
- package/lib/engine/mem/config/config_files.d.ts +26 -0
- package/lib/engine/mem/config/domains.d.ts +20 -0
- package/lib/engine/mem/config/loader.d.ts +35 -0
- package/lib/engine/mem/config/paths.d.ts +53 -0
- package/lib/engine/mem/db/chunk.d.ts +26 -0
- package/lib/engine/mem/db/conn.d.ts +13 -0
- package/lib/engine/mem/db/dao/chunks.d.ts +150 -0
- package/lib/engine/mem/db/dao/contradictions.d.ts +98 -0
- package/lib/engine/mem/db/dao/documents.d.ts +51 -0
- package/lib/engine/mem/db/dao/entities.d.ts +67 -0
- package/lib/engine/mem/db/dao/facts.d.ts +472 -0
- package/lib/engine/mem/db/dao/stats.d.ts +14 -0
- package/lib/engine/mem/db/dao/triples.d.ts +80 -0
- package/lib/engine/mem/db/hash.d.ts +2 -0
- package/lib/engine/mem/db/knowledge.d.ts +17 -0
- package/lib/engine/mem/db/migrations.d.ts +71 -0
- package/lib/engine/mem/db/port.d.ts +57 -0
- package/lib/engine/mem/db/schema.d.ts +26 -0
- package/lib/engine/mem/db/sqlite.d.ts +89 -0
- package/lib/engine/mem/db/store.d.ts +35 -0
- package/lib/engine/mem/db/tokenizer.d.ts +61 -0
- package/lib/engine/mem/db/vectors.d.ts +50 -0
- package/lib/engine/mem/dispatch.d.ts +51 -0
- package/lib/engine/mem/entities/extract.d.ts +84 -0
- package/lib/engine/mem/eval/loader.d.ts +19 -0
- package/lib/engine/mem/eval/metrics.d.ts +25 -0
- package/lib/engine/mem/eval/runner.d.ts +10 -0
- package/lib/engine/mem/eval/write_metrics.d.ts +88 -0
- package/lib/engine/mem/git.d.ts +73 -0
- package/lib/engine/mem/hrr/algebra.d.ts +30 -0
- package/lib/engine/mem/hrr/atoms.d.ts +22 -0
- package/lib/engine/mem/hrr/encode.d.ts +32 -0
- package/lib/engine/mem/hrr/index.d.ts +3 -0
- package/lib/engine/mem/index.d.ts +27 -0
- package/lib/engine/mem/lifecycle/contradiction.d.ts +156 -0
- package/lib/engine/mem/lifecycle/index.d.ts +5 -0
- package/lib/engine/mem/lifecycle/maintenance.d.ts +48 -0
- package/lib/engine/mem/lifecycle/presence.d.ts +29 -0
- package/lib/engine/mem/lifecycle/tick.d.ts +43 -0
- package/lib/engine/mem/lifecycle/trust.d.ts +90 -0
- package/lib/engine/mem/modelBootstrap.d.ts +207 -0
- package/lib/engine/mem/router.d.ts +51 -0
- package/lib/engine/mem/runtime.d.ts +232 -0
- package/lib/engine/mem/store/common.d.ts +82 -0
- package/lib/engine/mem/store/doc_files.d.ts +125 -0
- package/lib/engine/mem/store/document_text.d.ts +47 -0
- package/lib/engine/mem/store/floors.d.ts +191 -0
- package/lib/engine/mem/store/hybrid.d.ts +232 -0
- package/lib/engine/mem/store/ingest_guard.d.ts +25 -0
- package/lib/engine/mem/store/knowledge.d.ts +483 -0
- package/lib/engine/mem/store/lexical.d.ts +67 -0
- package/lib/engine/mem/store/memory.d.ts +543 -0
- package/lib/engine/mem/store/source_picker.d.ts +43 -0
- package/lib/engine/mem-contract/config.d.ts +129 -0
- package/lib/engine/mem-contract/env.d.ts +31 -0
- package/lib/engine/mem-contract/family.d.ts +25 -0
- package/lib/engine/mem-contract/index.d.ts +8 -0
- package/lib/engine/mem-contract/log.d.ts +29 -0
- package/lib/engine/mem-contract/remote.d.ts +80 -0
- package/lib/engine/mem-contract/text.d.ts +92 -0
- package/lib/engine/mem-contract/tools.d.ts +284 -0
- package/lib/engine/mem-contract/types.d.ts +615 -0
- package/lib/engine/mem-retrieval/adapters/auto_vstore.d.ts +47 -0
- package/lib/engine/mem-retrieval/adapters/hnswlib.d.ts +174 -0
- package/lib/engine/mem-retrieval/adapters/local_bge.d.ts +121 -0
- package/lib/engine/mem-retrieval/adapters/local_numpy.d.ts +28 -0
- package/lib/engine/mem-retrieval/adapters/local_reranker.d.ts +52 -0
- package/lib/engine/mem-retrieval/adapters/none_reranker.d.ts +10 -0
- package/lib/engine/mem-retrieval/adapters/warm_gate.d.ts +46 -0
- package/lib/engine/mem-retrieval/budget.d.ts +30 -0
- package/lib/engine/mem-retrieval/fusion.d.ts +53 -0
- package/lib/engine/mem-retrieval/index.d.ts +14 -0
- package/lib/engine/mem-retrieval/interfaces.d.ts +82 -0
- package/lib/engine/mem-retrieval/log.d.ts +11 -0
- package/lib/engine/mem-retrieval/registry.d.ts +23 -0
- package/lib/engine/mem-retrieval/rerank.d.ts +13 -0
- package/lib/engine/mem-retrieval/stats.d.ts +64 -0
- package/lib/engine/mem-retrieval/text_budget.d.ts +52 -0
- package/lib/index.js +1773 -473
- package/lib/interface-version.json +2 -2
- package/lib/types/client/domains.d.ts +5 -4
- package/lib/types/client/domains.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/ingest.d.ts +1 -1
- package/lib/types/client/wire.d.ts +36 -0
- package/lib/types/client/wire.d.ts.map +1 -0
- package/lib/types/envinit-bootstrap.d.ts +3 -2
- package/lib/types/envinit.d.ts +2 -19
- package/lib/types/envinit.d.ts.map +1 -1
- package/lib/types/hints.d.ts +1 -1
- package/lib/types/index.d.ts +8 -3
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/open.d.ts +61 -11
- package/lib/types/open.d.ts.map +1 -1
- package/lib/types/prompt.d.ts +19 -1
- package/lib/types/prompt.d.ts.map +1 -1
- package/lib/types/provision.d.ts +1 -1
- package/lib/types/provision.d.ts.map +1 -1
- package/lib/types/reconcile.d.ts +64 -0
- package/lib/types/reconcile.d.ts.map +1 -0
- package/lib/types/remote.d.ts +53 -0
- package/lib/types/remote.d.ts.map +1 -1
- package/lib/types/render.d.ts +35 -0
- package/lib/types/render.d.ts.map +1 -0
- package/lib/types/tool_schema.d.ts +3 -13
- package/lib/types/tool_schema.d.ts.map +1 -1
- package/package.json +24 -15
- package/lib/client.js.map +0 -1
- package/lib/types/client/domains.js +0 -25
- package/lib/types/client/domains.js.map +0 -1
- package/lib/types/client/index.js +0 -1371
- package/lib/types/client/index.js.map +0 -1
- package/lib/types/client/ingest.js +0 -30
- package/lib/types/client/ingest.js.map +0 -1
- package/lib/types/client/paging.js +0 -36
- package/lib/types/client/paging.js.map +0 -1
- package/lib/types/client/summarize.js +0 -79
- package/lib/types/client/summarize.js.map +0 -1
- package/lib/types/data_home.js +0 -26
- package/lib/types/data_home.js.map +0 -1
- package/lib/types/envinit-bootstrap.js +0 -333
- package/lib/types/envinit.js +0 -402
- package/lib/types/envinit.js.map +0 -1
- package/lib/types/hints.js +0 -53
- package/lib/types/hints.js.map +0 -1
- package/lib/types/index.js +0 -755
- package/lib/types/index.js.map +0 -1
- package/lib/types/inject.js +0 -14
- package/lib/types/inject.js.map +0 -1
- package/lib/types/interface_gate.js +0 -84
- package/lib/types/interface_gate.js.map +0 -1
- package/lib/types/open.js +0 -133
- package/lib/types/open.js.map +0 -1
- package/lib/types/prompt.js +0 -148
- package/lib/types/prompt.js.map +0 -1
- package/lib/types/provision.js +0 -177
- package/lib/types/provision.js.map +0 -1
- package/lib/types/remote.js +0 -247
- package/lib/types/remote.js.map +0 -1
- package/lib/types/tool_schema.js +0 -149
- package/lib/types/tool_schema.js.map +0 -1
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import type { AvantfRuntime } from './runtime.js';
|
|
2
|
+
/** The statuses the base's verdict can carry (kept structural — see the note above). */
|
|
3
|
+
export type DshCompatStatus = 'ok' | 'version-mismatch' | 'version-unknown' | 'probe-skipped' | 'probe-failed';
|
|
4
|
+
/**
|
|
5
|
+
* The slice of the base's verdict this package reads.
|
|
6
|
+
*
|
|
7
|
+
* `load: false` means a probe PROVED the host API incompatible (only `probe-failed`); a version
|
|
8
|
+
* difference is a warning, never a skip. Structural on purpose: a full `CompatVerdict` from the base
|
|
9
|
+
* satisfies it, and this package needs no import of (or dependency on) the base to say so.
|
|
10
|
+
*/
|
|
11
|
+
export interface DshCompatVerdict {
|
|
12
|
+
readonly load: boolean;
|
|
13
|
+
readonly status: DshCompatStatus;
|
|
14
|
+
readonly reason: string;
|
|
15
|
+
}
|
|
16
|
+
/** Options for {@link warmModels}. */
|
|
17
|
+
export interface WarmOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Resolve before the tokenizer parse starts. The DSH plugin passes its host's readiness signal
|
|
20
|
+
* (`loader.await()`), the same one that gates the `dsh web` URL line, so the synchronous parse
|
|
21
|
+
* runs once the host is up instead of delaying it.
|
|
22
|
+
*/
|
|
23
|
+
waitFor?: Promise<unknown>;
|
|
24
|
+
/**
|
|
25
|
+
* Hard cap on {@link waitFor} (default {@link WAIT_FOR_HOST_MS}).
|
|
26
|
+
*
|
|
27
|
+
* The host's own readiness signal is not bounded by the host (its URL print relies on the same
|
|
28
|
+
* promise), so a loader row that never settles would leave the tokenizer un-warmed for the whole
|
|
29
|
+
* process life — every first query then pays the ~1.2 s. Bounded here on principle, and
|
|
30
|
+
* injectable so a caller can shorten it without waiting the real interval.
|
|
31
|
+
*/
|
|
32
|
+
waitForCapMs?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Wait for an idle event loop before loading nodejieba (the host-boot path). Off for the CLI,
|
|
35
|
+
* which calls `warmModels` inline: there the process is already idle and the gate would only add
|
|
36
|
+
* its quiet window to every command. The name is tokenizer-shaped for history, but the gate is the
|
|
37
|
+
* general "do not run synchronous startup mission during the boot burst" one — the plugin's
|
|
38
|
+
* mount-time entity sweep passes the same flag through {@link awaitStartupGate}.
|
|
39
|
+
*/
|
|
40
|
+
deferTokenizerUntilIdle?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Fire-and-forget model bootstrap for DSH startup. Configures the semantic /
|
|
44
|
+
* rerank model env (mirror + cache) and triggers the model download/load
|
|
45
|
+
* asynchronously, without blocking the plugin's `apply`. Returns the availability
|
|
46
|
+
* of the semantic path once the download/load completes.
|
|
47
|
+
*
|
|
48
|
+
* Every stage logs through the runtime logger so a plugin mount is traceable in
|
|
49
|
+
* the DSH host output: warm start (mirror/cache/model), then ready or degraded.
|
|
50
|
+
*
|
|
51
|
+
* @param rt - the runtime whose memory store owns the semantic backend.
|
|
52
|
+
* @param options - host-readiness and idle-gating options (see {@link WarmOptions}).
|
|
53
|
+
* @param reportFinal - log the one-line verdict (`true`, the default). The provisioning sweep turns
|
|
54
|
+
* the same verdict into the artifact's result line and would otherwise print it twice; the CLI
|
|
55
|
+
* path keeps it, because there the caller only sees this logger.
|
|
56
|
+
*/
|
|
57
|
+
export declare function warmModels(rt: AvantfRuntime, options?: WarmOptions, reportFinal?: boolean): Promise<boolean>;
|
|
58
|
+
/**
|
|
59
|
+
* Warm ONLY the embedding model: load it from `semantic.cache_dir` and mark the engine ready.
|
|
60
|
+
*
|
|
61
|
+
* Split out of {@link warmModels} for the plugin's envinit path. There the framework fetches the
|
|
62
|
+
* model in the BACKGROUND, so the warm has to wait for the item to settle instead of running at
|
|
63
|
+
* mount — and the runtime's own constructor warm is deferred for exactly that reason
|
|
64
|
+
* (`RuntimeOptions.managedRoots` → `deferWarm`), which is what stops it racing the install and
|
|
65
|
+
* leaving a second copy behind. While the item is unsettled NOTHING warms; this function is the
|
|
66
|
+
* explicit "files are there now, look" step, and it still runs when the item skipped/failed so
|
|
67
|
+
* `semantic.auto_download` can decide between the runtime's own fetch and degradation.
|
|
68
|
+
*
|
|
69
|
+
* @param rt - the runtime whose memory store owns the semantic backend.
|
|
70
|
+
* @param reportFinal - log the one-line verdict (default `true`).
|
|
71
|
+
* @returns whether the semantic path is available.
|
|
72
|
+
*/
|
|
73
|
+
export declare function warmSemantic(rt: AvantfRuntime, reportFinal?: boolean): Promise<boolean>;
|
|
74
|
+
/**
|
|
75
|
+
* Wait out the startup gate — the host readiness signal (bounded by {@link WAIT_FOR_HOST_MS}) and,
|
|
76
|
+
* when asked, one quiet turn of the event loop.
|
|
77
|
+
*
|
|
78
|
+
* Extracted so there is exactly ONE definition of "a job that must not run inside the host's boot
|
|
79
|
+
* window". The tokenizer warm below uses it, and so does the plugin's mount-time entity sweep:
|
|
80
|
+
* `reindexEntities` tags stale rows through nodejieba, whose dictionary parse is the same ~1.2 s
|
|
81
|
+
* SYNCHRONOUS mission the warm defers, so a sweep fired at mount (before this) could pull that parse
|
|
82
|
+
* back into the startup window through a second door (performance review §7.5 / P5).
|
|
83
|
+
*
|
|
84
|
+
* The waits are NOT preconditions: a host whose boot failed still resolves them, and a host whose
|
|
85
|
+
* boot hung is bounded here rather than trusted (see {@link WarmOptions.waitForCapMs}).
|
|
86
|
+
*
|
|
87
|
+
* @param options - the same options the tokenizer warm takes (the idle gate is opt-in).
|
|
88
|
+
* @returns how long the idle gate waited (ms), or `0` when it was not requested.
|
|
89
|
+
*/
|
|
90
|
+
export declare function awaitStartupGate(options?: WarmOptions): Promise<number>;
|
|
91
|
+
/**
|
|
92
|
+
* Warm ONLY the tokenizer, after the host-readiness signal and (when asked) an idle event loop.
|
|
93
|
+
*
|
|
94
|
+
* nodejieba loads its dictionary lazily on the FIRST `tag()` (measured ~950 ms, after which a call
|
|
95
|
+
* is 0.1 ms), so without this the first write/query of a session pays it while the user waits. Its
|
|
96
|
+
* cost belongs to startup, and an unavailable jieba is worth saying out loud — entity extraction
|
|
97
|
+
* silently degrades to the regex fallback (weaker entity/jaccard legs).
|
|
98
|
+
*
|
|
99
|
+
* @param rt - the runtime whose logger reports the outcome.
|
|
100
|
+
* @param options - the host-readiness signal and the idle gate (see {@link WarmOptions}).
|
|
101
|
+
*/
|
|
102
|
+
export declare function warmTokenizer(rt: AvantfRuntime, options?: WarmOptions): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Non-blocking embedding warm, for a caller that knows the model files are already on disk.
|
|
105
|
+
*
|
|
106
|
+
* The plugin's envinit path calls this from the model item's `onSettled`, which is the "continue
|
|
107
|
+
* initialisation once the background resource is there" step (framework DESIGN §7.2 step 5).
|
|
108
|
+
*
|
|
109
|
+
* @param rt - the runtime to warm.
|
|
110
|
+
* @param reportFinal - log the one-line verdict (default `true`).
|
|
111
|
+
*/
|
|
112
|
+
export declare function warmSemanticAsync(rt: AvantfRuntime, reportFinal?: boolean): void;
|
|
113
|
+
/**
|
|
114
|
+
* Non-blocking tokenizer warm for the plugin startup path: the host is still mounting plugins and
|
|
115
|
+
* composing its client bundles, so the synchronous dictionary parse waits for the host's readiness
|
|
116
|
+
* signal (when one is available) and then for a quiet turn, instead of competing with the boot.
|
|
117
|
+
*
|
|
118
|
+
* @param rt - the runtime to warm.
|
|
119
|
+
* @param options - the host-readiness signal and the idle gate (see {@link WarmOptions}).
|
|
120
|
+
*/
|
|
121
|
+
export declare function warmTokenizerAsync(rt: AvantfRuntime, options?: WarmOptions): void;
|
|
122
|
+
/**
|
|
123
|
+
* Options for {@link provisionToolchainAsync}: the warm-up options plus the host-side dsh verdict.
|
|
124
|
+
*/
|
|
125
|
+
export interface ProvisionSweepOptions extends WarmOptions {
|
|
126
|
+
/**
|
|
127
|
+
* The verdict the DSH plugin ALREADY computed (see `packages/plugin/src/provision.ts`), as plain
|
|
128
|
+
* data. The plugin computes it once and this package reads it — no second derivation, and no
|
|
129
|
+
* import of the base. Absent for callers with no dsh context (the standalone MCP entry), in which
|
|
130
|
+
* case no gate runs.
|
|
131
|
+
*/
|
|
132
|
+
dshCompat?: DshCompatVerdict;
|
|
133
|
+
}
|
|
134
|
+
/** Outcome of {@link provisionToolchainAsync}. */
|
|
135
|
+
export interface ProvisionSweepResult {
|
|
136
|
+
/**
|
|
137
|
+
* `true` when the sweep did NOT run because the host dsh is incompatible: no artifact was
|
|
138
|
+
* resolved, installed or warmed. `false` means the sweep was dispatched as usual.
|
|
139
|
+
*/
|
|
140
|
+
skipped: boolean;
|
|
141
|
+
/** Verdict status when the gate ran (`probe-failed` is the only value that skips). */
|
|
142
|
+
status?: DshCompatStatus;
|
|
143
|
+
/** Why it was skipped (the proven incompatibility detail). */
|
|
144
|
+
reason?: string;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The ONE startup initialization for this plugin: register every artifact this build needs, then run
|
|
148
|
+
* the provisioning sweep (non-blocking).
|
|
149
|
+
*
|
|
150
|
+
* It replaced a one-shot model warm-up that sat beside a separate tools sweep, which meant two
|
|
151
|
+
* initialization paths, two logging vocabularies, and no single answer to "is this host fully
|
|
152
|
+
* provisioned?". Callers (the DSH plugin mount, the MCP entry) now make one call, and the per-artifact
|
|
153
|
+
* result lines in the host log come from the same mechanism that installs pandoc.
|
|
154
|
+
*
|
|
155
|
+
* The FIRST step is the dsh compatibility gate: an incompatible host means NOTHING is resolved,
|
|
156
|
+
* installed or warmed — preparing an environment for an API we cannot drive is wasted mission.
|
|
157
|
+
*
|
|
158
|
+
* @param rt - the runtime, whose resolved `tools` config and logger the sweep uses.
|
|
159
|
+
* @param options - the host's readiness signal and the dsh compatibility evidence.
|
|
160
|
+
* @returns whether the sweep ran or was skipped for incompatibility.
|
|
161
|
+
*/
|
|
162
|
+
export declare function provisionToolchainAsync(rt: AvantfRuntime, options?: ProvisionSweepOptions): Promise<ProvisionSweepResult>;
|
|
163
|
+
/** The artifact id, used in the startup log and in the model artifact's errors. */
|
|
164
|
+
export declare const MODEL_ARTIFACT_ID = "model";
|
|
165
|
+
/** The environment a model-artifact call runs under: the runtime that owns the model cache. */
|
|
166
|
+
export interface ModelArtifactEnv {
|
|
167
|
+
runtime: AvantfRuntime;
|
|
168
|
+
}
|
|
169
|
+
/** The slice of an artifact call this module needs (`@avantf/mem-provision`'s InstallContext). */
|
|
170
|
+
interface ModelInstallContext {
|
|
171
|
+
logger: {
|
|
172
|
+
info(message: string): void;
|
|
173
|
+
warn(message: string): void;
|
|
174
|
+
error(message: string): void;
|
|
175
|
+
};
|
|
176
|
+
artifactEnv?: unknown;
|
|
177
|
+
config: {
|
|
178
|
+
auto_install: boolean;
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* The embedding model (plus the nodejieba tokenizer it shares its warm-up budget with) as an
|
|
183
|
+
* ARTIFACT, so the startup sweep in `@avantf/mem-provision` is the only initialization path.
|
|
184
|
+
*
|
|
185
|
+
* It used to be a parallel model warm-up call beside the tools sweep, which meant two
|
|
186
|
+
* startup mechanisms with two logging vocabularies and no single place that answered "is this host
|
|
187
|
+
* fully provisioned?". The model has no pinned version and no managed directory of its own ON THIS
|
|
188
|
+
* (the flat `<repo>/<file>` layout the transformers.js cache reads), so it
|
|
189
|
+
* declares neither; its `isPresent` is `false` because "is the model warm?" is not a question that
|
|
190
|
+
* can be answered without attempting the warm-up, and the warm-up is cheap when the cache is
|
|
191
|
+
* already there (measured ~1.2 s, in the background). The DSH plugin uses this sweep only when the
|
|
192
|
+
* family framework is unavailable — with the framework up, `mem:model` owns the model root instead
|
|
193
|
+
* and this artifact is not registered.
|
|
194
|
+
*/
|
|
195
|
+
export declare const modelArtifact: {
|
|
196
|
+
id: string;
|
|
197
|
+
title: string;
|
|
198
|
+
verb: string;
|
|
199
|
+
isPresent(): Promise<boolean>;
|
|
200
|
+
install(ctx: ModelInstallContext): Promise<void>;
|
|
201
|
+
verify(ctx: {
|
|
202
|
+
logger: ModelInstallContext["logger"];
|
|
203
|
+
}): Promise<void>;
|
|
204
|
+
};
|
|
205
|
+
/** Register the model artifact once per process (idempotent for a test that mounts twice). */
|
|
206
|
+
export declare function registerModelArtifact(): void;
|
|
207
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type RecallHit, type RecallResult, type RetrievalFloorDrops } from '../mem-contract/index.js';
|
|
2
|
+
interface CrossQueryOptions {
|
|
3
|
+
limit?: number;
|
|
4
|
+
kind?: 'all' | 'fact' | 'doc_chunk';
|
|
5
|
+
domain?: string;
|
|
6
|
+
source?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Token budget for the merged result; `0` = unlimited, omitted = the caller already bounded
|
|
9
|
+
* the legs (the runtime lifts the per-store budgets for the pool and bounds the OUTCOME here,
|
|
10
|
+
* because fusion can reorder which entries deserve the text).
|
|
11
|
+
*/
|
|
12
|
+
maxTokens?: number;
|
|
13
|
+
/**
|
|
14
|
+
* The knowledge leg's floor report. `KnowledgeStore.search` answers with hits only, so the
|
|
15
|
+
* runtime hands its captured `HybridResult` drops here; the memory side already arrives on
|
|
16
|
+
* `memory.dropped_by_floor`. Without this, the merged result would report only half the floors.
|
|
17
|
+
*/
|
|
18
|
+
kbDroppedByFloor?: RetrievalFloorDrops;
|
|
19
|
+
}
|
|
20
|
+
/** The memory search result the router derives degradation/weights from. */
|
|
21
|
+
type CrossQueryMemory = Pick<RecallResult, 'hits' | 'degraded' | 'weights' | 'floors' | 'dropped_by_floor'>;
|
|
22
|
+
/**
|
|
23
|
+
* Joint min-max normalization over the MERGED pool — the rule the cross-store score
|
|
24
|
+
* contract is defined by (DESIGN §7).
|
|
25
|
+
*
|
|
26
|
+
* Degenerate case (every candidate carries the same score — including a pool of one):
|
|
27
|
+
* the raw score is KEPT. A collapsed range encodes no ranking information, so rescaling
|
|
28
|
+
* it would invent a "perfect match" out of an absolute fused score that may say
|
|
29
|
+
* otherwise. `minMaxNormalize` maps the same case to 1 instead, which is right there
|
|
30
|
+
* because it only ever feeds a relative weighted sum inside ONE fusion path; the two
|
|
31
|
+
* differ deliberately rather than by oversight.
|
|
32
|
+
*
|
|
33
|
+
* Returns COPIES: the inputs are the stores' own result objects, and callers still
|
|
34
|
+
* read their original scores.
|
|
35
|
+
*/
|
|
36
|
+
export declare function normalizeMergedScores<T extends {
|
|
37
|
+
score: number;
|
|
38
|
+
}>(pool: T[]): T[];
|
|
39
|
+
/**
|
|
40
|
+
* Cross-retrieval router: merge memory + knowledge hits, joint min-max normalize
|
|
41
|
+
* the final fused scores over the *combined* pool (so the two stores' scores are
|
|
42
|
+
* comparable), then rank by score.
|
|
43
|
+
*
|
|
44
|
+
* Degradation and weights are inherited from the memory search result rather
|
|
45
|
+
* than hardcoded: both stores share the same semantic adapter, so
|
|
46
|
+
* `memory.degraded`/`memory.weights` already describe whether the semantic leg
|
|
47
|
+
* ran. Reporting a fixed `degraded: true` here made every `kb_query` look
|
|
48
|
+
* degraded even when embeddings were live.
|
|
49
|
+
*/
|
|
50
|
+
export declare function crossQuery(memory: CrossQueryMemory, kb: RecallHit[], opts?: CrossQueryOptions): RecallResult;
|
|
51
|
+
export {};
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import type { AdminRequest, KbAddRequest, KbConflictReport, KbRequest, QueryRequest, RecallRequest, RecallResult, RememberRequest } from '../mem-contract/index.js';
|
|
2
|
+
import { type AvantfLogger, type StatsSummary } from '../mem-contract/index.js';
|
|
3
|
+
import { type SemanticBackend } from '../mem-retrieval/index.js';
|
|
4
|
+
import { type LoadedConfig } from './config/loader.js';
|
|
5
|
+
import { type Db } from './db/conn.js';
|
|
6
|
+
import { type RelevanceHit } from './store/lexical.js';
|
|
7
|
+
import { MemoryStore } from './store/memory.js';
|
|
8
|
+
import { KnowledgeStore } from './store/knowledge.js';
|
|
9
|
+
export interface RuntimeOptions {
|
|
10
|
+
dataHome?: string;
|
|
11
|
+
memoryDbPath?: string;
|
|
12
|
+
/** Host logger; defaults to the prefixed console logger. */
|
|
13
|
+
logger?: AvantfLogger;
|
|
14
|
+
/**
|
|
15
|
+
* Override the semantic backend instead of resolving `semantic.backend` through the
|
|
16
|
+
* registry. A seam for tests and embedders, NOT a second pluggability path: the registry
|
|
17
|
+
* + config stays the documented way to swap backends (DESIGN §5). The override must
|
|
18
|
+
* produce vectors of `config.semantic.dim`, since the vector stores are built from it.
|
|
19
|
+
*/
|
|
20
|
+
semantic?: SemanticBackend;
|
|
21
|
+
/**
|
|
22
|
+
* The family framework's managed roots, as the built-in defaults for `tools.dir` and
|
|
23
|
+
* `semantic.cache_dir` / `rerank.cache_dir` (see `LoadConfigOptions.managedRoots`).
|
|
24
|
+
*
|
|
25
|
+
* The DSH plugin passes `<family home>/tools` and `<family home>/models` while
|
|
26
|
+
* `@avantf/dsh-plugin-base` owns the fetching; the CLI and the MCP server pass nothing, because they
|
|
27
|
+
* provision through `@avantf/mem-provision` as before.
|
|
28
|
+
*
|
|
29
|
+
* This also decides WHEN the local models warm: with managed roots the framework may still be
|
|
30
|
+
* writing the model into that root, so the backends are resolved with `deferWarm` and the plugin
|
|
31
|
+
* warms them once the framework's item settles. Without managed roots the adapters keep their
|
|
32
|
+
* eager constructor warm.
|
|
33
|
+
*/
|
|
34
|
+
managedRoots?: {
|
|
35
|
+
readonly tools?: string;
|
|
36
|
+
readonly models?: string;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The awaited result of a store method — what a dispatch overload promises.
|
|
41
|
+
*
|
|
42
|
+
* Every member below is written in terms of the METHOD that produces it rather than a type
|
|
43
|
+
* spelled out by hand. That is the whole point: an invented type (`Promise<RecallResult>` for
|
|
44
|
+
* `related`, which answers with entity counts) compiles perfectly and then lies to every caller,
|
|
45
|
+
* while `ReturnType<Store['method']>` cannot disagree with the store. The trailing general
|
|
46
|
+
* overloads keep dynamic dispatchers (the plugin's tool runner, MCP) compiling, where the request
|
|
47
|
+
* arrives as the whole union.
|
|
48
|
+
*
|
|
49
|
+
* Exported because the DSH plugin's Remote gateway is a dynamic dispatcher too, and it should be able
|
|
50
|
+
* to say what each key answers without inventing a shape: `StoreResult<MemoryStore, 'add'>` is the
|
|
51
|
+
* fact the store actually wrote, whatever the store's author later changes it to.
|
|
52
|
+
*/
|
|
53
|
+
export type StoreResult<S, M extends keyof S> = Awaited<ReturnType<Extract<S[M], (...args: never[]) => unknown>>>;
|
|
54
|
+
/**
|
|
55
|
+
* A caller-error payload — the shape every self-made refusal in the `switch` returns.
|
|
56
|
+
*
|
|
57
|
+
* Named so the return sites can assert it (`satisfies DispatchError`): these payloads are the
|
|
58
|
+
* only ones NOT produced by a store method, so they have no `ReturnType` to be tied to.
|
|
59
|
+
*/
|
|
60
|
+
export type DispatchError = {
|
|
61
|
+
error: string;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* `admin.stats`: the store counts plus the process-wide retrieval health block (DESIGN §20) — the one
|
|
65
|
+
* admin payload no store method produces, because the runtime composes it.
|
|
66
|
+
*/
|
|
67
|
+
export type AdminStatsResult = StatsSummary;
|
|
68
|
+
/**
|
|
69
|
+
* The payload of the four hits-returning recall actions, which one overload groups because a
|
|
70
|
+
* caller may hold them as a union (`action: 'chain' | 'reason'`).
|
|
71
|
+
*
|
|
72
|
+
* Grouping them is a CLAIM — that `probe`, `chain` and `reason` all answer like `search` — so it is
|
|
73
|
+
* proven rather than assumed: a type-level check asserts that each store method's result equals this
|
|
74
|
+
* one, and a divergence fails there instead of the grouped overload quietly lying about it. (Only
|
|
75
|
+
* `probe` holds by construction, because it calls `memory.search`.)
|
|
76
|
+
*/
|
|
77
|
+
export type RecallHitsResult = StoreResult<MemoryStore, 'search'>;
|
|
78
|
+
/**
|
|
79
|
+
* `remember` dispatches to five store operations whose RESULTS differ: `add`/`update` return the
|
|
80
|
+
* written fact, `remove` a flag, the feedback actions the new counter (or `null`). One plain
|
|
81
|
+
* signature would either say `unknown` — leaving every caller to invent a cast, which is how a
|
|
82
|
+
* caller ends up reading a field that does not exist — or a union that forces every caller to
|
|
83
|
+
* narrow first. The callable type says what each ACTION returns.
|
|
84
|
+
*/
|
|
85
|
+
export type RememberDispatch = {
|
|
86
|
+
(req: Extract<RememberRequest, {
|
|
87
|
+
action: 'add';
|
|
88
|
+
}>): Promise<StoreResult<MemoryStore, 'add'>>;
|
|
89
|
+
(req: Extract<RememberRequest, {
|
|
90
|
+
action: 'update';
|
|
91
|
+
}>): Promise<StoreResult<MemoryStore, 'update'>>;
|
|
92
|
+
(req: Extract<RememberRequest, {
|
|
93
|
+
action: 'remove';
|
|
94
|
+
}>): Promise<StoreResult<MemoryStore, 'remove'>>;
|
|
95
|
+
(req: Extract<RememberRequest, {
|
|
96
|
+
action: 'helpful' | 'unhelpful';
|
|
97
|
+
}>): Promise<StoreResult<MemoryStore, 'helpful'>>;
|
|
98
|
+
(req: RememberRequest): Promise<unknown>;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Same per-action idea as {@link RememberDispatch}. `ask` may also report a caller error, and
|
|
102
|
+
* `related` answers with ENTITY COUNTS rather than hits (`memory.related`) — a distinction that
|
|
103
|
+
* only showed up once this type existed, because the callers had been casting to whatever they
|
|
104
|
+
* assumed.
|
|
105
|
+
*/
|
|
106
|
+
export type RecallDispatch = {
|
|
107
|
+
(req: Extract<RecallRequest, {
|
|
108
|
+
action: 'search' | 'probe' | 'chain' | 'reason';
|
|
109
|
+
}>): Promise<RecallHitsResult>;
|
|
110
|
+
(req: Extract<RecallRequest, {
|
|
111
|
+
action: 'ask';
|
|
112
|
+
}>): Promise<StoreResult<MemoryStore, 'ask'> | DispatchError>;
|
|
113
|
+
(req: Extract<RecallRequest, {
|
|
114
|
+
action: 'related';
|
|
115
|
+
}>): Promise<StoreResult<MemoryStore, 'related'>>;
|
|
116
|
+
(req: Extract<RecallRequest, {
|
|
117
|
+
action: 'contradict';
|
|
118
|
+
}>): Promise<StoreResult<MemoryStore, 'listContradictions'>>;
|
|
119
|
+
(req: RecallRequest): Promise<unknown>;
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* `admin` is the widest dispatcher: counts, a fact page, a detail, flags, and five diagnostic
|
|
123
|
+
* reports. `ReturnType<…>` keeps each member tied to the store method that actually produces it,
|
|
124
|
+
* so a change there propagates instead of leaving callers with a cast they invented.
|
|
125
|
+
*/
|
|
126
|
+
export type AdminDispatch = {
|
|
127
|
+
(req: Extract<AdminRequest, {
|
|
128
|
+
action: 'stats';
|
|
129
|
+
}>): AdminStatsResult;
|
|
130
|
+
(req: Extract<AdminRequest, {
|
|
131
|
+
action: 'list';
|
|
132
|
+
}>): StoreResult<MemoryStore, 'list'>;
|
|
133
|
+
(req: Extract<AdminRequest, {
|
|
134
|
+
action: 'detail';
|
|
135
|
+
}>): StoreResult<MemoryStore, 'get'> | DispatchError;
|
|
136
|
+
(req: Extract<AdminRequest, {
|
|
137
|
+
action: 'archive';
|
|
138
|
+
}>): StoreResult<MemoryStore, 'archive'>;
|
|
139
|
+
(req: Extract<AdminRequest, {
|
|
140
|
+
action: 'restore';
|
|
141
|
+
}>): StoreResult<MemoryStore, 'restore'>;
|
|
142
|
+
(req: Extract<AdminRequest, {
|
|
143
|
+
action: 'pin';
|
|
144
|
+
}>): StoreResult<MemoryStore, 'pin'>;
|
|
145
|
+
(req: Extract<AdminRequest, {
|
|
146
|
+
action: 'unpin';
|
|
147
|
+
}>): StoreResult<MemoryStore, 'unpin'>;
|
|
148
|
+
(req: Extract<AdminRequest, {
|
|
149
|
+
action: 'trust_diagnose';
|
|
150
|
+
}>): StoreResult<MemoryStore, 'trustDiagnose'>;
|
|
151
|
+
(req: Extract<AdminRequest, {
|
|
152
|
+
action: 'vectors_diagnose';
|
|
153
|
+
}>): StoreResult<MemoryStore, 'vectorsDiagnose'>;
|
|
154
|
+
(req: Extract<AdminRequest, {
|
|
155
|
+
action: 'vectors_fix';
|
|
156
|
+
}>): StoreResult<MemoryStore, 'vectorsFix'>;
|
|
157
|
+
(req: Extract<AdminRequest, {
|
|
158
|
+
action: 'contradict_check';
|
|
159
|
+
}>): StoreResult<MemoryStore, 'checkContradictions'>;
|
|
160
|
+
(req: Extract<AdminRequest, {
|
|
161
|
+
action: 'contradict_resolve';
|
|
162
|
+
}>): StoreResult<MemoryStore, 'resolveContradiction'>;
|
|
163
|
+
(req: Extract<AdminRequest, {
|
|
164
|
+
action: 'maintenance';
|
|
165
|
+
}>): StoreResult<MemoryStore, 'maintenance'>;
|
|
166
|
+
(req: AdminRequest): unknown;
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* `kb_manage`: ingest/import report a batch, list/detail the corpus, remove a flag, reindex a
|
|
170
|
+
* plan. Members are pinned to the store methods that produce them (`ReturnType<…>`) so the
|
|
171
|
+
* shapes cannot drift into "whatever the caller assumed".
|
|
172
|
+
*
|
|
173
|
+
* `ingest`/`import` may instead answer with a {@link KbConflictReport}: this face is the UI/CLI
|
|
174
|
+
* REPLACE mode, where an unconfirmed collision writes nothing and comes back as a structured
|
|
175
|
+
* list. The add-only face (`kbAdd`) never returns one — it refuses.
|
|
176
|
+
*/
|
|
177
|
+
export type KbDispatch = {
|
|
178
|
+
(req: Extract<KbRequest, {
|
|
179
|
+
action: 'ingest';
|
|
180
|
+
}>): Promise<StoreResult<KnowledgeStore, 'ingest'> | KbConflictReport | DispatchError>;
|
|
181
|
+
(req: Extract<KbRequest, {
|
|
182
|
+
action: 'import';
|
|
183
|
+
}>): Promise<StoreResult<KnowledgeStore, 'importPaths'> | KbConflictReport>;
|
|
184
|
+
(req: Extract<KbRequest, {
|
|
185
|
+
action: 'list';
|
|
186
|
+
}>): Promise<StoreResult<KnowledgeStore, 'list'>>;
|
|
187
|
+
(req: Extract<KbRequest, {
|
|
188
|
+
action: 'detail';
|
|
189
|
+
}>): Promise<StoreResult<KnowledgeStore, 'detail'>>;
|
|
190
|
+
(req: Extract<KbRequest, {
|
|
191
|
+
action: 'remove';
|
|
192
|
+
}>): Promise<StoreResult<KnowledgeStore, 'remove'>>;
|
|
193
|
+
(req: Extract<KbRequest, {
|
|
194
|
+
action: 'reindex';
|
|
195
|
+
}>): Promise<StoreResult<KnowledgeStore, 'reindex'>>;
|
|
196
|
+
(req: KbRequest): Promise<unknown>;
|
|
197
|
+
};
|
|
198
|
+
export interface AvantfRuntime {
|
|
199
|
+
config: LoadedConfig;
|
|
200
|
+
db: Db;
|
|
201
|
+
memory: MemoryStore;
|
|
202
|
+
knowledge: KnowledgeStore;
|
|
203
|
+
/** Logger every startup/lifecycle line goes through. */
|
|
204
|
+
logger: AvantfLogger;
|
|
205
|
+
remember: RememberDispatch;
|
|
206
|
+
recall: RecallDispatch;
|
|
207
|
+
admin: AdminDispatch;
|
|
208
|
+
kb: KbDispatch;
|
|
209
|
+
/**
|
|
210
|
+
* The ADD-ONLY face of the same shared ingestion entry (`kb_add`): same plan/classify/dispatch,
|
|
211
|
+
* but a collision is refused instead of replaced. Kept as its own runtime method — not a field on
|
|
212
|
+
* the request — so the model-facing `KB_ADD_TOOL.input` cannot even express "replace".
|
|
213
|
+
*/
|
|
214
|
+
kbAdd(req: KbAddRequest): Promise<unknown>;
|
|
215
|
+
query(req: QueryRequest): Promise<RecallResult>;
|
|
216
|
+
/**
|
|
217
|
+
* Plugin-internal, NOT a tool and not model-facing: does either store already hold something
|
|
218
|
+
* relevant to `text`? A boolean (`store/lexical.ts`) because the one tool the hint names
|
|
219
|
+
* (`kb_query`) retrieves from both stores, so "which one matched" changes no downstream decision.
|
|
220
|
+
* Synchronous on purpose — the caller renders it into a prompt provider that cannot await
|
|
221
|
+
* (DESIGN §12).
|
|
222
|
+
*/
|
|
223
|
+
relevance(text: string): RelevanceHit;
|
|
224
|
+
/**
|
|
225
|
+
* True once {@link shutdown} has run: the databases are closed and no background pass may start.
|
|
226
|
+
* A gated startup job checks this (as well as the plugin's fiber) before touching the stores.
|
|
227
|
+
*/
|
|
228
|
+
readonly closed: boolean;
|
|
229
|
+
shutdown(): void;
|
|
230
|
+
}
|
|
231
|
+
/** Build the runtime: load config, open the memory DB, construct the stores. */
|
|
232
|
+
export declare function buildRuntime(opts?: RuntimeOptions): AvantfRuntime;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pieces both stores need identically.
|
|
3
|
+
*
|
|
4
|
+
* Small on purpose: `MemoryStore` and `KnowledgeStore` are separate stores over separate databases
|
|
5
|
+
* and should stay that way (AGENTS.md). What they must NOT have is two copies of the same
|
|
6
|
+
* three-line helper, because a fix then lands in one of them — which is exactly what happened to the
|
|
7
|
+
* retrieval orchestration (see `store/hybrid.ts`) and to the foreign-vector-space warning below: the
|
|
8
|
+
* memory store checked whether its persisted vectors came from the CURRENT vector space and told the
|
|
9
|
+
* operator how to fix it, while the knowledge store checked only the width, so a model swap left two
|
|
10
|
+
* spaces silently mixed in one index until someone happened to run `kb_reindex`.
|
|
11
|
+
*
|
|
12
|
+
* @module store/common
|
|
13
|
+
*/
|
|
14
|
+
import type { SemanticBackend, VectorStore } from '../../mem-retrieval/index.js';
|
|
15
|
+
/**
|
|
16
|
+
* The ONE write-side normalization both stores call before anything reaches the database.
|
|
17
|
+
*
|
|
18
|
+
* `@avantf/mem-contract`'s `toWellFormedText` is the implementation (well-formed + NFC); this name
|
|
19
|
+
* is the store-layer entry, so the write paths say WHAT they are doing ("normalize this for a
|
|
20
|
+
* write") rather than repeating a Unicode recipe. Every text field a store persists — fact content,
|
|
21
|
+
* category, archive reason, a document's domain/source/title/uri, entity names and the SPO slots of
|
|
22
|
+
* extracted triples — goes through here, which is what keeps a lone surrogate (and the strict-JSON
|
|
23
|
+
* failure it causes downstream) out of the database in the first place.
|
|
24
|
+
*/
|
|
25
|
+
export declare function normalizeWrite(value: string): string;
|
|
26
|
+
/** {@link normalizeWrite} over a batch (entity names, caller paths, …). */
|
|
27
|
+
export declare function normalizeWrites(values: readonly string[]): string[];
|
|
28
|
+
/**
|
|
29
|
+
* Identity of the vector space a store currently writes into.
|
|
30
|
+
*
|
|
31
|
+
* A change of backend, model or width makes every stored vector stale, and the point of recording
|
|
32
|
+
* the identity is that such a change is DETECTED instead of silently mixing two spaces in one index.
|
|
33
|
+
*/
|
|
34
|
+
export declare function vectorSpaceOf(semantic: SemanticBackend, model: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Drop ids from the vector index, in one batch when the backend supports it.
|
|
37
|
+
*
|
|
38
|
+
* `removeMany` is optional on the interface: a backend that cannot delete incrementally would make
|
|
39
|
+
* an eviction loop O(N²) without it, and hnswlib's is O(k) tombstoning.
|
|
40
|
+
*/
|
|
41
|
+
export declare function evictVectors(vstore: VectorStore, ids: readonly number[]): void;
|
|
42
|
+
/**
|
|
43
|
+
* Tell the operator that persisted vectors came from ANOTHER vector space.
|
|
44
|
+
*
|
|
45
|
+
* A dim-valid vector from a different space is still RANKED — the reload cannot tell the difference
|
|
46
|
+
* — so a silent model swap would mix two spaces in one index and degrade every semantic query
|
|
47
|
+
* without an error anywhere. Said once per process; acting on it is the operator's choice because
|
|
48
|
+
* re-encoding is a real cost (and they should see the count first).
|
|
49
|
+
*
|
|
50
|
+
* @param kind store label for the message (`memory` / `knowledge`)
|
|
51
|
+
* @param space the vector space the store writes into now
|
|
52
|
+
* @param foreign how many persisted vectors were written in a different one
|
|
53
|
+
* @param fix the action that re-encodes them, as the operator would type it
|
|
54
|
+
*/
|
|
55
|
+
export declare function reportForeignVectors(kind: string, space: string, foreign: number, fix: string): void;
|
|
56
|
+
/**
|
|
57
|
+
* Hand the event loop back between two synchronous batches.
|
|
58
|
+
*
|
|
59
|
+
* The mount-time corpus reconcile reads and hashes EVERY managed document (and then walks the tree
|
|
60
|
+
* again for orphans), all of it synchronous `node:fs`; at 10k documents that is a >0.6 s block of the
|
|
61
|
+
* host's event loop (measured — see the performance review §7.5 / P6). `setImmediate` (a macrotask),
|
|
62
|
+
* not a resolved-promise microtask: yielding to the microtask queue would let other JS continuations
|
|
63
|
+
* run while timers and I/O queued behind the batch still waited.
|
|
64
|
+
*/
|
|
65
|
+
export declare function yieldToEventLoop(): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* How many items one synchronous batch may visit before {@link yieldToEventLoop} runs.
|
|
68
|
+
*
|
|
69
|
+
* 200 is a compromise: large enough that the yields themselves are noise next to the per-item
|
|
70
|
+
* `read`+`sha256` (measured ~60 µs each), small enough that the longest single block stays in the
|
|
71
|
+
* low tens of milliseconds at any corpus size.
|
|
72
|
+
*/
|
|
73
|
+
export declare const YIELD_BATCH = 200;
|
|
74
|
+
/**
|
|
75
|
+
* Visit `items` in order, yielding the event loop every `every` items.
|
|
76
|
+
*
|
|
77
|
+
* Use this INSTEAD of a bare `for` loop whenever the per-item mission is synchronous filesystem or
|
|
78
|
+
* hashing work whose item count follows the corpus (not the request): the caller's whole loop used to
|
|
79
|
+
* be one uninterrupted block, so the host could not serve a timer or an I/O callback until it was
|
|
80
|
+
* over. An empty list does nothing (and never yields).
|
|
81
|
+
*/
|
|
82
|
+
export declare function forEachYielding<T>(items: readonly T[], visit: (item: T, index: number) => void, every?: number): Promise<void>;
|