@aiwayds/dsh-topics-memory 0.22.0 → 0.24.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/lib/config.d.ts CHANGED
@@ -103,6 +103,28 @@ export declare const TopicsConfig: z<Schemastery.ObjectS<NoInfer<{
103
103
  /** laya-serve systemone endpoint for the pace-maker (default: the local
104
104
  * laya-serve bind from the laya repo's serve mode). */
105
105
  jevLayaUrl: z<string, string, "volatile-defined">;
106
+ /** Local embedding provider (A4 semantic reranker, spec §3.1): ''
107
+ * sentinel = the whole semantic layer is OFF (pure lexical); 'ollama' =
108
+ * the local ollama embed API (localhost is the ADR 0017 local exception).
109
+ * v1 ships no remote provider — a remote embedding may only ever enter a
110
+ * slow-lane/offline path, never the fast lane (red line ①). */
111
+ embeddingProvider: z<string, string, "volatile-defined">;
112
+ /** Embedding model; '' = provider default (ollama → nomic-embed-text),
113
+ * same sentinel pattern as distillModel. */
114
+ embeddingModel: z<string, string, "volatile-defined">;
115
+ /** Injection-package master gate (A-line, spec §2/§3): off | shadow | on.
116
+ * Carries the WHOLE new-behavior surface — query sanitization, the
117
+ * three-layer package, nearFloor semantic rerank, degradation. off = zero
118
+ * behavior change anywhere (byte-for-byte legacy); shadow = compute +
119
+ * bypass-ledger only, nothing lands in context; on = engage. */
120
+ packageMode: z<string, string, "volatile-defined">;
121
+ /** Lesson injection gate (B2, spec §7 C1 MJ-4): off | shadow | on, default
122
+ * off. off = established lessons neither inject nor shadow (zero injection-
123
+ * side behavior); shadow = the lesson priority seat double-computes into
124
+ * the bypass ledger (meta/lesson-shadow.jsonl), never lands in context;
125
+ * on = established lessons inject. Only the injection side lives here —
126
+ * the lesson loop (distill/approval) is the new repo's improveLoop gate. */
127
+ lessonInject: z<string, string, "volatile-defined">;
106
128
  }>>, Schemastery.ObjectT<NoInfer<{
107
129
  /** GitHub repo `owner/name`; empty = local-only mode (ADR 0008). */
108
130
  repo: z<string, string, "volatile-defined">;
@@ -201,6 +223,28 @@ export declare const TopicsConfig: z<Schemastery.ObjectS<NoInfer<{
201
223
  /** laya-serve systemone endpoint for the pace-maker (default: the local
202
224
  * laya-serve bind from the laya repo's serve mode). */
203
225
  jevLayaUrl: z<string, string, "volatile-defined">;
226
+ /** Local embedding provider (A4 semantic reranker, spec §3.1): ''
227
+ * sentinel = the whole semantic layer is OFF (pure lexical); 'ollama' =
228
+ * the local ollama embed API (localhost is the ADR 0017 local exception).
229
+ * v1 ships no remote provider — a remote embedding may only ever enter a
230
+ * slow-lane/offline path, never the fast lane (red line ①). */
231
+ embeddingProvider: z<string, string, "volatile-defined">;
232
+ /** Embedding model; '' = provider default (ollama → nomic-embed-text),
233
+ * same sentinel pattern as distillModel. */
234
+ embeddingModel: z<string, string, "volatile-defined">;
235
+ /** Injection-package master gate (A-line, spec §2/§3): off | shadow | on.
236
+ * Carries the WHOLE new-behavior surface — query sanitization, the
237
+ * three-layer package, nearFloor semantic rerank, degradation. off = zero
238
+ * behavior change anywhere (byte-for-byte legacy); shadow = compute +
239
+ * bypass-ledger only, nothing lands in context; on = engage. */
240
+ packageMode: z<string, string, "volatile-defined">;
241
+ /** Lesson injection gate (B2, spec §7 C1 MJ-4): off | shadow | on, default
242
+ * off. off = established lessons neither inject nor shadow (zero injection-
243
+ * side behavior); shadow = the lesson priority seat double-computes into
244
+ * the bypass ledger (meta/lesson-shadow.jsonl), never lands in context;
245
+ * on = established lessons inject. Only the injection side lives here —
246
+ * the lesson loop (distill/approval) is the new repo's improveLoop gate. */
247
+ lessonInject: z<string, string, "volatile-defined">;
204
248
  }>>, "plain">;
205
249
  export type TopicsConfigValue = {
206
250
  repo: string;
@@ -244,8 +288,18 @@ export type TopicsConfigValue = {
244
288
  jevSecretFile?: string;
245
289
  jevLayaFallback?: boolean;
246
290
  jevLayaUrl?: string;
291
+ /** Injection-quality line keys (spec §7). Optional in this TS view only
292
+ * (same sentinel pattern as the jev keys): index.ts's bare-harness
293
+ * DEFAULTS literal predates them — consumers must treat undefined as the
294
+ * documented default ('' / '' / 'off' = every new behavior zeroed). */
295
+ embeddingProvider?: string;
296
+ embeddingModel?: string;
297
+ packageMode?: 'off' | 'shadow' | 'on';
298
+ /** Lesson injection gate (B2). Optional in this TS view only (sentinel
299
+ * pattern as above) — consumers treat undefined as 'off'. */
300
+ lessonInject?: 'off' | 'shadow' | 'on';
247
301
  };
248
- export declare const CONFIG_KEYS: readonly ["repo", "autoInject", "injectDedup", "suppressEcho", "topK", "perTopicBudget", "totalBudget", "matchThreshold", "tagBoost", "injectMode", "qualityLane", "graphDepth", "recencyWindowDays", "autoObserve", "includeSubagents", "observationMaxChars", "distillEveryTurns", "distillOnSessionEnd", "distillProvider", "distillModel", "distillBatchSize", "distillMaxModelCalls", "consolidateCadence", "deprecatedTtlDays", "usageBoost", "zeroOpenDecay", "pushDebounceSeconds", "jevEnabled", "jevBackend", "jevModel", "jevTimeoutMs", "jevSecretFile", "jevLayaFallback", "jevLayaUrl"];
302
+ export declare const CONFIG_KEYS: readonly ["repo", "autoInject", "injectDedup", "suppressEcho", "topK", "perTopicBudget", "totalBudget", "matchThreshold", "tagBoost", "injectMode", "qualityLane", "graphDepth", "recencyWindowDays", "autoObserve", "includeSubagents", "observationMaxChars", "distillEveryTurns", "distillOnSessionEnd", "distillProvider", "distillModel", "distillBatchSize", "distillMaxModelCalls", "consolidateCadence", "deprecatedTtlDays", "usageBoost", "zeroOpenDecay", "pushDebounceSeconds", "jevEnabled", "jevBackend", "jevModel", "jevTimeoutMs", "jevSecretFile", "jevLayaFallback", "jevLayaUrl", "embeddingProvider", "embeddingModel", "packageMode", "lessonInject"];
249
303
  export type ConfigKey = (typeof CONFIG_KEYS)[number];
250
304
  /** CamelCase → dash-display (topK → top-k) for command output. */
251
305
  export declare function displayKey(key: string): string;
package/lib/config.js CHANGED
@@ -103,6 +103,28 @@ export const TopicsConfig = z.object({
103
103
  /** laya-serve systemone endpoint for the pace-maker (default: the local
104
104
  * laya-serve bind from the laya repo's serve mode). */
105
105
  jevLayaUrl: z.string().default('http://127.0.0.1:8000/v1/systemone').volatile(),
106
+ /** Local embedding provider (A4 semantic reranker, spec §3.1): ''
107
+ * sentinel = the whole semantic layer is OFF (pure lexical); 'ollama' =
108
+ * the local ollama embed API (localhost is the ADR 0017 local exception).
109
+ * v1 ships no remote provider — a remote embedding may only ever enter a
110
+ * slow-lane/offline path, never the fast lane (red line ①). */
111
+ embeddingProvider: z.string().default('').volatile(),
112
+ /** Embedding model; '' = provider default (ollama → nomic-embed-text),
113
+ * same sentinel pattern as distillModel. */
114
+ embeddingModel: z.string().default('').volatile(),
115
+ /** Injection-package master gate (A-line, spec §2/§3): off | shadow | on.
116
+ * Carries the WHOLE new-behavior surface — query sanitization, the
117
+ * three-layer package, nearFloor semantic rerank, degradation. off = zero
118
+ * behavior change anywhere (byte-for-byte legacy); shadow = compute +
119
+ * bypass-ledger only, nothing lands in context; on = engage. */
120
+ packageMode: z.string().default('off').volatile(),
121
+ /** Lesson injection gate (B2, spec §7 C1 MJ-4): off | shadow | on, default
122
+ * off. off = established lessons neither inject nor shadow (zero injection-
123
+ * side behavior); shadow = the lesson priority seat double-computes into
124
+ * the bypass ledger (meta/lesson-shadow.jsonl), never lands in context;
125
+ * on = established lessons inject. Only the injection side lives here —
126
+ * the lesson loop (distill/approval) is the new repo's improveLoop gate. */
127
+ lessonInject: z.string().default('off').volatile(),
106
128
  });
107
129
  export const CONFIG_KEYS = [
108
130
  'repo',
@@ -139,6 +161,10 @@ export const CONFIG_KEYS = [
139
161
  'jevSecretFile',
140
162
  'jevLayaFallback',
141
163
  'jevLayaUrl',
164
+ 'embeddingProvider',
165
+ 'embeddingModel',
166
+ 'packageMode',
167
+ 'lessonInject',
142
168
  ];
143
169
  /** CamelCase → dash-display (topK → top-k) for command output. */
144
170
  export function displayKey(key) {
@@ -214,10 +240,26 @@ export function parseConfigValue(key, raw) {
214
240
  }
215
241
  case 'distillProvider':
216
242
  case 'distillModel':
243
+ case 'embeddingModel':
217
244
  case 'jevModel':
218
245
  case 'jevSecretFile':
219
246
  case 'jevLayaUrl':
220
247
  return raw;
248
+ case 'embeddingProvider': {
249
+ if (raw === '' || raw === 'ollama')
250
+ return raw;
251
+ return { error: 'embedding-provider 仅支持 ollama(留空=语义层关;远程 provider 不进快道)' };
252
+ }
253
+ case 'packageMode': {
254
+ if (raw === 'off' || raw === 'shadow' || raw === 'on')
255
+ return raw;
256
+ return { error: 'package-mode 取值 off|shadow|on' };
257
+ }
258
+ case 'lessonInject': {
259
+ if (raw === 'off' || raw === 'shadow' || raw === 'on')
260
+ return raw;
261
+ return { error: 'lesson-inject 取值 off|shadow|on' };
262
+ }
221
263
  case 'jevLayaFallback': {
222
264
  if (raw === 'on' || raw === 'true')
223
265
  return true;
package/lib/digest.d.ts CHANGED
@@ -45,6 +45,8 @@ export interface AssembleResult {
45
45
  }[];
46
46
  /** Slow-lane pointers that entered the context (v4 §4.2). */
47
47
  slowIncluded?: string[];
48
+ /** Established lessons that entered the context (B2, spec 卷一 §6.3). */
49
+ lessonIncluded?: string[];
48
50
  }
49
51
  export interface AssembleConfig {
50
52
  perTopicBudget: number;
@@ -58,6 +60,27 @@ export interface SlowPointerInput {
58
60
  description?: string;
59
61
  why: string;
60
62
  }
63
+ /**
64
+ * One established-lesson pointer (B2, spec 卷一 §6.3): flat-mode normal-entry
65
+ * rendering with the machine-recognizable source marker (MJ-C combination
66
+ * matrix — lessonInject=on without the A3 package injects lessons as plain
67
+ * entries, tagged `lesson source:<kind>`).
68
+ */
69
+ export interface LessonPointerInput {
70
+ slug: string;
71
+ title: string;
72
+ status: string;
73
+ description?: string;
74
+ score: number;
75
+ source: string;
76
+ }
77
+ /** The `(lesson source:x)` marker on lesson entries — flat AND package form. */
78
+ export declare function lessonSourceTag(source: string): string;
79
+ /** One flat lesson entry: same two-line pointer shape + the source marker. */
80
+ export declare function lessonEntryText(input: LessonPointerInput, budgetTokens: number): {
81
+ text: string;
82
+ usedTokens: number;
83
+ };
61
84
  /**
62
85
  * One slow-lane pointer: same shape as a fast pointer plus the mandatory why
63
86
  * line (v4 §4.2 — 每条带 why 行). No score: the gate here was the LLM rerank,
@@ -69,11 +92,94 @@ export declare function slowEntryText(input: SlowPointerInput, budgetTokens: num
69
92
  };
70
93
  /**
71
94
  * Greedily pack pointer entries (hits already score-ordered) under the total
72
- * budget. Zero hits → empty text (ADR 0006: 零命中零注入).
95
+ * budget. Zero hits → empty text (ADR 0006: 零命中零注入). `lessons` (B2)
96
+ * packs after the slow pointers as flat entries with the source marker.
97
+ */
98
+ export declare function assemblePointer(entries: readonly DigestInput[], cfg: AssembleConfig, slow?: readonly SlowPointerInput[], lessons?: readonly LessonPointerInput[]): AssembleResult;
99
+ /**
100
+ * Greedily pack per-topic digests (hits already score-ordered) under the total
101
+ * budget. Zero hits → empty text (ADR 0006: 零命中零注入). `lessons` (B2).
102
+ */
103
+ export declare function assembleInjection(entries: readonly DigestInput[], cfg: AssembleConfig, slow?: readonly SlowPointerInput[], lessons?: readonly LessonPointerInput[]): AssembleResult;
104
+ /** One retrieval-side candidate, roster metadata pre-extracted by the caller. */
105
+ export interface PackageCandidate {
106
+ slug: string;
107
+ title: string;
108
+ status: string;
109
+ description?: string;
110
+ /** Conclusion section first line (fallback for a missing description). */
111
+ conclusion?: string;
112
+ score: number;
113
+ /** Retrieval reason tags (`triggers:0.83` / `title:0.50` / `tags:…` / …). */
114
+ reasons: string[];
115
+ /** Edge/lane provenance: `depends:dN` | `slow-pick` | `semantic…` | raw. */
116
+ via?: string;
117
+ /**
118
+ * Lesson priority seat (B2, spec 卷一 §6.3): sorts FIRST in the direct
119
+ * layer and is exempt from the ≤2 direct-seat cap (still budget-bound) —
120
+ * 「主层 ≤2 席中命中 lesson 至少 1 席」. Absent on every topic candidate.
121
+ */
122
+ prioritySeat?: boolean;
123
+ /** Rendered as a `(${sourceTag})` suffix on the entry head — lesson source
124
+ * marker (`lesson source:correction`), MJ-C. */
125
+ sourceTag?: string;
126
+ }
127
+ export interface PackageInput {
128
+ query: string;
129
+ /** Query lexemes the caller retrieved with (tokenize + §3.3 cleaning). */
130
+ queryTokens: string[];
131
+ /** Lexical strong hits (gate-passed); lands in the direct layer. */
132
+ direct: PackageCandidate[];
133
+ /** Graph expansion + slow-lane picks; lands in the neighborhood layer. */
134
+ neighborhood: PackageCandidate[];
135
+ /** nearFloor band (0.20–0.30) head; lands in the merged weak-signal line. */
136
+ weak: PackageCandidate[];
137
+ /** ADR 0015 usage signal — within-layer ordering tiebreak only (v1). */
138
+ usageBySlug?: Map<string, number>;
139
+ }
140
+ export interface PackageOpts {
141
+ /**
142
+ * `'on'` renders the live package; `'shadow'` means the caller double-runs
143
+ * (old assemblePointer + this) for the bypass ledger (spec §8.3). The pure
144
+ * output is identical in both modes — the mode is call-site bookkeeping.
145
+ */
146
+ mode?: 'on' | 'shadow';
147
+ }
148
+ export interface PackageResult {
149
+ text: string;
150
+ usedTokens: number;
151
+ included: string[];
152
+ dropped: {
153
+ slug: string;
154
+ reason: string;
155
+ }[];
156
+ /** Final per-layer rosters (what the 包头 counts report). */
157
+ layers: {
158
+ direct: string[];
159
+ neighborhood: string[];
160
+ weak: string[];
161
+ };
162
+ }
163
+ /** Total package cap — pinned, does not float with hit strength (A3 决议 3). */
164
+ export declare const PACKAGE_TOTAL = 600;
165
+ export declare const PACKAGE_DIRECT_MAX = 2;
166
+ export declare const PACKAGE_DIRECT_BUDGET = 120;
167
+ export declare const PACKAGE_NEIGHBOR_MAX = 3;
168
+ export declare const PACKAGE_NEIGHBOR_BUDGET = 40;
169
+ export declare const PACKAGE_WEAK_MAX = 2;
170
+ /**
171
+ * Padded-trivial arm of the downgrade guard (spec §2.3 名词实体口径, an
172
+ * implementation-time item per §11): after stripping whitespace, a query this
173
+ * short counts as 琐碎 even when the tokenizer expands CJK bigrams past 3
174
+ * tokens. Calibrated on the two named scenarios: 场景① ack query strips to
175
+ * 15 chars (downgrade), a real review query strips to 18 (kept).
73
176
  */
74
- export declare function assemblePointer(entries: readonly DigestInput[], cfg: AssembleConfig, slow?: readonly SlowPointerInput[]): AssembleResult;
177
+ export declare const PACKAGE_TRIVIAL_QUERY_CHARS = 16;
75
178
  /**
76
- * Greedily pack per-topic digests (hits already score-ordered) under the
77
- * total budget. Zero hits → empty text (ADR 0006: 零命中零注入).
179
+ * Assemble the three-layer package. Returns `null` — the whole-package skip
180
+ * signal — when the roster is empty (ADR 0006 零命中零注入) or when the
181
+ * downgrade guard fires (spec §2.3: 琐碎 query 且与全部候选 slug/title 零
182
+ * 词面实体交集). Packing is layered greedy: direct first, then neighborhood,
183
+ * then the weak line — over budget, lower layers are cut first.
78
184
  */
79
- export declare function assembleInjection(entries: readonly DigestInput[], cfg: AssembleConfig, slow?: readonly SlowPointerInput[]): AssembleResult;
185
+ export declare function assemblePackage(input: PackageInput, opts?: PackageOpts): PackageResult | null;