@remnic/bench 9.6.10 → 9.6.11

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/dist/index.d.ts CHANGED
@@ -131,6 +131,17 @@ interface BenchMemoryAdapter {
131
131
  store(sessionId: string, messages: Message[], control?: BenchPhaseControl): Promise<void>;
132
132
  recall(sessionId: string, query: string, budgetChars?: number, options?: BenchRecallOptions, control?: BenchPhaseControl): Promise<string>;
133
133
  search(query: string, limit: number, sessionId?: string, control?: BenchPhaseControl): Promise<SearchResult[]>;
134
+ /**
135
+ * Optional explicit-correction surface (issue #1584 plan item 2a). Routes a
136
+ * natural-language correction through the system's correction contract
137
+ * (plan + confirmed apply) instead of a plain turn store. Resolves
138
+ * `{ applied: false }` when the planner produced no applicable actions so
139
+ * the caller can fall back to the turn path. Adapters without an explicit
140
+ * correction surface omit this method entirely.
141
+ */
142
+ correct?(sessionId: string, text: string, at?: string, control?: BenchPhaseControl): Promise<{
143
+ applied: boolean;
144
+ }>;
134
145
  reset(sessionId?: string, control?: BenchPhaseControl): Promise<void>;
135
146
  getStats(sessionId?: string, control?: BenchPhaseControl): Promise<MemoryStats>;
136
147
  /** Wait for background summarization (e.g. LCM) to finish after store(). */
package/dist/index.js CHANGED
@@ -620,6 +620,7 @@ import {
620
620
  buildExplicitCueRecallSection,
621
621
  buildTrajectoryAnalysisRecallSection,
622
622
  collectExplicitTurnReferences,
623
+ EngramAccessService,
623
624
  expandTildePath,
624
625
  normalizeTurnExpansionEnd,
625
626
  Orchestrator,
@@ -1131,6 +1132,27 @@ function benchCoreMemoryTier(memory) {
1131
1132
  function benchCoreMemorySource(sessionId) {
1132
1133
  return `bench-replay-${createHash("sha256").update(sessionId).digest("hex").slice(0, 16)}`;
1133
1134
  }
1135
+ function resolveSessionScopedCorrectionDecision(plan, ownedIds, phase) {
1136
+ const actionTargets = [];
1137
+ for (const action of plan.actions) {
1138
+ if (action.kind === "supersede") actionTargets.push(action.loserId);
1139
+ else if (action.kind !== "redaction_rule") actionTargets.push(action.memoryId);
1140
+ }
1141
+ const foreignIds = actionTargets.filter((id) => !ownedIds.has(id));
1142
+ if (phase === "initial") {
1143
+ const ownedAffected = plan.affected.filter((entry) => ownedIds.has(entry.memoryId));
1144
+ if (plan.affected.length > 0 && ownedAffected.length === 0) {
1145
+ return { kind: "no-owned-target" };
1146
+ }
1147
+ if (ownedAffected.length < plan.affected.length) {
1148
+ return { kind: "replan", targetIds: ownedAffected.map((entry) => entry.memoryId) };
1149
+ }
1150
+ }
1151
+ if (foreignIds.length > 0) {
1152
+ return { kind: "foreign-actions", foreignIds };
1153
+ }
1154
+ return { kind: "proceed" };
1155
+ }
1134
1156
  function benchEntityStructuredFactSourceDir(memoryDir) {
1135
1157
  return path.join(memoryDir, "state", "bench-entity-structured-facts");
1136
1158
  }
@@ -1837,6 +1859,16 @@ function createAdapterFactory(mode) {
1837
1859
  coreReplayChain = coreReplayChain.catch(() => void 0).then(() => trackedCleanup).catch(() => void 0);
1838
1860
  void trackedCleanup;
1839
1861
  };
1862
+ let correctionAccess;
1863
+ const getCorrectionAccess = () => {
1864
+ if (!correctionAccess || correctionAccess.orchestrator !== state.orchestrator) {
1865
+ correctionAccess = {
1866
+ service: new EngramAccessService(state.orchestrator),
1867
+ orchestrator: state.orchestrator
1868
+ };
1869
+ }
1870
+ return correctionAccess.service;
1871
+ };
1840
1872
  return {
1841
1873
  async store(sessionId, messages, control) {
1842
1874
  throwIfBenchPhaseAborted(control, "store");
@@ -2381,6 +2413,71 @@ ${expanded.map((message) => `[${message.role}]: ${message.content}`).join("\n")}
2381
2413
  });
2382
2414
  sessionTurnCounters.clear();
2383
2415
  },
2416
+ async correct(sessionId, text, _at, control) {
2417
+ throwIfBenchPhaseAborted(control, "correct");
2418
+ sessionId = normalizeBenchSessionId(sessionId);
2419
+ const access2 = getCorrectionAccess();
2420
+ const discardPlan = async (planId) => {
2421
+ await access2.correctionDiscard(planId, { sessionKey: sessionId }).catch(() => void 0);
2422
+ };
2423
+ let plan = await withBenchPhaseAbort(
2424
+ access2.correctionPlan({ text, sessionKey: sessionId }),
2425
+ control,
2426
+ "correct"
2427
+ );
2428
+ const sessionSource = benchCoreMemorySource(sessionId);
2429
+ const owned = new Set(
2430
+ (await readBenchCoreMemories(state.orchestrator)).filter((memory) => memory.frontmatter.source === sessionSource).map((memory) => memory.frontmatter.id)
2431
+ );
2432
+ let replanned = false;
2433
+ const decision = resolveSessionScopedCorrectionDecision(plan, owned, "initial");
2434
+ if (decision.kind === "no-owned-target") {
2435
+ await discardPlan(plan.planId);
2436
+ return { applied: false };
2437
+ }
2438
+ if (decision.kind === "replan") {
2439
+ await discardPlan(plan.planId);
2440
+ replanned = true;
2441
+ plan = await withBenchPhaseAbort(
2442
+ access2.correctionPlan({
2443
+ text,
2444
+ sessionKey: sessionId,
2445
+ targetIds: decision.targetIds
2446
+ }),
2447
+ control,
2448
+ "correct"
2449
+ );
2450
+ }
2451
+ const finalDecision = resolveSessionScopedCorrectionDecision(
2452
+ plan,
2453
+ owned,
2454
+ replanned ? "replanned" : "initial"
2455
+ );
2456
+ if (finalDecision.kind === "foreign-actions") {
2457
+ await discardPlan(plan.planId);
2458
+ throw new Error(
2459
+ `correction plan drafts actions against ${finalDecision.foreignIds.length} foreign-session memory(ies) (${finalDecision.foreignIds.join(", ")}) \u2014 the bench harness cannot scope this correction; refusing to apply or mismeasure`
2460
+ );
2461
+ }
2462
+ if (plan.actions.length === 0) {
2463
+ await discardPlan(plan.planId);
2464
+ if (replanned || plan.affected.length > 0) {
2465
+ throw new Error(
2466
+ `correction planner drafted no applicable action (${replanned ? "re-planned with explicit session-owned targets" : `${plan.affected.length} candidate(s) located`}; warnings: ${plan.warnings.join("; ") || "none"}) \u2014 refusing to measure the turn path as contract behavior`
2467
+ );
2468
+ }
2469
+ return { applied: false };
2470
+ }
2471
+ const outcome = await withBenchPhaseAbort(
2472
+ access2.correctionApply(plan.planId, {
2473
+ confirm: true,
2474
+ sessionKey: sessionId
2475
+ }),
2476
+ control,
2477
+ "correct"
2478
+ );
2479
+ return { applied: outcome.status === "applied" };
2480
+ },
2384
2481
  async drain(control) {
2385
2482
  throwIfBenchPhaseAborted(control, "drain");
2386
2483
  const engine = getEngine();
@@ -3328,6 +3425,16 @@ function createTimeoutGuardedAdapter(adapter, options) {
3328
3425
  drainTimeoutMs
3329
3426
  );
3330
3427
  }
3428
+ if (adapter.correct) {
3429
+ wrapped.correct = (sessionId, text, at, control) => phaseTimeoutMs === void 0 ? adapter.correct(sessionId, text, at, control) : run(`correct session=${sessionId}`, async (signal) => {
3430
+ const merged = mergeBenchPhaseControl(signal, control);
3431
+ try {
3432
+ return await adapter.correct(sessionId, text, at, merged.control);
3433
+ } finally {
3434
+ merged.cleanup();
3435
+ }
3436
+ });
3437
+ }
3331
3438
  if (adapter.responder) {
3332
3439
  wrapped.responder = phaseTimeoutMs === void 0 ? adapter.responder : wrapResponder(adapter.responder, run);
3333
3440
  }
@@ -31955,6 +32062,142 @@ function computeMetricBundle(args) {
31955
32062
  }
31956
32063
 
31957
32064
  // src/benchmarks/remnic/memcorrect/adapters.ts
32065
+ var STOP_WORDS = /* @__PURE__ */ new Set([
32066
+ "the",
32067
+ "a",
32068
+ "an",
32069
+ "is",
32070
+ "are",
32071
+ "was",
32072
+ "were",
32073
+ "to",
32074
+ "of",
32075
+ "for",
32076
+ "and",
32077
+ "or",
32078
+ "but",
32079
+ "in",
32080
+ "on",
32081
+ "at",
32082
+ "by",
32083
+ "it",
32084
+ "my",
32085
+ "your",
32086
+ "we",
32087
+ "you",
32088
+ "i",
32089
+ "me",
32090
+ "this",
32091
+ "that",
32092
+ "with",
32093
+ "from",
32094
+ "not",
32095
+ "have",
32096
+ "has",
32097
+ "do",
32098
+ "does",
32099
+ "did",
32100
+ "be",
32101
+ "been",
32102
+ "being",
32103
+ "will",
32104
+ "would",
32105
+ "should",
32106
+ "could",
32107
+ "can",
32108
+ "may",
32109
+ "might",
32110
+ "must",
32111
+ "shall",
32112
+ "if",
32113
+ "then",
32114
+ "than",
32115
+ "so",
32116
+ "as",
32117
+ "go",
32118
+ "going",
32119
+ "forward",
32120
+ "instead",
32121
+ "last",
32122
+ "month",
32123
+ "now",
32124
+ "their",
32125
+ "them",
32126
+ "they",
32127
+ "he",
32128
+ "she",
32129
+ "his",
32130
+ "her",
32131
+ "got",
32132
+ "noting",
32133
+ "noted",
32134
+ "what",
32135
+ "setting",
32136
+ "preference",
32137
+ "update",
32138
+ "actually",
32139
+ "back",
32140
+ "went",
32141
+ "consider",
32142
+ "decided",
32143
+ "might",
32144
+ "someone",
32145
+ "asked",
32146
+ "mentioned",
32147
+ "said",
32148
+ "should",
32149
+ "change",
32150
+ "wrong",
32151
+ "saying",
32152
+ "record",
32153
+ "oh",
32154
+ "by",
32155
+ "way",
32156
+ "project"
32157
+ ]);
32158
+ function tokenize4(text) {
32159
+ const matches = text.toLowerCase().match(/[a-z0-9][a-z0-9-]*/g);
32160
+ return (matches ?? []).filter((t) => !STOP_WORDS.has(t));
32161
+ }
32162
+ function termFrequencies(tokens) {
32163
+ const tf = /* @__PURE__ */ new Map();
32164
+ for (const token of tokens) {
32165
+ tf.set(token, (tf.get(token) ?? 0) + 1);
32166
+ }
32167
+ return tf;
32168
+ }
32169
+ var PromptOnlyBaselineAdapter = class {
32170
+ label = "prompt-only-baseline";
32171
+ turns = [];
32172
+ async reset() {
32173
+ this.turns = [];
32174
+ }
32175
+ async ingestTurn(sessionKey, role, text, at) {
32176
+ this.turns.push({ sessionKey, role, text, at });
32177
+ }
32178
+ async recall(query, sessionKey) {
32179
+ const queryTf = termFrequencies(tokenize4(query));
32180
+ if (queryTf.size === 0) return [];
32181
+ const scored = this.turns.filter((turn) => turn.sessionKey === sessionKey).map((turn) => {
32182
+ const turnTf = termFrequencies(tokenize4(turn.text));
32183
+ let overlap = 0;
32184
+ for (const [term, qCount] of queryTf) {
32185
+ const tCount = turnTf.get(term);
32186
+ if (tCount !== void 0) overlap += qCount * tCount;
32187
+ }
32188
+ return { turn, overlap };
32189
+ }).filter((entry) => entry.overlap > 0).sort((a, b) => {
32190
+ if (b.overlap !== a.overlap) return b.overlap - a.overlap;
32191
+ return this.turns.indexOf(b.turn) - this.turns.indexOf(a.turn);
32192
+ }).slice(0, 5).map((entry) => entry.turn.text);
32193
+ return scored;
32194
+ }
32195
+ async correct(text, sessionKey) {
32196
+ await this.ingestTurn(sessionKey, "user", text, (/* @__PURE__ */ new Date()).toISOString());
32197
+ }
32198
+ async runMaintenance() {
32199
+ }
32200
+ };
31958
32201
  function createRemnicMemCorrectAdapter(adapter, options = {}) {
31959
32202
  const label = options.label ?? "remnic-native";
31960
32203
  const sessionPrefix = options.sessionPrefix ?? "memcorrect";
@@ -31975,7 +32218,12 @@ function createRemnicMemCorrectAdapter(adapter, options = {}) {
31975
32218
  return trimmed.split(/\n\s*\n/).map((s) => s.trim()).filter((s) => s.length > 0);
31976
32219
  },
31977
32220
  async correct(text, sessionKey, at) {
31978
- await adapter.store(`${sessionPrefix}:${sessionKey}`, [
32221
+ const scopedSession = `${sessionPrefix}:${sessionKey}`;
32222
+ if (adapter.correct) {
32223
+ const outcome = await adapter.correct(scopedSession, text, at);
32224
+ if (outcome.applied) return;
32225
+ }
32226
+ await adapter.store(scopedSession, [
31979
32227
  { role: "user", content: text, timestamp: at }
31980
32228
  ]);
31981
32229
  },
@@ -32023,7 +32271,17 @@ var FULL_OPTIONS2 = {
32023
32271
  };
32024
32272
  function resolveAdapter(options) {
32025
32273
  const override = options.benchmarkOptions?.["adapter"];
32026
- if (override && typeof override === "object" && "reset" in override && "ingestTurn" in override && "recall" in override) {
32274
+ if (typeof override === "string") {
32275
+ if (override === "prompt-only") {
32276
+ const adapter = new PromptOnlyBaselineAdapter();
32277
+ return { adapter, adapterLabel: adapter.label };
32278
+ }
32279
+ if (override !== "remnic") {
32280
+ throw new Error(
32281
+ `memcorrect-v1 adapter must be one of ["remnic", "prompt-only"] (or an in-process MemCorrectSystemAdapter object); received "${override}"`
32282
+ );
32283
+ }
32284
+ } else if (override && typeof override === "object" && "reset" in override && "ingestTurn" in override && "recall" in override) {
32027
32285
  const adapter = override;
32028
32286
  return { adapter, adapterLabel: adapter.label ?? "custom" };
32029
32287
  }
@@ -32980,7 +33238,7 @@ function assemblePack(task, condition, contract, injectSkills) {
32980
33238
  };
32981
33239
  }
32982
33240
  function classifySkillTrigger(skill, task) {
32983
- const taskTokens = tokenize4(task.prompt + " " + task.subjectKeywords.join(" "));
33241
+ const taskTokens = tokenize5(task.prompt + " " + task.subjectKeywords.join(" "));
32984
33242
  const taskSet = new Set(taskTokens);
32985
33243
  const appliesHits = skill.appliesWhen.filter((k) => taskSet.has(k));
32986
33244
  const blocksHits = skill.doesNotApplyWhen.filter((k) => taskSet.has(k));
@@ -33001,7 +33259,7 @@ function classifySkillTrigger(skill, task) {
33001
33259
  }
33002
33260
  return { considered, injected: false, reason: "no trigger overlap" };
33003
33261
  }
33004
- function tokenize4(text) {
33262
+ function tokenize5(text) {
33005
33263
  return text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((t) => t.length > 0);
33006
33264
  }
33007
33265
  function rankCandidates(pack, task, sourceItems) {
@@ -36703,7 +36961,7 @@ var PROCEDURAL_REAL_SCENARIOS_SMOKE = [
36703
36961
  ];
36704
36962
 
36705
36963
  // src/security/extraction-attack/tokenize.ts
36706
- function tokenize5(text) {
36964
+ function tokenize6(text) {
36707
36965
  return text.toLowerCase().split(/[^a-z0-9]+/u).filter((t) => t.length > 2);
36708
36966
  }
36709
36967
 
@@ -36745,7 +37003,7 @@ function createSeededRng2(seed) {
36745
37003
  }
36746
37004
  };
36747
37005
  }
36748
- var tokenizeContent = tokenize5;
37006
+ var tokenizeContent = tokenize6;
36749
37007
  function recoveryTokensFor(memory) {
36750
37008
  if (memory.tokens && memory.tokens.length > 0) {
36751
37009
  const seen = /* @__PURE__ */ new Set();
@@ -37224,11 +37482,11 @@ function createSyntheticTarget(options) {
37224
37482
  }
37225
37483
  const normalized = memories.map((m) => ({
37226
37484
  memory: m,
37227
- tokens: new Set((m.tokens ?? tokenize5(m.content)).map((t) => t.toLowerCase()))
37485
+ tokens: new Set((m.tokens ?? tokenize6(m.content)).map((t) => t.toLowerCase()))
37228
37486
  }));
37229
37487
  return {
37230
37488
  async recall(query, recallOptions) {
37231
- const qTokens = tokenize5(query);
37489
+ const qTokens = tokenize6(query);
37232
37490
  if (qTokens.length === 0) return [];
37233
37491
  const requestedNs = recallOptions?.namespace;
37234
37492
  if (enforceNamespaceAcl && requestedNs !== void 0 && requestedNs !== allowedNamespace) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/bench",
3
- "version": "9.6.10",
3
+ "version": "9.6.11",
4
4
  "description": "Retrieval latency ladder benchmarks + CI regression gates for @remnic/core",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -36,8 +36,8 @@
36
36
  "dependencies": {
37
37
  "hyparquet": "^1.25.7",
38
38
  "yaml": "^2.4.2",
39
- "@remnic/coding-graph": "^9.6.10",
40
- "@remnic/core": "^9.6.10"
39
+ "@remnic/coding-graph": "^9.6.11",
40
+ "@remnic/core": "^9.6.11"
41
41
  },
42
42
  "devDependencies": {
43
43
  "tsup": "^8.5.1",