@avantf/dsh-mem 0.1.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.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +140 -0
  3. package/lib/client.js +5901 -0
  4. package/lib/client.js.map +1 -0
  5. package/lib/dsh-build.json +7 -0
  6. package/lib/index.js +14329 -0
  7. package/lib/types/client/domains.d.ts +52 -0
  8. package/lib/types/client/domains.d.ts.map +1 -0
  9. package/lib/types/client/domains.js +25 -0
  10. package/lib/types/client/domains.js.map +1 -0
  11. package/lib/types/client/index.d.ts +35 -0
  12. package/lib/types/client/index.d.ts.map +1 -0
  13. package/lib/types/client/index.js +1351 -0
  14. package/lib/types/client/index.js.map +1 -0
  15. package/lib/types/client/ingest.d.ts +24 -0
  16. package/lib/types/client/ingest.d.ts.map +1 -0
  17. package/lib/types/client/ingest.js +30 -0
  18. package/lib/types/client/ingest.js.map +1 -0
  19. package/lib/types/client/paging.d.ts +30 -0
  20. package/lib/types/client/paging.d.ts.map +1 -0
  21. package/lib/types/client/paging.js +36 -0
  22. package/lib/types/client/paging.js.map +1 -0
  23. package/lib/types/client/summarize.d.ts +11 -0
  24. package/lib/types/client/summarize.d.ts.map +1 -0
  25. package/lib/types/client/summarize.js +79 -0
  26. package/lib/types/client/summarize.js.map +1 -0
  27. package/lib/types/envinit-bootstrap.d.ts +43 -0
  28. package/lib/types/envinit-bootstrap.js +323 -0
  29. package/lib/types/envinit.d.ts +145 -0
  30. package/lib/types/envinit.d.ts.map +1 -0
  31. package/lib/types/envinit.js +495 -0
  32. package/lib/types/envinit.js.map +1 -0
  33. package/lib/types/hints.d.ts +42 -0
  34. package/lib/types/hints.d.ts.map +1 -0
  35. package/lib/types/hints.js +54 -0
  36. package/lib/types/hints.js.map +1 -0
  37. package/lib/types/index.d.ts +74 -0
  38. package/lib/types/index.d.ts.map +1 -0
  39. package/lib/types/index.js +708 -0
  40. package/lib/types/index.js.map +1 -0
  41. package/lib/types/inject.d.ts +14 -0
  42. package/lib/types/inject.d.ts.map +1 -0
  43. package/lib/types/inject.js +14 -0
  44. package/lib/types/inject.js.map +1 -0
  45. package/lib/types/open.d.ts +27 -0
  46. package/lib/types/open.d.ts.map +1 -0
  47. package/lib/types/open.js +133 -0
  48. package/lib/types/open.js.map +1 -0
  49. package/lib/types/prompt.d.ts +49 -0
  50. package/lib/types/prompt.d.ts.map +1 -0
  51. package/lib/types/prompt.js +65 -0
  52. package/lib/types/prompt.js.map +1 -0
  53. package/lib/types/provision.d.ts +117 -0
  54. package/lib/types/provision.d.ts.map +1 -0
  55. package/lib/types/provision.js +233 -0
  56. package/lib/types/provision.js.map +1 -0
  57. package/lib/types/remote.d.ts +17 -0
  58. package/lib/types/remote.d.ts.map +1 -0
  59. package/lib/types/remote.js +233 -0
  60. package/lib/types/remote.js.map +1 -0
  61. package/lib/types/tool_schema.d.ts +40 -0
  62. package/lib/types/tool_schema.d.ts.map +1 -0
  63. package/lib/types/tool_schema.js +149 -0
  64. package/lib/types/tool_schema.js.map +1 -0
  65. package/package.json +109 -0
@@ -0,0 +1,495 @@
1
+ /**
2
+ * Environment initialisation for the memory plugin, through the family framework
3
+ * `@avantf/dsh-envinit`.
4
+ *
5
+ * The shape is fixed by the framework's DESIGN §7.2 and is the same for every plugin:
6
+ *
7
+ * bootstrap (inlined) → load the framework → register + declare → wait for the `blocking` set →
8
+ * dispatch the `background` set → the plugin's own pre-mount check.
9
+ *
10
+ * What this plugin needs, in three phases:
11
+ *
12
+ * 1. **`mem:compat`** (`npm-package`, `startup: 'blocking'`) — the family compatibility gate
13
+ * `@avantf/dsh-compat`. It has to be there BEFORE anything is registered, because a proven
14
+ * incompatible host must refuse the mount with nothing half-registered. This replaces the
15
+ * bespoke self-heal the plugin used to run itself (`@avantf/mem-provision`'s
16
+ * `ensureCompatBase`), whose private root (`<dataHome>/dsh-compat/**`) the framework
17
+ * deliberately does not read.
18
+ * 2. **`mem:pandoc`** (`binary-archive`, `startup: 'background'`) — the documented "expensive but
19
+ * not startup-blocking" resource. It is declared AFTER the runtime exists, because its spec
20
+ * comes from the resolved config (`tools.auto_install`, `tools.mirror`), and it is dispatched
21
+ * without waiting: the caller continues its own initialisation and hooks the outcome through
22
+ * `onSettled`.
23
+ * 3. **`mem:model`** (`model-cache`, `startup: 'background'`) — the embedding model. It uses the
24
+ * framework's `flat` layout, whose files land at `<root>/<repo>/<file>` — exactly the shape
25
+ * `@huggingface/transformers` reads under its `cacheDir` — so the runtime loads the framework's
26
+ * copy instead of fetching a second one. For the default repo the file list is the narrow set
27
+ * the runtime actually opens (see {@link MODEL_FILES}); any other repo omits the list, because
28
+ * a fixed six-file list made the item fail for a repo whose file set differs. The endpoint is
29
+ * the mirror only the operator (env) can move. It is NOT declared when the runtime reads its
30
+ * models elsewhere (`AVANTF_MEM_MODEL_CACHE`) — that would install a second, never-read copy.
31
+ * A model that cannot be fetched is a WARNING: the semantic path degrades to FTS+entity, it
32
+ * never refuses the mount.
33
+ *
34
+ * Two hard rules:
35
+ *
36
+ * - **The framework is never statically imported.** Only `import type` and the inlined bootstrap
37
+ * are static; the value comes from `loadFramework()`. A top-level value import would throw before
38
+ * the bootstrap ran — exactly the broken-tree case the bootstrap exists for.
39
+ * - **Nothing here refuses the mount.** "Cannot tell" is not "incompatible": a missing framework,
40
+ * an unavailable gate or a failed resource is a WARNING, and the plugin mounts with its tools,
41
+ * service, prompt and Remote face intact.
42
+ *
43
+ * @module @avantf/dsh-mem/envinit
44
+ */
45
+ import { readFile } from 'node:fs/promises';
46
+ import { createRequire } from 'node:module';
47
+ import { homedir } from 'node:os';
48
+ import { dirname, join, resolve } from 'node:path';
49
+ import { fileURLToPath, pathToFileURL } from 'node:url';
50
+ import { envAutoDownload, expandHome } from '@avantf/mem-contract';
51
+ import { PANDOC_ARTIFACT_ID, PANDOC_BINARY, PANDOC_PACKS, PANDOC_VERSION } from '@avantf/mem-provision';
52
+ import { loadFramework, readDependencyRange } from './envinit-bootstrap.js';
53
+ import { COMPAT_PACKAGE, compatRange, reasonOf, runtimeFromCompat, warn, } from './provision.js';
54
+ /** The framework package, as declared in this plugin's `peerDependencies`. */
55
+ const FRAMEWORK_PACKAGE = '@avantf/dsh-envinit';
56
+ /** The plugin namespace every item is declared under (`<plugin>:<name>`, framework DESIGN §13.4). */
57
+ const PLUGIN = 'mem';
58
+ /**
59
+ * The item ids. `resolve()` / `onSettled` are keyed by them, so the DSH plugin compares against these
60
+ * names rather than re-spelling literals.
61
+ */
62
+ export const COMPAT_ITEM = 'mem:compat';
63
+ export const PANDOC_ITEM = 'mem:pandoc';
64
+ export const MODEL_ITEM = 'mem:model';
65
+ /**
66
+ * The default ONNX embedding repository — the one `semantic.local_model` defaults to and the one
67
+ * {@link MODEL_FILES} describes. A DIFFERENT repo is a different file set, which is why the item
68
+ * omits `spec.files` for it (see {@link managedModelSpec}).
69
+ */
70
+ export const DEFAULT_MODEL_REPO = 'Xenova/bge-small-zh-v1.5';
71
+ /**
72
+ * The files the local embedding runtime actually requests for the DEFAULT ONNX model.
73
+ *
74
+ * Measured, not guessed: with an empty cache, `@huggingface/transformers@4.x` resolving
75
+ * `feature-extraction` for `Xenova/bge-small-zh-v1.5` fetches exactly these four — `config.json`,
76
+ * `tokenizer.json`, `tokenizer_config.json` and the fp32 `onnx/model.onnx`. The repo ALSO carries
77
+ * `special_tokens_map.json`, `vocab.txt` and seven other ONNX quantisations the default run never
78
+ * opens; declaring them would download hundreds of megabytes of dead weight. The list stays narrow
79
+ * to what is read.
80
+ */
81
+ export const MODEL_FILES = [
82
+ 'config.json',
83
+ 'tokenizer.json',
84
+ 'tokenizer_config.json',
85
+ 'onnx/model.onnx',
86
+ ];
87
+ /** Startup budget for the blocking set; the framework's default is the same. */
88
+ const DEADLINE_MS = 15_000;
89
+ /** This module's warning token, so one grep finds every environment-initialisation line. */
90
+ const OWN_PREFIX = 'envinit:';
91
+ /**
92
+ * The model item the framework should own, or `undefined` when it should not.
93
+ *
94
+ * Two refusals, both deliberate:
95
+ *
96
+ * 1. **Not a local backend** — nothing to provision.
97
+ * 2. **The runtime reads its models from elsewhere** — `semantic.cache_dir` is not the framework's
98
+ * `<home>/models`. That happens through the operator escape hatch `AVANTF_MEM_MODEL_CACHE`
99
+ * (env layer ④): the runtime loads from that directory, so a `mem:model` item would install a
100
+ * full second copy under `<home>/models` that nothing ever reads (~190 MB). Declaring nothing
101
+ * is the fix; the caller warms from the override directory instead.
102
+ *
103
+ * `files` is the narrow {@link MODEL_FILES} set only for the DEFAULT repo; a user-changed repo has a
104
+ * different file set, and a stale six-file list would make the item `failed` while the runtime could
105
+ * serve it. Empty means "omit `spec.files`": the framework then installs the repository's own file
106
+ * list.
107
+ */
108
+ export function managedModelSpec(semantic, modelsRoot) {
109
+ if (semantic.backend !== 'local_bge')
110
+ return undefined;
111
+ if (!sameDir(semantic.cache_dir, modelsRoot))
112
+ return undefined;
113
+ return {
114
+ repo: semantic.local_model,
115
+ files: semantic.local_model === DEFAULT_MODEL_REPO ? MODEL_FILES : [],
116
+ endpoint: semantic.mirror,
117
+ };
118
+ }
119
+ /** Same directory, after `~/` expansion and normalisation (a trailing slash is not a difference). */
120
+ function sameDir(a, b) {
121
+ return resolve(expandHome(a)) === resolve(expandHome(b));
122
+ }
123
+ /**
124
+ * The family root: `$AVANTF_HOME`, else `~/.avantf/env`.
125
+ *
126
+ * `AVANTF_HOME` is the same variable the engine uses for its DATA home (`memory/`, `knowledge/`),
127
+ * and that is deliberate: setting it redirects a whole tree (data AND managed resources), which is
128
+ * exactly what a test or a sandboxed host needs. Unset — the normal case — the family root is the
129
+ * framework's documented default and the data home stays `~/.avantf`.
130
+ */
131
+ function resolveHome() {
132
+ const configured = process.env['AVANTF_HOME'];
133
+ return configured !== undefined && configured.trim() !== '' ? configured : join(homedir(), '.avantf', 'env');
134
+ }
135
+ /**
136
+ * The download gate. Off when EITHER switch is off:
137
+ *
138
+ * - `AVANTF_MEM_AUTO_DOWNLOAD=0` — this project's documented kill switch (the CLI, the MCP server
139
+ * and every test honour it);
140
+ * - `AVANTF_ENVINIT_AUTO_DOWNLOAD=0` — the family framework's switch, so an operator can turn the
141
+ * whole family's provisioning off in one place.
142
+ *
143
+ * The two-variable rule lives in ONE place (`@avantf/mem-contract`'s `envAutoDownload`), which the
144
+ * config loader and both model adapters also call: a family switch that stops the framework but not
145
+ * the runtime's own fetch is exactly the bug that duplication would reintroduce.
146
+ */
147
+ function envDownloadsAllowed() {
148
+ return envAutoDownload() ?? true;
149
+ }
150
+ /** A `ProvisionLogger` over the plugin's own logger (the framework also wants `debug`). */
151
+ function provisionLogger(log) {
152
+ return {
153
+ debug: (message) => { log?.info(message); },
154
+ info: (message) => { log?.info(message); },
155
+ warn: (message) => { log?.warn(message); },
156
+ error: (message) => { log?.error(message); },
157
+ };
158
+ }
159
+ /**
160
+ * The file URL to `import()` for a managed npm copy.
161
+ *
162
+ * The framework hands back the package DIRECTORY (`<version dir>/node_modules/<name>`), and a
163
+ * directory is not importable, so the entry is read from the copy's own manifest the way Node would:
164
+ * `exports["."]` first, then `main`.
165
+ */
166
+ async function entryUrlOf(packageDir) {
167
+ let manifest;
168
+ try {
169
+ manifest = JSON.parse(await readFile(join(packageDir, 'package.json'), 'utf8'));
170
+ }
171
+ catch {
172
+ return undefined;
173
+ }
174
+ const dot = manifest.exports?.['.'];
175
+ const fromExports = typeof dot === 'string'
176
+ ? dot
177
+ : dot?.default;
178
+ const entry = typeof fromExports === 'string'
179
+ ? fromExports
180
+ : typeof manifest.main === 'string' ? manifest.main : './index.js';
181
+ return pathToFileURL(join(packageDir, entry)).href;
182
+ }
183
+ /**
184
+ * The resolved directory of a peer in THIS plugin's tree.
185
+ *
186
+ * The framework must reuse the host's own copy for identity-sensitive peers, and it cannot find them
187
+ * by itself: its resolver looks at `process.cwd()` and at its own module URL — neither is the
188
+ * plugin's tree. So the caller hands the directory over explicitly (`NpmPackageSpec.peers[].dir`);
189
+ * without it the item fails with `peer/unsatisfied`.
190
+ */
191
+ function peerDirOf(name) {
192
+ const require = createRequire(import.meta.url);
193
+ try {
194
+ return dirname(require.resolve(`${name}/package.json`));
195
+ }
196
+ catch {
197
+ // A package that does not export `./package.json`: walk up from its entry instead.
198
+ try {
199
+ let dir = dirname(require.resolve(name));
200
+ for (let depth = 0; depth < 6; depth += 1) {
201
+ if (dir.endsWith(`/${name}`))
202
+ return dir;
203
+ const parent = dirname(dir);
204
+ if (parent === dir)
205
+ break;
206
+ dir = parent;
207
+ }
208
+ }
209
+ catch {
210
+ // Not resolvable from this tree; the framework reports `peer/unsatisfied`.
211
+ }
212
+ }
213
+ return undefined;
214
+ }
215
+ /** The `mem:compat` item: the family compatibility gate, waited for before anything registers. */
216
+ function compatItem(framework, range, zodDir) {
217
+ return {
218
+ id: COMPAT_ITEM,
219
+ kind: framework.NPM_PACKAGE_KIND,
220
+ spec: {
221
+ name: COMPAT_PACKAGE,
222
+ range,
223
+ // The host's own `zod`, by directory: identity-sensitive, and the framework cannot find it
224
+ // from its own tree (see `peerDirOf`).
225
+ peers: [{ name: 'zod', ...(zodDir === undefined ? {} : { dir: zodDir }) }],
226
+ },
227
+ target: { root: 'runtime' },
228
+ // Never refuse: an unavailable gate must not take the memory engine down.
229
+ onMissing: { atStartup: 'degrade', atUse: 'degrade' },
230
+ startup: 'blocking',
231
+ schemaVersion: framework.ITEM_SCHEMA_VERSION,
232
+ };
233
+ }
234
+ /**
235
+ * The `mem:pandoc` item: the pinned pandoc release as a `binary-archive`.
236
+ *
237
+ * The packs are `@avantf/mem-provision`'s (`PANDOC_PACKS`), the single source of the URLs and the
238
+ * measured digests; this only re-spells them in the framework's vocabulary (`format` → `archive`,
239
+ * and the binary as a NAME the archive search finds rather than a path inside the archive). The
240
+ * framework installs it to `<home>/tools/pandoc/3.11/bin/pandoc` — the same layout the converter
241
+ * already resolves, which is what makes the handover a config change and not a code change.
242
+ */
243
+ function pandocItem(framework) {
244
+ const packs = {};
245
+ for (const [key, pack] of Object.entries(PANDOC_PACKS)) {
246
+ if (pack === undefined)
247
+ continue;
248
+ packs[key] = { url: pack.url, sha256: pack.sha256, archive: pack.format };
249
+ }
250
+ return {
251
+ id: PANDOC_ITEM,
252
+ kind: framework.BINARY_ARCHIVE_KIND,
253
+ spec: {
254
+ id: PANDOC_ARTIFACT_ID,
255
+ version: PANDOC_VERSION,
256
+ binary: PANDOC_BINARY,
257
+ packs,
258
+ },
259
+ target: { root: 'tools' },
260
+ // A missing converter degrades the knowledge base's document ingestion, never the mount.
261
+ onMissing: { atStartup: 'degrade', atUse: 'error' },
262
+ startup: 'background',
263
+ schemaVersion: framework.ITEM_SCHEMA_VERSION,
264
+ };
265
+ }
266
+ /**
267
+ * The `mem:model` item: the embedding model as a `model-cache` in the framework's `flat` layout.
268
+ *
269
+ * `flat` places the files at `<home>/models/<repo>/<file>` — the same tree `semantic.cache_dir`
270
+ * points at (the framework root, through `managedRoots`) and the same shape the local runtime reads.
271
+ * `files` is the narrow default-repo set (see {@link MODEL_FILES}); it is OMITTED for any other repo,
272
+ * whose file set this build cannot know, so the framework installs the repository's own list instead
273
+ * of failing on a missing fixed path. `endpoint` is the mirror resolved from the operator's env
274
+ * escape hatch, so neither the landing site nor the source is chosen by an end user's `config.yaml`.
275
+ *
276
+ * A missing model is a degraded semantic path, NOT a refused mount: both `onMissing` axes are
277
+ * `degrade`, and the engine falls back to FTS+entity.
278
+ */
279
+ function modelItem(framework, model) {
280
+ return {
281
+ id: MODEL_ITEM,
282
+ kind: framework.MODEL_CACHE_KIND,
283
+ spec: {
284
+ repo: model.repo,
285
+ revision: 'main',
286
+ layout: 'flat',
287
+ ...(model.files.length === 0 ? {} : { files: [...model.files] }),
288
+ endpoint: model.endpoint,
289
+ },
290
+ target: { root: 'models' },
291
+ onMissing: { atStartup: 'degrade', atUse: 'degrade' },
292
+ startup: 'background',
293
+ schemaVersion: framework.ITEM_SCHEMA_VERSION,
294
+ };
295
+ }
296
+ let loaded;
297
+ let loading;
298
+ /**
299
+ * Ensure the framework, then the compatibility base, then dispatch the expensive resources.
300
+ *
301
+ * Never throws. Returns `undefined` when the framework cannot be loaded — the caller then warns and
302
+ * mounts on the legacy provisioning path (the engine's own `@avantf/mem-provision` sweep, whose
303
+ * default directories are the pre-migration ones). A framework that loads but cannot provide the
304
+ * base still returns a runtime, with `compat: undefined`: the mount must continue, and the resource
305
+ * items are still worth dispatching.
306
+ *
307
+ * The result is cached per process; a FAILED load is not, so a profile reload retries instead of
308
+ * replaying the first failure for the whole process lifetime.
309
+ *
310
+ * @param options - logger, family root, download policy and the test seams.
311
+ * @returns the loaded runtime, or `undefined` when the framework itself is unavailable.
312
+ */
313
+ export async function loadEnvinit(options = {}) {
314
+ if (loaded !== undefined)
315
+ return loaded;
316
+ if (loading !== undefined)
317
+ return loading;
318
+ loading = loadGuarded(options).then((runtime) => {
319
+ // A failure is "cannot tell", not a verdict — and it may be transient (offline at boot,
320
+ // registry hiccup). Drop the cache so a profile reload, or a second instance in the same
321
+ // process, retries instead of replaying the first failure for the whole process lifetime.
322
+ if (runtime === undefined)
323
+ loading = undefined;
324
+ else
325
+ loaded = runtime;
326
+ return runtime;
327
+ }, () => {
328
+ loading = undefined;
329
+ return undefined;
330
+ });
331
+ return loading;
332
+ }
333
+ /** The guard that makes "never throws" true: every failure is a warning plus `undefined`. */
334
+ async function loadGuarded(options) {
335
+ try {
336
+ return await loadOnce(options);
337
+ }
338
+ catch (error) {
339
+ warn(options.log, `${OWN_PREFIX} WARNING — environment initialisation failed (${reasonOf(error)}); the framework is SKIPPED and the plugin will mount anyway`);
340
+ return undefined;
341
+ }
342
+ }
343
+ /** The body of {@link loadEnvinit}; the cache above makes it run at most once per process. */
344
+ async function loadOnce(options) {
345
+ const home = options.home ?? resolveHome();
346
+ const range = await readDependencyRange(fileURLToPath(import.meta.url));
347
+ const autoDownload = options.autoDownload ?? envDownloadsAllowed();
348
+ const deadlineMs = options.deadlineMs ?? DEADLINE_MS;
349
+ // Step 1–2: bootstrap → the framework itself (the package manager's copy, zero download). The
350
+ // plugin's declared range is not judged here; it goes to `envinitRange` below, per manifest.
351
+ const framework = options.framework ?? await loadFramework({
352
+ logger: {
353
+ warn: (message) => { warn(options.log, `${FRAMEWORK_PACKAGE}: ${message}`); },
354
+ info: (message) => { options.log?.info(`${FRAMEWORK_PACKAGE}: ${message}`); },
355
+ },
356
+ });
357
+ if (framework === undefined) {
358
+ warn(options.log, `${OWN_PREFIX} WARNING — ${FRAMEWORK_PACKAGE} could not be made available; the plugin falls back to its own provisioning and mounts anyway`);
359
+ return undefined;
360
+ }
361
+ // Step 3–4: declare the blocking set and wait for it.
362
+ const provisioner = framework.createProvisioner({
363
+ home,
364
+ envinitRange: range,
365
+ logger: provisionLogger(options.log),
366
+ policy: { autoDownload, deadlineMs },
367
+ });
368
+ provisioner.register(framework.npmPackageProvider());
369
+ provisioner.declare({
370
+ plugin: PLUGIN,
371
+ items: [compatItem(framework, await compatRange(), peerDirOf('zod'))],
372
+ });
373
+ await provisioner.ensure({
374
+ only: [COMPAT_ITEM],
375
+ deadlineMs,
376
+ onSettled: (entry) => {
377
+ if (entry.action === 'failed') {
378
+ warn(options.log, `${OWN_PREFIX} WARNING — ${COMPAT_ITEM} failed (${String(entry.code)}): ${entry.reason ?? ''}`);
379
+ }
380
+ },
381
+ });
382
+ const disposers = [provisioner];
383
+ let compat;
384
+ const state = provisioner.resolve(COMPAT_ITEM);
385
+ if (state.state !== 'ready') {
386
+ warn(options.log, `${OWN_PREFIX} WARNING — ${COMPAT_PACKAGE} is unavailable (${describeState(state)}); the compatibility gate is SKIPPED and the plugin will mount anyway`);
387
+ }
388
+ else {
389
+ try {
390
+ const module = options.compatModule ?? await importCompat(state.handle.dir);
391
+ if (module === undefined) {
392
+ warn(options.log, `${OWN_PREFIX} WARNING — ${COMPAT_PACKAGE} has no readable entry at ${state.handle.dir}; the compatibility gate is SKIPPED`);
393
+ }
394
+ else {
395
+ compat = runtimeFromCompat(module);
396
+ }
397
+ }
398
+ catch (error) {
399
+ warn(options.log, `${OWN_PREFIX} WARNING — ${COMPAT_PACKAGE} could not be imported (${reasonOf(error)}); the compatibility gate is SKIPPED and the plugin will mount anyway`);
400
+ }
401
+ }
402
+ let disposed = false;
403
+ return {
404
+ home,
405
+ compat,
406
+ roots: { tools: join(home, 'tools'), models: join(home, 'models') },
407
+ provisionResources(plan, onSettled) {
408
+ if (disposed)
409
+ return;
410
+ const items = [];
411
+ if (plan.pandoc)
412
+ items.push(pandocItem(framework));
413
+ if (plan.model !== undefined)
414
+ items.push(modelItem(framework, plan.model));
415
+ if (items.length === 0)
416
+ return;
417
+ // A SECOND provisioner, on purpose: `policy.mirrors.archive` is the engine's `tools.mirror`,
418
+ // which is only known after the runtime resolved its config — while the compat provisioner had
419
+ // to exist before that, to run the gate. Two instances sharing one family root are supported by
420
+ // design (framework DESIGN §13.1), and their item sets are disjoint.
421
+ //
422
+ // `autoDownload` is per KIND: the env kill switches turn everything off, otherwise the model
423
+ // follows `semantic.auto_download` while pandoc keeps its own gate (`tools.auto_install`,
424
+ // which decides whether it is declared at all). Folding the two into one boolean would let
425
+ // `semantic.auto_download: false` skip a pandoc the operator explicitly asked for — or, the
426
+ // other way round, let a disabled model still hit the network.
427
+ const resources = framework.createProvisioner({
428
+ home,
429
+ envinitRange: range,
430
+ logger: provisionLogger(options.log),
431
+ policy: {
432
+ autoDownload: autoDownload ? { [framework.MODEL_CACHE_KIND]: plan.modelAutoDownload } : false,
433
+ deadlineMs,
434
+ mirrors: { archive: [...plan.archiveMirrors] },
435
+ },
436
+ });
437
+ resources.register(framework.binaryArchiveProvider());
438
+ resources.register(framework.modelCacheProvider());
439
+ resources.declare({ plugin: PLUGIN, items });
440
+ disposers.push(resources);
441
+ // Dispatched, not awaited: `startup: 'background'` items never occupy the ensure budget.
442
+ // The `.catch` is not decoration: an unhandled rejection is FATAL on a strict host (Node's
443
+ // default `--unhandled-rejections=throw`), and a memory plugin must not be able to take the
444
+ // host down over a resource it could not fetch. The compat ensure above is awaited instead,
445
+ // so its rejection is handled by `loadGuarded`.
446
+ void resources.ensure({
447
+ only: items.map(item => item.id),
448
+ onSettled: (entry) => {
449
+ try {
450
+ onSettled(entry);
451
+ }
452
+ catch (error) {
453
+ warn(options.log, `${OWN_PREFIX} WARNING — the onSettled callback failed (${reasonOf(error)})`);
454
+ }
455
+ },
456
+ }).catch((error) => {
457
+ warn(options.log, `${OWN_PREFIX} WARNING — declaring the background resources failed (${reasonOf(error)}); the plugin continues (tools stay registered)`);
458
+ });
459
+ },
460
+ dispose() {
461
+ if (disposed)
462
+ return;
463
+ disposed = true;
464
+ for (const instance of disposers.splice(0)) {
465
+ try {
466
+ instance.dispose();
467
+ }
468
+ catch {
469
+ // Disposal is best-effort; a throwing release must not break unmount.
470
+ }
471
+ }
472
+ loaded = undefined;
473
+ loading = undefined;
474
+ },
475
+ };
476
+ }
477
+ /** One line describing why a non-ready item is not ready, safe to put in a WARNING. */
478
+ function describeState(state) {
479
+ if (state.state === 'ready')
480
+ return `ready (${state.handle.source})`;
481
+ if (state.state === 'failed' || state.state === 'skipped') {
482
+ return `${state.state}: ${state.code}${state.detail === undefined ? '' : ` (${state.detail})`}`;
483
+ }
484
+ if (state.state === 'pending')
485
+ return 'pending (still being fetched)';
486
+ return state.state;
487
+ }
488
+ /** Load the managed copy's entry by file URL; `undefined` when it has no readable entry. */
489
+ async function importCompat(packageDir) {
490
+ const url = await entryUrlOf(packageDir);
491
+ if (url === undefined)
492
+ return undefined;
493
+ return (await import(url));
494
+ }
495
+ //# sourceMappingURL=envinit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envinit.js","sourceRoot":"","sources":["../../src/envinit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type { RelevanceHit } from '@avantf/mem';
2
+ /**
3
+ * The plugin's CONDITIONAL hints: one line per store, contributed only when that store already
4
+ * holds something relevant to what the user just sent (DESIGN §12).
5
+ *
6
+ * These are deliberately NOT part of the always-on usage sections. A usage section says WHEN a
7
+ * store is worth consulting and therefore has to be paid for on every step of every session; this
8
+ * says "there is something here, right now", which is only true sometimes. The two layers compose:
9
+ * the section is the fallback for the paraphrase the lexical probe misses, and the hint is the
10
+ * concrete nudge for the case it catches.
11
+ *
12
+ * ONE LINE PER STORE, never a merged sentence. The two stores answer different questions and
13
+ * either can be the relevant one, so `RelevanceHit` is a closed four-valued answer and each store
14
+ * gets its own contribution — a caller can then see, and later tune or drop, one without the other.
15
+ *
16
+ * WHY EACH LINE NAMES THE PLUGIN. This text is injected as runtime context, and the harness
17
+ * materialises that as a USER-role snapshot in the conversation (`dsh-agent-loop`'s
18
+ * `runtimeContext.project`). Without an author, a line like "记忆里有相关内容" reads as something
19
+ * the user said. The `[avantf-mem 插件]` prefix is what keeps the provenance honest.
20
+ */
21
+ export declare const MEMORY_HINT = "[avantf-mem \u63D2\u4EF6] \u8BB0\u5FC6\u91CC\u6709\u4E0E\u672C\u6B21\u63D0\u95EE\u76F8\u5173\u7684\u4E8B\u5B9E\uFF1B\u9700\u8981\u65F6\u7528 `mem_recall` \u68C0\u7D22\u3002";
22
+ export declare const KNOWLEDGE_HINT = "[avantf-mem \u63D2\u4EF6] \u77E5\u8BC6\u5E93\u91CC\u6709\u4E0E\u672C\u6B21\u63D0\u95EE\u76F8\u5173\u7684\u5185\u5BB9\uFF1B\u9700\u8981\u65F6\u7528 `kb_query` \u68C0\u7D22\u3002";
23
+ /**
24
+ * Map one answer to the two contributions, as text.
25
+ *
26
+ * An empty string is the "contribute nothing" value: `renderPrompt`/`renderContextSections` drop
27
+ * empty text, so `none` (and the missing half of `memory`/`knowledge`) costs no tokens at all.
28
+ */
29
+ export declare function hintLines(hit: RelevanceHit): {
30
+ memory: string;
31
+ knowledge: string;
32
+ };
33
+ /**
34
+ * The plain text of an inbox message, or `''` when it carries none.
35
+ *
36
+ * `dsh-agent-loop`'s own extractor returns `undefined` unless the message is exactly ONE text
37
+ * block; a steered or multi-part message would then silently produce no hint. Concatenating the
38
+ * text blocks keeps the probe working for those, and non-text blocks (images) are simply skipped —
39
+ * the probe is lexical, so it has nothing to say about them anyway.
40
+ */
41
+ export declare function messageText(message: unknown): string;
42
+ //# sourceMappingURL=hints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hints.d.ts","sourceRoot":"","sources":["../../src/hints.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C;;;;;;;;;;;;;;;;;;GAkBG;AAEH,eAAO,MAAM,WAAW,iLAAyD,CAAA;AAEjF,eAAO,MAAM,cAAc,qLAAwD,CAAA;AAEnF;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,YAAY,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAKlF;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAUpD"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The plugin's CONDITIONAL hints: one line per store, contributed only when that store already
3
+ * holds something relevant to what the user just sent (DESIGN §12).
4
+ *
5
+ * These are deliberately NOT part of the always-on usage sections. A usage section says WHEN a
6
+ * store is worth consulting and therefore has to be paid for on every step of every session; this
7
+ * says "there is something here, right now", which is only true sometimes. The two layers compose:
8
+ * the section is the fallback for the paraphrase the lexical probe misses, and the hint is the
9
+ * concrete nudge for the case it catches.
10
+ *
11
+ * ONE LINE PER STORE, never a merged sentence. The two stores answer different questions and
12
+ * either can be the relevant one, so `RelevanceHit` is a closed four-valued answer and each store
13
+ * gets its own contribution — a caller can then see, and later tune or drop, one without the other.
14
+ *
15
+ * WHY EACH LINE NAMES THE PLUGIN. This text is injected as runtime context, and the harness
16
+ * materialises that as a USER-role snapshot in the conversation (`dsh-agent-loop`'s
17
+ * `runtimeContext.project`). Without an author, a line like "记忆里有相关内容" reads as something
18
+ * the user said. The `[avantf-mem 插件]` prefix is what keeps the provenance honest.
19
+ */
20
+ export const MEMORY_HINT = '[avantf-mem 插件] 记忆里有与本次提问相关的事实;需要时用 `mem_recall` 检索。';
21
+ export const KNOWLEDGE_HINT = '[avantf-mem 插件] 知识库里有与本次提问相关的内容;需要时用 `kb_query` 检索。';
22
+ /**
23
+ * Map one answer to the two contributions, as text.
24
+ *
25
+ * An empty string is the "contribute nothing" value: `renderPrompt`/`renderContextSections` drop
26
+ * empty text, so `none` (and the missing half of `memory`/`knowledge`) costs no tokens at all.
27
+ */
28
+ export function hintLines(hit) {
29
+ return {
30
+ memory: hit === 'memory' || hit === 'both' ? MEMORY_HINT : '',
31
+ knowledge: hit === 'knowledge' || hit === 'both' ? KNOWLEDGE_HINT : '',
32
+ };
33
+ }
34
+ /**
35
+ * The plain text of an inbox message, or `''` when it carries none.
36
+ *
37
+ * `dsh-agent-loop`'s own extractor returns `undefined` unless the message is exactly ONE text
38
+ * block; a steered or multi-part message would then silently produce no hint. Concatenating the
39
+ * text blocks keeps the probe working for those, and non-text blocks (images) are simply skipped —
40
+ * the probe is lexical, so it has nothing to say about them anyway.
41
+ */
42
+ export function messageText(message) {
43
+ const content = message?.content;
44
+ if (!Array.isArray(content))
45
+ return '';
46
+ return content
47
+ .map((block) => {
48
+ const b = block;
49
+ return b?.type === 'text' && typeof b.text === 'string' ? b.text : '';
50
+ })
51
+ .filter(Boolean)
52
+ .join('\n');
53
+ }
54
+ //# sourceMappingURL=hints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hints.js","sourceRoot":"","sources":["../../src/hints.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG,sDAAsD,CAAA;AAEjF,MAAM,CAAC,MAAM,cAAc,GAAG,qDAAqD,CAAA;AAEnF;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,GAAiB;IACzC,OAAO;QACL,MAAM,EAAE,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE;QAC7D,SAAS,EAAE,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE;KACvE,CAAA;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,MAAM,OAAO,GAAI,OAAwC,EAAE,OAAO,CAAA;IAClE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAA;IACtC,OAAO,OAAO;SACX,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,MAAM,CAAC,GAAG,KAA0C,CAAA;QACpD,OAAO,CAAC,EAAE,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;IACvE,CAAC,CAAC;SACD,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,IAAI,CAAC,CAAA;AACf,CAAC"}