@avantf/dsh-mem 0.6.4 → 0.8.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 (150) hide show
  1. package/README.md +19 -10
  2. package/lib/client.js +1665 -3466
  3. package/lib/dsh-build.json +6 -5
  4. package/lib/engine/graph-recall/in-memory.d.ts +93 -0
  5. package/lib/engine/graph-recall/index.d.ts +22 -0
  6. package/lib/engine/graph-recall/traverse.d.ts +119 -0
  7. package/lib/engine/graph-recall/types.d.ts +188 -0
  8. package/lib/engine/mem/config/config_files.d.ts +1 -1
  9. package/lib/engine/mem/config/loader.d.ts +2 -3
  10. package/lib/engine/mem/config/paths.d.ts +2 -13
  11. package/lib/engine/mem/db/dao/contradictions.d.ts +4 -22
  12. package/lib/engine/mem/db/dao/entities.d.ts +80 -7
  13. package/lib/engine/mem/db/dao/fact_sources.d.ts +3 -13
  14. package/lib/engine/mem/db/dao/facts.d.ts +311 -56
  15. package/lib/engine/mem/db/dao/triples.d.ts +1 -26
  16. package/lib/engine/mem/db/graph.d.ts +249 -0
  17. package/lib/engine/mem/db/journal_notes.d.ts +35 -0
  18. package/lib/engine/mem/db/migrations.d.ts +9 -0
  19. package/lib/engine/mem/db/port.d.ts +22 -3
  20. package/lib/engine/mem/db/schema.d.ts +119 -0
  21. package/lib/engine/mem/db/tokenizer.d.ts +9 -0
  22. package/lib/engine/mem/dbdrivers/fake/driver.d.ts +75 -0
  23. package/lib/engine/mem/dbdrivers/fake/index.d.ts +7 -0
  24. package/lib/engine/mem/dbdrivers/fake/surface.d.ts +34 -0
  25. package/lib/engine/mem/dbdrivers/fake/transport.d.ts +61 -0
  26. package/lib/engine/mem/dbdrivers/sqlite/connection.d.ts +14 -0
  27. package/lib/engine/mem/dbdrivers/sqlite/contradictions.d.ts +35 -0
  28. package/lib/engine/mem/dbdrivers/sqlite/dao/index.d.ts +16 -0
  29. package/lib/engine/mem/dbdrivers/sqlite/driver.d.ts +54 -0
  30. package/lib/engine/mem/dbdrivers/sqlite/facts.d.ts +117 -0
  31. package/lib/engine/mem/dbdrivers/sqlite/index.d.ts +6 -0
  32. package/lib/engine/mem/dbdrivers/sqlite/migrations.d.ts +15 -0
  33. package/lib/engine/mem/dbdrivers/sqlite/relations.d.ts +62 -0
  34. package/lib/engine/mem/dbdrivers/sqlite/search.d.ts +56 -0
  35. package/lib/engine/mem/dbdrivers/sqlite/sources.d.ts +37 -0
  36. package/lib/engine/mem/defaults.d.ts +3 -4
  37. package/lib/engine/mem/dispatch.d.ts +7 -32
  38. package/lib/engine/mem/entities/extract.d.ts +44 -47
  39. package/lib/engine/mem/eval/derived_runner.d.ts +6 -1
  40. package/lib/engine/mem/eval/relation_gold.d.ts +251 -0
  41. package/lib/engine/mem/eval/write_metrics.d.ts +1 -1
  42. package/lib/engine/mem/facts_io.d.ts +328 -0
  43. package/lib/engine/mem/index.d.ts +10 -2
  44. package/lib/engine/mem/interfaces/capabilities.d.ts +30 -0
  45. package/lib/engine/mem/interfaces/common.d.ts +11 -0
  46. package/lib/engine/mem/interfaces/errors.d.ts +65 -0
  47. package/lib/engine/mem/interfaces/index.d.ts +18 -0
  48. package/lib/engine/mem/interfaces/migrations.d.ts +21 -0
  49. package/lib/engine/mem/interfaces/repositories.d.ts +471 -0
  50. package/lib/engine/mem/interfaces/search_index.d.ts +113 -0
  51. package/lib/engine/mem/interfaces/storage_backend.d.ts +84 -0
  52. package/lib/engine/mem/interfaces/storage_driver.d.ts +43 -0
  53. package/lib/engine/mem/interfaces/version.d.ts +34 -0
  54. package/lib/engine/mem/lifecycle/contradiction.d.ts +2 -2
  55. package/lib/engine/mem/maintenance_paths.d.ts +41 -0
  56. package/lib/engine/mem/modelBootstrap.d.ts +1 -1
  57. package/lib/engine/mem/relations/confidence.d.ts +39 -0
  58. package/lib/engine/mem/relations/extract.d.ts +128 -0
  59. package/lib/engine/mem/relations/index.d.ts +32 -0
  60. package/lib/engine/mem/relations/name-quality.d.ts +78 -0
  61. package/lib/engine/mem/relations/normalize.d.ts +275 -0
  62. package/lib/engine/mem/relations/patterns.d.ts +67 -0
  63. package/lib/engine/mem/relations/statement.d.ts +88 -0
  64. package/lib/engine/mem/relations/types.d.ts +79 -0
  65. package/lib/engine/mem/runtime.d.ts +117 -90
  66. package/lib/engine/mem/storage/create.d.ts +42 -0
  67. package/lib/engine/mem/storage/errors.d.ts +65 -0
  68. package/lib/engine/mem/storage/index.d.ts +15 -0
  69. package/lib/engine/mem/storage/ports.d.ts +472 -0
  70. package/lib/engine/mem/storage/registry.d.ts +37 -0
  71. package/lib/engine/mem/storage/sqlite.d.ts +28 -0
  72. package/lib/engine/mem/store/common.d.ts +1 -1
  73. package/lib/engine/mem/store/entity_leg.d.ts +59 -2
  74. package/lib/engine/mem/store/entity_sweep.d.ts +4 -3
  75. package/lib/engine/mem/store/explicit_relations.d.ts +52 -0
  76. package/lib/engine/mem/store/facts_restore.d.ts +124 -0
  77. package/lib/engine/mem/store/floors.d.ts +4 -1
  78. package/lib/engine/mem/store/graph_reader.d.ts +146 -0
  79. package/lib/engine/mem/store/hybrid.d.ts +50 -13
  80. package/lib/engine/mem/store/kinds.d.ts +47 -0
  81. package/lib/engine/mem/store/legs.d.ts +24 -29
  82. package/lib/engine/mem/store/lexical.d.ts +36 -45
  83. package/lib/engine/mem/store/memory.d.ts +486 -82
  84. package/lib/engine/mem/store/recall_policy.d.ts +49 -0
  85. package/lib/engine/mem/store/relation_backfill.d.ts +210 -0
  86. package/lib/engine/mem/store/relation_planner.d.ts +191 -0
  87. package/lib/engine/mem/store/secret_guard.d.ts +0 -1
  88. package/lib/engine/mem/store/self_query.d.ts +4 -2
  89. package/lib/engine/mem/store/time_filter.d.ts +121 -0
  90. package/lib/engine/mem/store/titles.d.ts +52 -0
  91. package/lib/engine/mem/store/vector_repair.d.ts +44 -54
  92. package/lib/engine/mem-contract/config.d.ts +44 -38
  93. package/lib/engine/mem-contract/text.d.ts +1 -1
  94. package/lib/engine/mem-contract/tools.d.ts +184 -117
  95. package/lib/engine/mem-contract/types.d.ts +364 -288
  96. package/lib/index.js +13898 -11500
  97. package/lib/interface-version.json +1 -1
  98. package/lib/types/client/index.d.ts +2 -2
  99. package/lib/types/client/index.d.ts.map +1 -1
  100. package/lib/types/client/paging.d.ts +3 -12
  101. package/lib/types/client/paging.d.ts.map +1 -1
  102. package/lib/types/client/summarize.d.ts.map +1 -1
  103. package/lib/types/command.d.ts +61 -0
  104. package/lib/types/command.d.ts.map +1 -0
  105. package/lib/types/envinit-bootstrap.d.ts +1 -1
  106. package/lib/types/envinit.d.ts +2 -6
  107. package/lib/types/envinit.d.ts.map +1 -1
  108. package/lib/types/hints.d.ts +28 -24
  109. package/lib/types/hints.d.ts.map +1 -1
  110. package/lib/types/index.d.ts +21 -40
  111. package/lib/types/index.d.ts.map +1 -1
  112. package/lib/types/inject.d.ts +3 -2
  113. package/lib/types/inject.d.ts.map +1 -1
  114. package/lib/types/interface_gate.d.ts +9 -9
  115. package/lib/types/prompt.d.ts +17 -41
  116. package/lib/types/prompt.d.ts.map +1 -1
  117. package/lib/types/provision.d.ts +3 -0
  118. package/lib/types/provision.d.ts.map +1 -1
  119. package/lib/types/remote.d.ts +23 -9
  120. package/lib/types/remote.d.ts.map +1 -1
  121. package/lib/types/render.d.ts +3 -3
  122. package/lib/types/tool_schema.d.ts +4 -0
  123. package/lib/types/tool_schema.d.ts.map +1 -1
  124. package/lib/types/vectorMigration.d.ts +5 -7
  125. package/lib/types/vectorMigration.d.ts.map +1 -1
  126. package/package.json +16 -15
  127. package/lib/engine/mem/config/domains.d.ts +0 -20
  128. package/lib/engine/mem/db/dao/chunks.d.ts +0 -231
  129. package/lib/engine/mem/db/dao/documents.d.ts +0 -51
  130. package/lib/engine/mem/db/hash.d.ts +0 -2
  131. package/lib/engine/mem/db/knowledge.d.ts +0 -17
  132. package/lib/engine/mem/eval/index.d.ts +0 -4
  133. package/lib/engine/mem/git.d.ts +0 -73
  134. package/lib/engine/mem/router.d.ts +0 -51
  135. package/lib/engine/mem/store/doc_files.d.ts +0 -125
  136. package/lib/engine/mem/store/document_text.d.ts +0 -47
  137. package/lib/engine/mem/store/ingest_guard.d.ts +0 -25
  138. package/lib/engine/mem/store/knowledge.d.ts +0 -598
  139. package/lib/engine/mem/store/source_picker.d.ts +0 -43
  140. package/lib/engine/mem-retrieval/adapters/local_reranker.d.ts +0 -52
  141. package/lib/engine/mem-retrieval/adapters/none_reranker.d.ts +0 -10
  142. package/lib/engine/mem-retrieval/rerank.d.ts +0 -13
  143. package/lib/types/client/domains.d.ts +0 -53
  144. package/lib/types/client/domains.d.ts.map +0 -1
  145. package/lib/types/client/ingest.d.ts +0 -24
  146. package/lib/types/client/ingest.d.ts.map +0 -1
  147. package/lib/types/open.d.ts +0 -77
  148. package/lib/types/open.d.ts.map +0 -1
  149. package/lib/types/reconcile.d.ts +0 -64
  150. package/lib/types/reconcile.d.ts.map +0 -1
@@ -1,7 +1,8 @@
1
1
  {
2
- "@deepseek-ai/dsh-system-prompt": "0.2.0-rc.2",
3
- "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
4
- "@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.2",
5
- "@deepseek-ai/dsh-typert-registry": "0.2.0-rc.2",
6
- "@deepseek-ai/dsh-util-values": "0.2.0-rc.2"
2
+ "@deepseek-ai/dsh-llm": "0.2.1-rc.2",
3
+ "@deepseek-ai/dsh-system-prompt": "0.2.1-rc.2",
4
+ "@deepseek-ai/dsh-tools": "0.2.1-rc.2",
5
+ "@deepseek-ai/dsh-typert-protocol": "0.2.1-rc.2",
6
+ "@deepseek-ai/dsh-typert-registry": "0.2.1-rc.2",
7
+ "@deepseek-ai/dsh-util-values": "0.2.1-rc.2"
7
8
  }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * A complete {@link GraphReader} over plain arrays, used by this package's golden tests and
3
+ * available to consumers that need the same fixture (the mem and knowledge adapter contract tests
4
+ * are expected to run against it as the reference implementation).
5
+ *
6
+ * It is intentionally literal: insertion order for reads, exact-match predicate filtering, no
7
+ * inferred edges, no normalization beyond the alias maps a fixture declares. Anything cleverer
8
+ * belongs in a real adapter, where it is tested against real storage.
9
+ *
10
+ * TWO PROPERTIES A FIXTURE READER MUST HAVE, so tests over it mean something for a real adapter:
11
+ *
12
+ * - **Reads are immutable.** Nodes and edges are copied and `Object.freeze`d when they are
13
+ * registered, so a caller mutating a read result (or the node `resolveNode` returned) throws in
14
+ * strict mode instead of silently rewriting the graph. The fixtures' own objects are NOT frozen:
15
+ * the copy is the reader's, the original stays the caller's.
16
+ * - **`resolveNode` is alias-first**, then label, then id — the order memory's adapter uses, so a
17
+ * query resolves to the same node here and on a real store.
18
+ * - **Re-registering an id keeps the graph consistent.** A new label (or kind) replaces the stored
19
+ * node AND every already-indexed edge that names it, so `resolveNode(id)` and an edge's endpoint
20
+ * never disagree. Labels are names, not identities: the old label keeps resolving to that id.
21
+ */
22
+ import type { GraphEdge, GraphNode, GraphReader } from './types.js';
23
+ /** Seed data for an {@link InMemoryGraphReader}. */
24
+ export interface InMemoryGraphReaderOptions {
25
+ nodes?: readonly GraphNode[];
26
+ edges?: readonly GraphEdge[];
27
+ /** `alias → node id` for {@link InMemoryGraphReader.resolveNode}. */
28
+ nodeAliases?: Readonly<Record<string, string>>;
29
+ /** `alias → canonical predicate` for {@link InMemoryGraphReader.resolvePredicate}. */
30
+ predicateAliases?: Readonly<Record<string, string>>;
31
+ }
32
+ /**
33
+ * In-memory graph with adjacency and fact indexes kept in sync by {@link addEdge}.
34
+ *
35
+ * Duplicate edge ids are REJECTED: path dedup is defined on edge ids, so two different edges
36
+ * sharing one id would make a result ambiguous. Test fixtures must give every edge a unique id.
37
+ */
38
+ export declare class InMemoryGraphReader implements GraphReader {
39
+ private readonly nodes;
40
+ private readonly nodesByLabel;
41
+ private readonly aliases;
42
+ private readonly outgoingIndex;
43
+ private readonly incomingIndex;
44
+ private readonly factIndex;
45
+ private readonly edgesById;
46
+ private readonly edgeIds;
47
+ private readonly predicates;
48
+ private readonly predicateAliases;
49
+ constructor(options?: InMemoryGraphReaderOptions);
50
+ /**
51
+ * Register a node (idempotent; the last registration of an id wins) and return the stored copy.
52
+ *
53
+ * Labels are names, not identities: two nodes may share one, and this adapter keeps the FIRST
54
+ * registration for label lookups (a duplicate label is ambiguous; pick once, not per call).
55
+ * Re-registering an id with a NEW label adds that label and leaves the old one pointing at the
56
+ * same id, so a name the reader has already answered with never stops resolving.
57
+ *
58
+ * When the re-registration CHANGES the node (a new label or kind), every already-indexed edge that
59
+ * names that id is rewritten to the stored copy as well. Reads hand out edges, so leaving them on
60
+ * the previous frozen node would make `resolveNode(id)` and `edge.subject` describe two different
61
+ * nodes for one id.
62
+ */
63
+ private registerNode;
64
+ /** Point every indexed edge that uses `node.id` at the new stored copy. */
65
+ private rewireIndexedEdges;
66
+ /** Register a node (idempotent; the last registration of an id wins). */
67
+ addNode(node: GraphNode): this;
68
+ /** Register `alias → node id`. The node must exist by the time {@link resolveNode} is called. */
69
+ addNodeAlias(alias: string, id: string): this;
70
+ /** Register a canonical predicate so {@link resolvePredicate} recognizes it without an alias. */
71
+ addPredicate(canonical: string): this;
72
+ /** Register `alias → canonical predicate` (also registers the canonical name). */
73
+ addPredicateAlias(alias: string, canonical: string): this;
74
+ /**
75
+ * Index one edge. Subject and object nodes are registered automatically (so a fixture only has to
76
+ * declare its extra nodes/aliases), and the predicate becomes a known canonical name. The edge is
77
+ * stored as a frozen copy whose endpoints are the reader's nodes.
78
+ *
79
+ * @throws when `edge.id` is already present — see the class comment.
80
+ */
81
+ addEdge(edge: GraphEdge): this;
82
+ /**
83
+ * Resolve a ref ALIAS → LABEL → ID, the order the `GraphReader` contract fixes and memory's
84
+ * adapter implements. An alias whose target node is missing falls through to the label/id lookups
85
+ * rather than reporting "unknown", because the ref may still name a node directly.
86
+ */
87
+ resolveNode(ref: string): GraphNode | null;
88
+ resolvePredicate(ref: string): string | null;
89
+ outgoing(node: GraphNode, predicate?: string, limit?: number): GraphEdge[];
90
+ incoming(node: GraphNode, predicate?: string, limit?: number): GraphEdge[];
91
+ /** Every edge of one evidence record, in insertion order, as a fresh array of frozen edges. */
92
+ edgesForFact(factId: string | number): GraphEdge[];
93
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `@avantf/graph-recall` — storage-agnostic relation-graph traversal.
3
+ *
4
+ * The package owns the algorithms that memory and knowledge share: one-hop, reverse, two-hop, AND
5
+ * and neighbour walks over a {@link GraphReader}, returning {@link RelationPath}s that keep their
6
+ * evidence. It deliberately does NOT own storage, filtering policy, extraction, planning, answer
7
+ * generation, or anything DSH/Cordis/SQLite-specific — see `docs/GRAPH_RECALL_LIBRARY.md` in the
8
+ * mem repository for the boundary and its rationale.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { InMemoryGraphReader, twoHop } from './index.js'
13
+ *
14
+ * const reader = new InMemoryGraphReader({ edges })
15
+ * const { paths, total, truncated } = twoHop(reader, zhang, '管理', '负责')
16
+ * for (const path of paths) console.log(path.evidence) // fact ids behind the path
17
+ * ```
18
+ */
19
+ export type { AndOptions, GraphEdge, GraphNode, GraphQuery, GraphReader, GraphResult, NeighborOptions, PathOptions, RelationPath, } from './types.js';
20
+ export { DEFAULT_LIMIT, MAX_AND_NODES, MAX_LIMIT, andPaths, compareRelationPaths, neighbors, oneHop, query, reverse, twoHop, } from './traverse.js';
21
+ export { InMemoryGraphReader } from './in-memory.js';
22
+ export type { InMemoryGraphReaderOptions } from './in-memory.js';
@@ -0,0 +1,119 @@
1
+ /**
2
+ * The traversal half of `@avantf/graph-recall`: five explicit, bounded operators over a
3
+ * {@link GraphReader}, plus a thin {@link query} dispatcher.
4
+ *
5
+ * Design constraints (docs/GRAPH_RECALL_LIBRARY.md §5), enforced here rather than left to callers:
6
+ *
7
+ * - **Two hops at most.** There is no recursive walk and no depth parameter beyond `1 | 2`.
8
+ * - **Every operator is bounded.** Reader calls are over-fetched to `limit × 5` (capped at
9
+ * `MAX_SCAN`), so a reader's arbitrary ordering cannot hide a better-ranked path just past the
10
+ * page; nothing iterates the whole graph. The multi-read operators cap the total work as well:
11
+ * `twoHop` and `andPaths` stop collecting at `MAX_SCAN` candidates, and `andPaths` accepts at
12
+ * most {@link MAX_AND_NODES} distinct nodes (it throws above that rather than answering a
13
+ * different question). The README's "Bounded work" table lists the cost of every operator.
14
+ * - **Paths are deduplicated by edge-id tuple** — one fact reached through two equivalent walks is
15
+ * one path.
16
+ * - **Ordering is fixed**: confidence DESC, hops ASC, evidence recency DESC, edge ids as the final
17
+ * deterministic tiebreak. The same comparator is exported as
18
+ * {@link compareRelationPaths} so a consumer merging several operators' pages (Phase 4) sorts
19
+ * them the same way.
20
+ * - **No path means an empty result**, never a fabricated one; the caller decides whether to fall
21
+ * back to fuzzy retrieval.
22
+ */
23
+ import type { AndOptions, GraphNode, GraphQuery, GraphReader, GraphResult, NeighborOptions, PathOptions, RelationPath } from './types.js';
24
+ /** Default page size of every operator (§5 "每个算子有 limit,默认 10"). */
25
+ export declare const DEFAULT_LIMIT = 10;
26
+ /**
27
+ * Hard page-size ceiling. A larger `limit` is clamped instead of honored: the package's contract is
28
+ * bounded work, and a caller asking for everything is asking for an unbounded page.
29
+ */
30
+ export declare const MAX_LIMIT = 100;
31
+ /**
32
+ * The most DISTINCT queried nodes `andPaths` accepts.
33
+ *
34
+ * Each node costs two reads, so this is the node half of that operator's bound (20 nodes × 2 reads ×
35
+ * `scan` edges). A longer list THROWS instead of being clamped: silently dropping nodes would turn
36
+ * "facts mentioning all of these entities" into a different, weaker question — the same reasoning
37
+ * that makes `query` reject `maxHops: 3`.
38
+ */
39
+ export declare const MAX_AND_NODES = 20;
40
+ /**
41
+ * The ordering every operator's page uses, exported so consumers can merge pages consistently:
42
+ * confidence DESC, hops ASC, evidence recency DESC, then edge ids (deterministic last resort).
43
+ */
44
+ export declare function compareRelationPaths(a: RelationPath, b: RelationPath): number;
45
+ /**
46
+ * Edges leaving `start` (optionally filtered by `predicate`), each as a one-hop path in the edge's
47
+ * stored direction. `includeIncoming` adds edges arriving at `start`; those paths are
48
+ * `[subject, object]` with `start` as the object.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * const { paths } = oneHop(reader, zhang, '管理')
53
+ * // [{ nodes: [张伟, 李娜], edges: [张伟 管理 李娜], evidence: [101], confidence: 0.9 }]
54
+ * ```
55
+ */
56
+ export declare function oneHop(reader: GraphReader, start: GraphNode, predicate?: string, options?: PathOptions): GraphResult;
57
+ /**
58
+ * Edges arriving at `object` (optionally filtered by `predicate`) — "谁管理李娜". Each path is the
59
+ * edge in its stored direction (`[subject, object]`), with `object` at the end and the found
60
+ * subject as the answer node.
61
+ */
62
+ export declare function reverse(reader: GraphReader, object: GraphNode, predicate?: string, options?: PathOptions): GraphResult;
63
+ /**
64
+ * Two-hop paths `start -predicate-> mid -secondPredicate-> end` ("张伟管理的人负责什么").
65
+ * `secondPredicate === undefined` accepts any predicate on the second hop.
66
+ *
67
+ * Only the FORWARD direction is walked: a path that returns to `start` is dropped (it is a cycle,
68
+ * not an answer), and a node reached twice produces one path per distinct edge pair (dedup is by
69
+ * edge ids, so two facts connecting the same nodes stay two paths with their own evidence).
70
+ */
71
+ export declare function twoHop(reader: GraphReader, start: GraphNode, predicate?: string, secondPredicate?: string, options?: PathOptions): GraphResult;
72
+ /**
73
+ * AND semantics: the evidence FACTS that contain at least `minSupport` of the queried nodes
74
+ * ("一条事实/路径同时包含多个实体"). Incident edges of every queried node are grouped by `factId`
75
+ * (edges without one form their own group), a group qualifies when at least two distinct queried
76
+ * nodes appear in it, and each qualifying edge becomes a one-hop path with its `factId` as
77
+ * evidence. Fewer than two distinct nodes can never satisfy an AND and yields an empty result.
78
+ *
79
+ * WHAT THE RETURNED PATHS ARE. Every path is ONE EDGE OF A QUALIFYING FACT — the evidence the fact
80
+ * was recorded as — **not** a path connecting the queried nodes. A fact naming 张伟, 李娜 and 王芳
81
+ * contributes all three of its edges, including 李娜 协作 王芳 between two nodes that were not both
82
+ * queried, and no single path here visits both 张伟 and 李娜 in one walk. A consumer that projects
83
+ * these paths into an answer (mem's `mode:"relation"`) must present them as "the recorded facts that
84
+ * mention these entities", never as "the relation between them"; use {@link twoHop} for the latter.
85
+ *
86
+ * This is what replaces the old `reason` action: it answers "which recorded facts connect these
87
+ * entities", not "invent a prose answer about them".
88
+ *
89
+ * Bounds: at most {@link MAX_AND_NODES} distinct queried nodes — a longer list throws, because a
90
+ * clamped AND would answer a different question — two reads per node, and at most `MAX_SCAN`
91
+ * distinct edges collected across all of them. Reaching that edge ceiling stops the collection,
92
+ * because more evidence would only be paged away, and reports `truncated: true`.
93
+ */
94
+ export declare function andPaths(reader: GraphReader, nodes: readonly GraphNode[], options?: AndOptions): GraphResult;
95
+ /**
96
+ * Distinct neighbours of `node` in both directions (or only outgoing when
97
+ * `includeIncoming: false`), each represented by its best single-hop path.
98
+ *
99
+ * Self-loops are skipped: an edge from `node` to itself makes `node` its own neighbour, which is a
100
+ * degenerate answer ("what is near 张伟" → 张伟). Memory's extractor rejects self-relations, but a
101
+ * knowledge adapter may store them, so the filter lives here rather than in one adapter.
102
+ *
103
+ * Dedup is by NEIGHBOUR id here, not by edge ids: "who is near this node" is a node question, and
104
+ * the best evidence for that neighbour (highest confidence, then most recent) is the one returned.
105
+ */
106
+ export declare function neighbors(reader: GraphReader, node: GraphNode, options?: NeighborOptions): GraphResult;
107
+ /**
108
+ * Dispatch a {@link GraphQuery} onto the operators above. This is a convenience for callers that
109
+ * already hold a structured query; it adds no planning and no interpretation.
110
+ *
111
+ * `maxHops` defaults to 2 when `secondPredicate` is present, otherwise 1. `includeIncoming` applies
112
+ * to one-hop only (a two-hop query walks forward). Anything other than 1 or 2 throws — the design
113
+ * forbids arbitrary depth, and silently clamping a request for 3 hops would hide a caller bug.
114
+ *
115
+ * `maxHops: 1` together with `secondPredicate` also throws: a one-hop query has no second hop, so
116
+ * the field can only be a caller mistake (the old behaviour dropped it silently and answered a
117
+ * different question than the caller asked).
118
+ */
119
+ export declare function query(reader: GraphReader, graphQuery: GraphQuery): GraphResult;
@@ -0,0 +1,188 @@
1
+ /**
2
+ * The storage-agnostic vocabulary of `@avantf/graph-recall`.
3
+ *
4
+ * These types are the CONTRACT between the traversal algorithms in `traverse.ts` and whatever
5
+ * storage a consumer has: memory's `edges` table, knowledge's document/entity graph, or a test
6
+ * fixture. Nothing in this file (or this package) knows about tables, SQLite, Cordis or DSH — an
7
+ * adapter maps its rows onto these shapes and owns every filtering decision (active facts, kinds,
8
+ * sources, time windows, permissions).
9
+ *
10
+ * The two invariants that make a result usable as EVIDENCE rather than a string answer:
11
+ *
12
+ * 1. every `GraphEdge` can name the record it came from (`factId` / `source`);
13
+ * 2. every `RelationPath` keeps its edges and their evidence ids, in the same order.
14
+ */
15
+ /** A graph vertex, identified by an adapter-stable id. */
16
+ export interface GraphNode {
17
+ /** Adapter-stable identity (memory: `entity_id` as a string; knowledge: doc/entity id). */
18
+ id: string;
19
+ /** Human-readable name (memory: `entities.name`; knowledge: the document/entity title). */
20
+ label: string;
21
+ /** Optional adapter-defined type (`person`, `org`, `doc`, …). Never interpreted by this library. */
22
+ kind?: string;
23
+ }
24
+ /**
25
+ * One directed, predicate-labelled relation between two nodes, as stored.
26
+ *
27
+ * `subject -> predicate -> object` is the STORED direction. An adapter that stores inverse
28
+ * predicates swapped (memory's `direction='inverse'` rule) has already normalized it before the
29
+ * edge reaches this library; walkers never flip an edge's endpoints.
30
+ */
31
+ export interface GraphEdge {
32
+ /** Adapter-stable edge identity (memory: `edge_id` as a string). Used for path dedup. */
33
+ id: string;
34
+ subject: GraphNode;
35
+ /** Canonical predicate (memory: `predicates.canonical_name`). */
36
+ predicate: string;
37
+ object: GraphNode;
38
+ /** Evidence pointer: memory `fact_id`, knowledge chunk/doc id. */
39
+ factId?: string | number;
40
+ /**
41
+ * Producer confidence in `[0, 1]`. Missing means the neutral default `0.5` — the same value
42
+ * memory's `edges.confidence` column defaults to, so an unannotated edge is not treated as a lie.
43
+ */
44
+ confidence?: number;
45
+ /** Producer of the edge (`heuristic` / `llm` / adapter-defined). Never interpreted here. */
46
+ source?: string;
47
+ /**
48
+ * When the evidence was recorded: an ISO-8601 timestamp or epoch milliseconds. Optional because
49
+ * the design's `GraphEdge` does not require it; when present it drives the recency tiebreak
50
+ * (§5 "证据时间 DESC"). An adapter maps `edges.created_at` / `facts.created_at` here.
51
+ */
52
+ observedAt?: string | number;
53
+ }
54
+ /**
55
+ * One walkable path and the evidence behind it.
56
+ *
57
+ * Direction invariant: `nodes[i]` is `edges[i].subject` and `nodes[i + 1]` is `edges[i].object`.
58
+ * A reverse query therefore returns `[subject, object]` (the edge's stored direction), NOT
59
+ * `[object, subject]` — consumers wanting the "answer node" read `edges[].subject`/`object`.
60
+ */
61
+ export interface RelationPath {
62
+ nodes: GraphNode[];
63
+ /**
64
+ * The edges walked, in order. The ARRAY is this path's own (concatenating two results cannot
65
+ * corrupt either), but the EDGE OBJECTS are the adapter's — an adapter must hand out values that
66
+ * cannot be mutated through a read (see {@link GraphReader}).
67
+ */
68
+ edges: GraphEdge[];
69
+ /**
70
+ * Product of the edge confidences (an absent confidence counts as `0.5`), rounded to 4 decimals
71
+ * so `0.9 × 0.8` is exactly `0.72` and test expectations are not float noise.
72
+ */
73
+ confidence: number;
74
+ /**
75
+ * `factId ?? edge.id` per edge, deduplicated by `String(id)` and order preserved — one id per
76
+ * piece of evidence, and `7` and `"7"` are the same fact (the same identity `edgesForFact` uses).
77
+ */
78
+ evidence: Array<string | number>;
79
+ }
80
+ /**
81
+ * A structured relation question. This is deliberately a small, explicit shape: the library has no
82
+ * natural-language planner, and Phase 3a does not add one.
83
+ */
84
+ export interface GraphQuery {
85
+ /** Node the walk starts from (already resolved by the caller/adapter). */
86
+ start: GraphNode;
87
+ /** Predicate (canonical name or an alias the reader can resolve) for the first hop. */
88
+ predicate?: string;
89
+ /**
90
+ * Predicate for the second hop; its presence implies `maxHops: 2`. Combining it with an explicit
91
+ * `maxHops: 1` throws: a one-hop query has no second hop, so the field is a caller mistake.
92
+ */
93
+ secondPredicate?: string;
94
+ /** Keep only paths whose far endpoint has this node id. */
95
+ target?: GraphNode;
96
+ /** Maximum hops. Only 1 and 2 are supported; defaults to 2 when `secondPredicate` is set, else 1. */
97
+ maxHops?: 1 | 2;
98
+ /** Maximum paths to return (default 10, clamped to `[0, MAX_LIMIT]`). */
99
+ limit?: number;
100
+ /** One-hop only: also walk edges pointing AT `start`. Ignored by two-hop queries. */
101
+ includeIncoming?: boolean;
102
+ }
103
+ /** The outcome of one operator: the bounded page plus an honest count and truncation flag. */
104
+ export interface GraphResult {
105
+ paths: RelationPath[];
106
+ /**
107
+ * `total` counts the DISTINCT candidate paths found by the operator's bounded scan — not the
108
+ * whole graph, and not the page. The scan is over-fetched to `limit × 5` (capped at 500) so a
109
+ * reader's arbitrary ordering cannot hide better-ranked paths just past `limit`.
110
+ */
111
+ total: number;
112
+ /** `true` when more distinct candidates existed than the page returned. */
113
+ truncated: boolean;
114
+ }
115
+ /**
116
+ * What a storage adapter must provide. All methods are read-only and must return results in a
117
+ * STABLE order (insertion order is fine); the library sorts by confidence/hops/recency itself.
118
+ *
119
+ * Filters (active status, kind, source, time window, permissions) belong to the adapter: this
120
+ * library never guesses them. `resolveNode` / `resolvePredicate` are the adapter's normalization
121
+ * entry points; walkers use `resolvePredicate` so an alias resolves to the canonical name before
122
+ * the read, while `resolveNode` is for callers that start from a plain string.
123
+ *
124
+ * ADAPTER OBLIGATIONS, in the order the contract suite (`test/contract.ts`) checks them:
125
+ *
126
+ * 1. **`resolveNode` is alias-first**: an explicit alias wins over a node id and over a label, so
127
+ * `resolveNode('ali')` and a node whose id is `ali` cannot mean different things in two
128
+ * adapters. The full order is alias → label/name → node id (memory's `entity_aliases` →
129
+ * `entities.name` → numeric `entity_id`). Labels are not unique; an adapter that has to choose
130
+ * between two nodes with one label must choose the same one on every call. A ref that matches
131
+ * nothing (or only whitespace) resolves to `null`.
132
+ * 2. **Edge ids are unique.** Path dedup is defined on edge ids, so one id must name one edge; an
133
+ * adapter must never hold two edges under one id (the reference adapter rejects the write, and
134
+ * the contract only requires that the duplicate is not visible).
135
+ * 3. **Reads never expose mutable internals.** A caller may mutate (or attempt to mutate) whatever
136
+ * a read returned — edges, their endpoint nodes, the node `resolveNode` returned — and the next
137
+ * read must return the unchanged graph. `InMemoryGraphReader` freezes its objects; an adapter
138
+ * that builds fresh values per read (memory does) satisfies this by construction.
139
+ * 4. **Every edge can be traced back**: `factId` is the evidence pointer (`facts.fact_id`), and
140
+ * `edgesForFact` returns every edge one fact produced, in a stable order.
141
+ */
142
+ export interface GraphReader {
143
+ /** Resolve a raw reference (alias, label/name, id — in that order) to a node, else `null`. */
144
+ resolveNode(ref: string): GraphNode | null;
145
+ /** Resolve a predicate reference (canonical name or alias) to its canonical name, else `null`. */
146
+ resolvePredicate(ref: string): string | null;
147
+ /** Edges leaving `node`, optionally filtered by canonical predicate, at most `limit` of them. */
148
+ outgoing(node: GraphNode, predicate?: string, limit?: number): GraphEdge[];
149
+ /** Edges arriving at `node`, optionally filtered by canonical predicate, at most `limit`. */
150
+ incoming(node: GraphNode, predicate?: string, limit?: number): GraphEdge[];
151
+ /** Every edge one evidence record (`factId`) produced. */
152
+ edgesForFact(factId: string | number): GraphEdge[];
153
+ }
154
+ /** Options shared by the single-hop operators ({@link oneHop} / {@link reverse}). */
155
+ export interface PathOptions {
156
+ /** Maximum paths to return. Default 10; clamped to `[0, MAX_LIMIT]`. */
157
+ limit?: number;
158
+ /** Keep only paths whose far endpoint (the node the walk leads to) has this id. */
159
+ target?: GraphNode;
160
+ /** `oneHop` only: also walk edges pointing at the start node. */
161
+ includeIncoming?: boolean;
162
+ }
163
+ /**
164
+ * Options for {@link andPaths}.
165
+ *
166
+ * `andPaths` returns ONE EDGE PER QUALIFYING FACT (the evidence the fact was recorded as), not a
167
+ * path that joins the queried nodes: a fact naming 张伟, 李娜 and 王芳 contributes all three of its
168
+ * edges even though some of them connect nodes that were not queried. Use `twoHop` for "the path
169
+ * between these nodes". At most `MAX_AND_NODES` (20) distinct nodes are accepted; more throws.
170
+ */
171
+ export interface AndOptions {
172
+ /** Maximum paths to return. Default 10; clamped to `[0, MAX_LIMIT]`. */
173
+ limit?: number;
174
+ /**
175
+ * How many DISTINCT queried nodes one evidence fact must contain for its edges to count.
176
+ * Default 2 (the minimum an AND means); values below 2 are raised to 2.
177
+ */
178
+ minSupport?: number;
179
+ }
180
+ /** Options for {@link neighbors}. */
181
+ export interface NeighborOptions {
182
+ /** Maximum distinct neighbors to return. Default 10; clamped to `[0, MAX_LIMIT]`. */
183
+ limit?: number;
184
+ /** Canonical predicate (or alias) to restrict the adjacency to. */
185
+ predicate?: string;
186
+ /** Walk incoming edges too. Default `true` — "neighbours" means both directions. */
187
+ includeIncoming?: boolean;
188
+ }
@@ -15,7 +15,7 @@ import type { AvantfLogger } from '../../mem-contract/index.js';
15
15
  * UNCOMMENTED line is warned about by the loader, so a wrong name here would be a trap rather than a
16
16
  * hint. `config_files.spec.ts` pins the ones this template names.
17
17
  */
18
- export declare const DEFAULT_COMMON_CONFIG = "# avantf-mem \u516C\u5171\u914D\u7F6E\uFF08\u5206\u5C42\u7B2C\u2461\u5C42\uFF09\u3002\u8FD9\u91CC\u5168\u662F\u6CE8\u91CA\uFF1A\u5220\u6389/\u7559\u7A7A = \u7528\u5185\u5EFA\u9ED8\u8BA4\uFF0C\u53D6\u6D88\u6CE8\u91CA\u5373\u751F\u6548\u3002\n# \u5206\u5C42\uFF08\u4F4E\u2192\u9AD8\uFF09\uFF1A\u2460 \u5185\u5EFA\u9ED8\u8BA4 \u2192 \u2461 \u672C\u6587\u4EF6 \u2192 \u2462 \u540C\u76EE\u5F55\u7684 memory.yaml / knowledge.yaml\uFF08\u7F3A\u5931=\u4E0D\u8986\u76D6\uFF09\n# \u2192 \u2463 \u73AF\u5883\u53D8\u91CF \u2192 \u2464 \u63D2\u4EF6/CLI \u663E\u5F0F\u4F20\u53C2\n# \u6240\u6709\u53EF\u7F16\u8F91\u6587\u672C\u90FD\u5728 ~/.avantf \u4E0B\uFF1Aconfigs/*.yaml\uFF08\u672C\u76EE\u5F55\uFF09\u4E0E prompts/*.md\uFF08\u6A21\u578B\u63D0\u793A\u8BCD\uFF09\u3002\n\n# \u6570\u636E\u6839\u4E0D\u5728\u8FD9\u91CC\u914D\uFF1A\u5B83\u7531 \u2463 AVANTF_HOME\uFF08\u6216 CLI --data-home\u3001\u63D2\u4EF6 profile \u7684 dataHome\uFF09\u51B3\u5B9A \u2014\u2014\n# \u672C\u6587\u4EF6\u5C31\u5728\u6570\u636E\u6839\u91CC\u9762\uFF0C\u89E3\u6790\u6839\u7684\u65F6\u5019\u8FD8\u6CA1\u8BFB\u5230\u5B83\uFF0C\u6240\u4EE5\u5199\u5728\u8FD9\u91CC\u7684 dataHome \u5BF9\u6570\u636E\u6839\u65E0\u6548\uFF08DESIGN \u00A73\uFF09\u3002\n\n# semantic: # \u5D4C\u5165\u540E\u7AEF\uFF08\u8BB0\u5FC6\u4E0E\u77E5\u8BC6\u5171\u7528\uFF0C\u8DE8\u5E93\u5206\u6570\u624D\u53EF\u6BD4\uFF09\n# backend: local_bge\n# local_model: Xenova/bge-base-zh-v1.5 # \u5FC5\u987B\u662F ONNX \u4ED3\u5E93\n# dim: 768\n# auto_download: true\n# auto_migrate: true # \u6362\u6A21\u578B/\u5BBD\u5EA6\u540E\u81EA\u52A8\u5206\u6279\u91CD\u7B97\u65E7\u7A7A\u95F4\u7684\u5411\u91CF\uFF08\u9ED8\u8BA4\u5F00\uFF09\uFF1B\u5173\u6389\u53EA\u5269\u542F\u52A8\u544A\u8B66 + vectors --fix\n# \u26A0\uFE0F cache_dir / mirror \u7531\u5BB6\u65CF\u5E95\u5EA7 @avantf/dsh-plugin-base \u7BA1\u7406\uFF1A\u5199\u5728\u8FD9\u91CC\u4F1A\u88AB\u5FFD\u7565\u5E76\u544A\u8B66\u3002\n# \u7F13\u5B58\u76EE\u5F55\u7528\u73AF\u5883\u53D8\u91CF AVANTF_MEM_MODEL_CACHE\uFF0C\u955C\u50CF\u7528 AVANTF_MEM_MODEL_MIRROR\uFF08\u6216 HF_ENDPOINT\uFF09\u3002\n\n# vectorStore:\n# backend: auto # auto = \u8D8A\u8FC7\u9608\u503C\u81EA\u52A8\u4ECE local_numpy \u8FC1\u5230 hnswlib\n# auto_thresholds:\n# hnswlib: 2000\n# hnswlib_ef_search: 256 # ANN \u7684\u53EC\u56DE/\u901F\u5EA6\u65CB\u94AE\uFF0C\u8BED\u6599\u53D8\u5927\u5148\u8C03\u5B83\n\n# retriever: # \u4E09\u6761\u68C0\u7D22\u817F\u5728\u878D\u5408\u91CC\u7684\u6743\u91CD\u4E0E\"\u76F8\u5173\u6027\u95E8\u69DB\"\n# weight_semantic: 0.55\n# weight_fts: 0.30\n# weight_jaccard: 0.15\n# # \u7EDD\u5BF9\u95E8\u69DB\u6253\u5728\u6BCF\u6761\u817F\u7684\u539F\u59CB\u5206\u4E0A\u3001fuse() \u4E4B\u524D\uFF08\u878D\u5408\u5206\u53EA\u5728\u4E00\u6B21\u67E5\u8BE2\u5185\u53EF\u6BD4\uFF0C\u4E0D\u80FD\u5F53\u95E8\u69DB\uFF09\u3002\n# # 0 = \u5173\u95ED\u8BE5\u95E8\u69DB\uFF1B\u5206\u6570\u7B49\u4E8E\u95E8\u69DB\u4FDD\u7559\u3002\u8BED\u4E49\u540E\u7AEF\u4E0D\u53EF\u7528\u65F6 min_fts_terms \u7684\u751F\u6548\u503C\u653E\u5BBD\u5230 1\u3002\n# min_semantic_similarity: 0.5 # \u8BED\u4E49\u817F\u4F59\u5F26\uFF1B0.5 \u53EA\u5BF9 bge-base-zh-v1.5/768\uFF08mean pooling\uFF09\u6807\u5B9A\u8FC7\uFF0C\u6362\u6A21\u578B\u8981\u91CD\u6807\n# min_fts_terms: 2 # FTS \u817F\uFF1A\u8FD9\u4E00\u884C\u547D\u4E2D\u51E0\u4E2A\u4E0D\u540C\u7684\u67E5\u8BE2\u8BCD\u5143\uFF08\u62C9\u4E01\u8BCD\u22655\u5B57\u7B26 + CJK 3-gram\uFF09\n# min_jaccard: 0.2 # \u5B9E\u4F53\u817F\uFF1A\u951A\u70B9\u5B9E\u4F53\u7684 Jaccard\uFF08\u4E8B\u5B9E\u5BBD\u5EA6\u9971\u548C\uFF09\uFF1B2 \u5B9E\u4F53\u547D\u4E2D 1 \u4E2A\u6B63\u597D 0.2\n# # P-11 \u4E2D\u6587\u65F6\u95F4\u7A97\u817F\uFF08\u9ED8\u8BA4\u5173\uFF09\uFF1A\u5F00\u542F\u540E\uFF0C\u5E26\u4E2D\u6587\u65F6\u95F4\u8868\u8FBE\u7684\u67E5\u8BE2\uFF08\u4E0A\u4E2A\u6708 / \u6700\u8FD1\u4E09\u5929 / 2026\u5E749\u6708 \u2026\uFF09\n# # \u4F1A\u989D\u5916\u628A\"\u4E8B\u4EF6\u65F6\u95F4 valid_from \u843D\u5728\u8BE5\u7A97\u53E3\"\u7684\u6D3B\u8DC3\u4E8B\u5B9E\u4F5C\u4E3A\u4E00\u6761\u817F\u53C2\u4E0E\u878D\u5408\u3002\u5B83\u662F**\u52A0\u4E00\u6761\u817F\u3001\u4E0D\u662F\u8FC7\u6EE4\u5668**\uFF1A\n# # \u53EA\u589E\u52A0\u5019\u9009\uFF0C\u7EDD\u4E0D\u5220\u6389\u8BED\u4E49 / FTS / \u5B9E\u4F53\u817F\u5DF2\u7ECF\u627E\u5230\u7684\u4E8B\u5B9E\uFF1B\u6CA1\u6709\u4E8B\u4EF6\u65F6\u95F4\u7684\u4E8B\u5B9E\u4E0D\u4F5C\u4E3A\u8BE5\u817F\u5019\u9009\uFF0C\n# # \u4E5F**\u4E0D\u56DE\u9000\u5230\u5199\u5165\u65F6\u95F4 created_at**\u3002\u5B83\u5171\u7528 jaccard \u6743\u91CD\u4F4D\uFF08\u5BF9\u5916\u7684\u4E09\u952E\u6743\u91CD\u4E0D\u53D8\uFF09\u3002\n# # \u4EC0\u4E48\u65F6\u5019\u8BE5\u5F00\uFF1A\u5148\u7528 mem_admin stats \u770B validity.coverage\uFF08\u6709\u591A\u5C11\u6D3B\u8DC3\u4E8B\u5B9E\u5E26\u4E8B\u4EF6\u65F6\u95F4\uFF09\uFF0C\n# # \u518D\u7528\u56DE\u5F52\u7F51\u786E\u8BA4\u5B83\u786E\u5B9E\u6539\u5584\u65F6\u95F4\u7C7B\u67E5\u8BE2\u2014\u2014\u8986\u76D6\u7387\u22480 \u65F6\u5B83\u7B49\u4E8E\u6CA1\u5F00\u3002\u5199\u5165\u4E8B\u4EF6\u65F6\u95F4\u7528 mem_remember \u7684 event_date\u3002\n# time_window: false\n\n# lifecycle:\n# purge_after_archived_days: 365 # \u5F52\u6863\u4E8B\u5B9E\u7684\u7269\u7406\u6E05\u7406\u7A97\u53E3\uFF08\u6D3B\u8DC3\u65E5\uFF09\n# contradiction_threshold: 0.6\n\n# trust:\n# decay_per_day: 0.0055\u2026\n# presence:\n# heartbeat_minutes: 60 # \u5E38\u9A7B\u8FDB\u7A0B\u7684\u5FC3\u8DF3\uFF08\u6D3B\u8DC3\u65E5\u65F6\u949F\u4E0E\u751F\u547D\u5468\u671F\u626B\u63CF\uFF09\n\n# tools: # \u5916\u90E8\u4E8C\u8FDB\u5236\uFF08pandoc\uFF09\u7684\u53D7\u7BA1\u76EE\u5F55\n# dir: '' # \u7559\u7A7A = \u5BB6\u65CF\u6839 tools\uFF08~/.avantf/env/tools\uFF09\uFF1BAVANTF_TOOLS_DIR \u53EF\u8986\u76D6\n# auto_install: true\n";
18
+ export declare const DEFAULT_COMMON_CONFIG = "# avantf-mem \u516C\u5171\u914D\u7F6E\uFF08\u5206\u5C42\u7B2C\u2461\u5C42\uFF09\u3002\u8FD9\u91CC\u5168\u662F\u6CE8\u91CA\uFF1A\u5220\u6389/\u7559\u7A7A = \u7528\u5185\u5EFA\u9ED8\u8BA4\uFF0C\u53D6\u6D88\u6CE8\u91CA\u5373\u751F\u6548\u3002\n# \u5206\u5C42\uFF08\u4F4E\u2192\u9AD8\uFF09\uFF1A\u2460 \u5185\u5EFA\u9ED8\u8BA4 \u2192 \u2461 \u672C\u6587\u4EF6 \u2192 \u2462 \u540C\u76EE\u5F55\u7684 memory.yaml\uFF08\u7F3A\u5931=\u4E0D\u8986\u76D6\uFF09\n# \u2192 \u2463 \u73AF\u5883\u53D8\u91CF \u2192 \u2464 \u63D2\u4EF6/CLI \u663E\u5F0F\u4F20\u53C2\n# \u6240\u6709\u53EF\u7F16\u8F91\u6587\u672C\u90FD\u5728 ~/.avantf \u4E0B\uFF1Aconfigs/*.yaml\uFF08\u672C\u76EE\u5F55\uFF09\u4E0E prompts/*.md\uFF08\u6A21\u578B\u63D0\u793A\u8BCD\uFF09\u3002\n\n# \u6570\u636E\u6839\u4E0D\u5728\u8FD9\u91CC\u914D\uFF1A\u5B83\u7531 \u2463 AVANTF_HOME\uFF08\u5BB6\u65CF\u6839\uFF1A\u540C\u65F6\u51B3\u5B9A\u6570\u636E\u6839\u4E0E models/tools\uFF09\u51B3\u5B9A \u2014\u2014\n# \u53EA\u60F3\u6362\u6570\u636E\u6839\u3001\u4E0D\u642C\u6A21\u578B\u7F13\u5B58\u65F6\u7528 CLI \u7684 \u2464 \u663E\u5F0F\u53C2\u6570 avantf-mem --data-home <path>\uFF08\u63D2\u4EF6\u5219\u662F profile \u7684\n# dataHome\uFF0C\u5C5E \u2461\uFF09\u3002\u672C\u6587\u4EF6\u5C31\u5728\u6570\u636E\u6839\u91CC\u9762\uFF0C\u89E3\u6790\u6839\u65F6\u8FD8\u6CA1\u8BFB\u5230\u5B83\uFF0C\u6240\u4EE5\u5199\u5728\u8FD9\u91CC\u7684 dataHome \u5BF9\u6570\u636E\u6839\u65E0\u6548\uFF08DESIGN \u00A73\uFF09\u3002\n\n# semantic: # \u5D4C\u5165\u540E\u7AEF\uFF08\u8BB0\u5FC6\u4E0E\u77E5\u8BC6\u5171\u7528\uFF0C\u8DE8\u5E93\u5206\u6570\u624D\u53EF\u6BD4\uFF09\n# backend: local_bge\n# local_model: Xenova/bge-base-zh-v1.5 # \u5FC5\u987B\u662F ONNX \u4ED3\u5E93\n# dim: 768\n# auto_download: true\n# auto_migrate: true # \u6362\u6A21\u578B/\u5BBD\u5EA6\u540E\u81EA\u52A8\u5206\u6279\u91CD\u7B97\u65E7\u7A7A\u95F4\u7684\u5411\u91CF\uFF08\u9ED8\u8BA4\u5F00\uFF09\uFF1B\u5173\u6389\u53EA\u5269\u542F\u52A8\u544A\u8B66 + vectors --fix\n# \u26A0\uFE0F cache_dir / mirror \u7531\u5BB6\u65CF\u5E95\u5EA7 @avantf/dsh-plugin-base \u7BA1\u7406\uFF1A\u5199\u5728\u8FD9\u91CC\u4F1A\u88AB\u5FFD\u7565\u5E76\u544A\u8B66\u3002\n# \u7F13\u5B58\u76EE\u5F55\u7528\u73AF\u5883\u53D8\u91CF AVANTF_MEM_MODEL_CACHE\uFF0C\u955C\u50CF\u7528 AVANTF_MEM_MODEL_MIRROR\uFF08\u6216 HF_ENDPOINT\uFF09\u3002\n\n# vectorStore:\n# backend: auto # auto = \u8D8A\u8FC7\u9608\u503C\u81EA\u52A8\u4ECE local_numpy \u8FC1\u5230 hnswlib\n# auto_thresholds:\n# hnswlib: 2000\n# hnswlib_ef_search: 256 # ANN \u7684\u53EC\u56DE/\u901F\u5EA6\u65CB\u94AE\uFF0C\u8BED\u6599\u53D8\u5927\u5148\u8C03\u5B83\n\n# retriever: # \u4E09\u6761\u68C0\u7D22\u817F\u5728\u878D\u5408\u91CC\u7684\u6743\u91CD\u4E0E\"\u76F8\u5173\u6027\u95E8\u69DB\"\n# weight_semantic: 0.55\n# weight_fts: 0.30\n# weight_jaccard: 0.15\n# # \u817F\u95E8\u69DB\u662F\u7EDD\u5BF9\u95E8\u69DB\uFF0C\u6253\u5728\u6BCF\u6761\u817F\u7684\u539F\u59CB\u5206\u4E0A\u3001fuse() \u4E4B\u524D\uFF1B0 = \u5173\u95ED\u8BE5\u95E8\u69DB\uFF0C\u5206\u6570\u7B49\u4E8E\u95E8\u69DB\u4FDD\u7559\u3002\n# # \u8BED\u4E49\u540E\u7AEF\u4E0D\u53EF\u7528\u65F6 min_fts_terms \u7684\u751F\u6548\u503C\u653E\u5BBD\u5230 1\u3002\n# min_semantic_similarity: 0.5 # \u8BED\u4E49\u817F\u4F59\u5F26\uFF1B0.5 \u53EA\u5BF9 bge-base-zh-v1.5/768\uFF08mean pooling\uFF09\u6807\u5B9A\u8FC7\uFF0C\u6362\u6A21\u578B\u8981\u91CD\u6807\n# min_fts_terms: 2 # FTS \u817F\uFF1A\u8FD9\u4E00\u884C\u547D\u4E2D\u51E0\u4E2A\u4E0D\u540C\u7684\u67E5\u8BE2\u8BCD\u5143\uFF08\u62C9\u4E01\u8BCD\u22655\u5B57\u7B26 + CJK 3-gram\uFF09\n# min_jaccard: 0.2 # \u5B9E\u4F53\u817F\uFF1A\u951A\u70B9 Jaccard \u7684**\u4E0B\u754C**\uFF1B\u5B9E\u9645\u8BC1\u636E\u95E8\u69DB\u518D\u6309\u951A\u70B9\u7A00\u6709\u5EA6\u52A0\u6743\uFF08\u00A720.19 \u7EED\uFF09\n# # min_score \u662F\u552F\u4E00\u7684**\u663E\u793A\u5206**\u95E8\u69DB\uFF0C\u4E5F\u662F UI \u663E\u793A\u7684\u90A3\u4E2A score\uFF1A\u5408\u5E76\u6C60\u6309\u6700\u5927\u878D\u5408\u5206\u5F52\u4E00\u5316\u540E\u7684\u503C\uFF0C\n# # \u662F**\u76F8\u5BF9\u91CF**\u2014\u2014\u540C\u4E00\u4E8B\u5B9E\u5728\u4E0D\u540C\u67E5\u8BE2\u91CC\u53EF\u80FD\u901A\u8FC7\u3001\u4E5F\u53EF\u80FD\u4E0D\u901A\u8FC7\uFF08\u53D6\u51B3\u4E8E\u540C\u4E00\u67E5\u8BE2\u91CC\u6709\u6CA1\u6709\u66F4\u5F3A\u7684\u5019\u9009\uFF09\u3002\n# # \u5B83\u53EA\u5E94\u7528\u4E00\u6B21\uFF1A\u5728\u5408\u5E76\u540E\u7684\u5019\u9009\u6C60\u4E0A\u3001\u6309 kind/source \u8FC7\u6EE4\u4E0E slice \u4E4B\u524D\uFF08mem_recall \u4E0E UI\n# # \u56E0\u6B64\u662F\u540C\u4E00\u4E2A\u523B\u5EA6\uFF09\uFF1B0 = \u5173\u95ED\uFF0C\u5206\u6570\u7B49\u4E8E\u95E8\u69DB\u4FDD\u7559\uFF1B\u653E\u5BBD\u6863\u53EA\u964D\u817F\u95E8\u69DB\uFF0C\u4E0D\u964D\u5B83\u3002\n# # \u26A0\uFE0F \u88AB\u5B83\u4E22\u5F03\u7684\u6761\u76EE\u4E0D\u8FDB `dropped_by_floor`\uFF0C\u56E0\u6B64 UI \u7684\u300C\u4E25\u683C\u95E8\u69DB\u4E22\u5F03\u4E86 N \u6761\u300D**\u4E0D\u542B**\u8FD9\u4E00\u7C7B\n# # \uFF08\u4FE1\u5C01\u91CC\u5355\u72EC\u6709 dropped_by_score\uFF1B\u9762\u677F\u8FD9\u4E00\u7248\u8FD8\u6CA1\u6E32\u67D3\u5B83\uFF09\u3002\n# min_score: 0.5\n# # P-11 \u4E2D\u6587\u65F6\u95F4\u7A97\u817F\uFF08\u9ED8\u8BA4\u5173\uFF09\uFF1A\u5F00\u542F\u540E\uFF0C\u5E26\u4E2D\u6587\u65F6\u95F4\u8868\u8FBE\u7684\u67E5\u8BE2\uFF08\u4E0A\u4E2A\u6708 / \u6700\u8FD1\u4E09\u5929 / 2026\u5E749\u6708 \u2026\uFF09\n# # \u4F1A\u989D\u5916\u628A\"\u4E8B\u4EF6\u65F6\u95F4 valid_from \u843D\u5728\u8BE5\u7A97\u53E3\"\u7684\u6D3B\u8DC3\u4E8B\u5B9E\u4F5C\u4E3A\u4E00\u6761\u817F\u53C2\u4E0E\u878D\u5408\u3002\u5B83\u662F**\u52A0\u4E00\u6761\u817F\u3001\u4E0D\u662F\u8FC7\u6EE4\u5668**\uFF1A\n# # \u53EA\u589E\u52A0\u5019\u9009\uFF0C\u7EDD\u4E0D\u5220\u6389\u8BED\u4E49 / FTS / \u5B9E\u4F53\u817F\u5DF2\u7ECF\u627E\u5230\u7684\u4E8B\u5B9E\uFF1B\u6CA1\u6709\u4E8B\u4EF6\u65F6\u95F4\u7684\u4E8B\u5B9E\u4E0D\u4F5C\u4E3A\u8BE5\u817F\u5019\u9009\uFF0C\n# # \u4E5F**\u4E0D\u56DE\u9000\u5230\u5199\u5165\u65F6\u95F4 created_at**\u3002\u5B83\u5171\u7528 jaccard \u6743\u91CD\u4F4D\uFF08\u5BF9\u5916\u7684\u4E09\u952E\u6743\u91CD\u4E0D\u53D8\uFF09\u3002\n# # \u4EC0\u4E48\u65F6\u5019\u8BE5\u5F00\uFF1A\u5148\u7528 mem_admin stats \u770B validity.coverage\uFF08\u6709\u591A\u5C11\u6D3B\u8DC3\u4E8B\u5B9E\u5E26\u4E8B\u4EF6\u65F6\u95F4\uFF09\uFF0C\n# # \u518D\u7528\u56DE\u5F52\u7F51\u786E\u8BA4\u5B83\u786E\u5B9E\u6539\u5584\u65F6\u95F4\u7C7B\u67E5\u8BE2\u2014\u2014\u8986\u76D6\u7387\u22480 \u65F6\u5B83\u7B49\u4E8E\u6CA1\u5F00\u3002\u5199\u5165\u4E8B\u4EF6\u65F6\u95F4\u7528 mem_remember \u7684 event_date\u3002\n# time_window: false\n\n# lifecycle:\n# purge_after_archived_days: 365 # \u5F52\u6863\u4E8B\u5B9E\u7684\u7269\u7406\u6E05\u7406\u7A97\u53E3\uFF08\u6D3B\u8DC3\u65E5\uFF09\n# contradiction_threshold: 0.6\n\n# trust:\n# decay_per_day: 0.0055\u2026\n# presence:\n# heartbeat_minutes: 60 # \u5E38\u9A7B\u8FDB\u7A0B\u7684\u5FC3\u8DF3\uFF08\u6D3B\u8DC3\u65E5\u65F6\u949F\u4E0E\u751F\u547D\u5468\u671F\u626B\u63CF\uFF09\n\n# tools: # \u5916\u90E8\u4E8C\u8FDB\u5236\uFF08pandoc\uFF09\u7684\u53D7\u7BA1\u76EE\u5F55\n# dir: '' # \u7559\u7A7A = \u5BB6\u65CF\u6839 tools\uFF08~/.avantf/env/tools\uFF09\uFF1BAVANTF_TOOLS_DIR \u53EF\u8986\u76D6\n# auto_install: true\n";
19
19
  /**
20
20
  * Ensure the common config exists with content, and hand back the path either way.
21
21
  *
@@ -1,9 +1,8 @@
1
- import { type AvantfLogger, type Config, type MemoryConfig, type KnowledgeConfig } from '../../mem-contract/index.js';
1
+ import { type AvantfLogger, type Config, type MemoryConfig } from '../../mem-contract/index.js';
2
2
  export interface LoadedConfig {
3
3
  home: string;
4
4
  common: Config;
5
5
  memory: MemoryConfig;
6
- knowledge: KnowledgeConfig;
7
6
  }
8
7
  export interface LoadConfigOptions {
9
8
  dataHome?: string;
@@ -29,7 +28,7 @@ export interface LoadConfigOptions {
29
28
  }
30
29
  /**
31
30
  * Load config with layering: ① built-in defaults → ② `~/.avantf/configs/common.yaml`
32
- * (common) → ③ `~/.avantf/{memory,knowledge}/config.yaml` (store) → ④ env →
31
+ * (common) → ③ `~/.avantf/configs/memory.yaml` (store) → ④ env →
33
32
  * ⑤ explicit. Returns the resolved per-store config + data home.
34
33
  */
35
34
  export declare function loadConfig(opts?: LoadConfigOptions): LoadedConfig;
@@ -31,23 +31,12 @@ export declare function resolveDataHome(input: {
31
31
  *
32
32
  * The layering is unchanged (① built-in → ② common → ③ store → ④ env → ⑤ explicit); only the
33
33
  * locations moved, so all of a deployment's editable text — `configs/*.yaml` and `prompts/*.md` —
34
- * is in one place instead of scattered through `memory/` and `knowledge/` next to the databases.
34
+ * is in one place instead of scattered through `memory/` next to the database.
35
35
  */
36
36
  export declare function configsDir(home: string): string;
37
37
  export declare function commonConfigPath(home: string): string;
38
38
  export declare function memoryDir(home: string): string;
39
- export declare function knowledgeDir(home: string): string;
40
39
  export declare function memoryConfigPath(home: string): string;
41
- export declare function knowledgeConfigPath(home: string): string;
42
40
  export declare function memoryDbPath(home: string, override?: string): string;
43
- export declare function knowledgeDbPath(home: string, override?: string): string;
44
- /**
45
- * The managed document directory (default `<home>/knowledge/docs`).
46
- *
47
- * One editable `.md` per document, so the corpus can be maintained with ordinary tools. It sits
48
- * BESIDE the database rather than replacing it: the file holds the body, the database holds the
49
- * identity, chunks and indexes derived from it.
50
- */
51
- export declare function knowledgeDocsDir(home: string, override?: string): string;
52
- /** Ensure the memory/knowledge layout exists under `home`. */
41
+ /** Ensure the memory layout exists under `home`. */
53
42
  export declare function ensureDataLayout(home: string): void;
@@ -6,28 +6,10 @@
6
6
  * sides of every read are filtered to ACTIVE facts: the corpus is "changed × active", so a
7
7
  * pair whose other side left it is no longer actionable (see DESIGN §11).
8
8
  */
9
- import { type ContradictionRecord } from '../../../mem-contract/index.js';
9
+ import { type ContradictionRecord, type MemoryKind } from '../../../mem-contract/index.js';
10
10
  import type { Db } from '../port.js';
11
- export interface ConflictPair {
12
- /** Row id of the pair — the handle `contradict_resolve` takes. */
13
- contradiction_id: number;
14
- fact_a: number;
15
- fact_b: number;
16
- score: number;
17
- }
18
- /** A row of `contradiction_log` as stored: the pair plus its adjudication state. */
19
- export interface StoredConflict {
20
- id: number;
21
- fact_a: number;
22
- fact_b: number;
23
- score: number;
24
- /** 0 = open, 1 = closed (see `resolution` / `loser_fact_id` for how). */
25
- resolved: number;
26
- resolution: string | null;
27
- loser_fact_id: number | null;
28
- /** `auto` (a fact left the corpus) or `verdict` (explicit adjudication). */
29
- resolved_by: string | null;
30
- }
11
+ import type { ConflictPair, StoredConflict } from '../../interfaces/repositories.js';
12
+ export type { ConflictPair, StoredConflict } from '../../interfaces/repositories.js';
31
13
  export declare class ContradictionsDao {
32
14
  private readonly db;
33
15
  constructor(db: Db);
@@ -83,7 +65,7 @@ export declare class ContradictionsDao {
83
65
  * loser (the fact that actually left the corpus), so the recorded verdict is unchanged.
84
66
  */
85
67
  resolveForFacts(factIds: readonly number[], resolution?: 'true_positive' | 'false_positive'): number;
86
- list(resolved?: number, category?: string, threshold?: number, limit?: number): ContradictionRecord[];
68
+ list(resolved?: number, kind?: MemoryKind, threshold?: number, limit?: number): ContradictionRecord[];
87
69
  /**
88
70
  * Every OPEN conflict that names `factId`, most severe first, with BOTH sides still active.
89
71
  *