@remnic/core 9.25.3 → 9.25.4

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Orchestrator
3
- } from "./chunk-NBT7O25Z.js";
3
+ } from "./chunk-FDAPXLRC.js";
4
4
  import "./chunk-4B77XSG5.js";
5
5
  import "./chunk-B7GROTXD.js";
6
6
  import "./chunk-DPYYSG6F.js";
@@ -61,7 +61,7 @@ import "./chunk-PHRLJFWZ.js";
61
61
  import "./chunk-64NJRYU2.js";
62
62
  import "./chunk-K2TDK7GG.js";
63
63
  import "./chunk-LTHVM4XN.js";
64
- import "./chunk-PXJ4A6IK.js";
64
+ import "./chunk-BUK2FXOL.js";
65
65
  import "./chunk-4RA3C3EV.js";
66
66
  import "./chunk-TGTTESJS.js";
67
67
  import "./chunk-54V4BZWP.js";
@@ -43,6 +43,9 @@ import {
43
43
  import {
44
44
  normalizeProcedureSteps
45
45
  } from "./chunk-QDW3E4RD.js";
46
+ import {
47
+ isMemoryCategory
48
+ } from "./chunk-CGASUJVP.js";
46
49
  import {
47
50
  FallbackLlmClient,
48
51
  fallbackLlmRuntimeContextFromConfig,
@@ -105,6 +108,10 @@ var EXTRACTION_RESPONSE_SHAPE = `{
105
108
  "identityReflection": "<conversation-grounded agent reflection>",
106
109
  "relationships": [{"source": "<normalized-name>", "target": "<normalized-name>", "label": "<source-grounded relationship>"}]
107
110
  }`;
111
+ var EXTRACTION_RESPONSE_PLACEHOLDERS = {};
112
+ for (const placeholder of EXTRACTION_RESPONSE_SHAPE.match(/<[^<>\r\n]+>/g) ?? []) {
113
+ EXTRACTION_RESPONSE_PLACEHOLDERS[placeholder] = true;
114
+ }
108
115
  var CONSOLIDATION_RESPONSE_SCHEMA = `{
109
116
  "items": [
110
117
  {
@@ -121,6 +128,35 @@ var CONSOLIDATION_RESPONSE_SCHEMA = `{
121
128
  function isPlainRecord(value) {
122
129
  return typeof value === "object" && value !== null && !Array.isArray(value);
123
130
  }
131
+ function containsExtractionPlaceholder(value) {
132
+ if (typeof value === "string") return EXTRACTION_RESPONSE_PLACEHOLDERS[value.trim()] === true;
133
+ if (Array.isArray(value)) return value.some(containsExtractionPlaceholder);
134
+ return isPlainRecord(value) && Object.values(value).some(containsExtractionPlaceholder);
135
+ }
136
+ function extractionText(value) {
137
+ if (typeof value !== "string") return void 0;
138
+ const text = value.trim();
139
+ return text.length > 0 && !containsExtractionPlaceholder(text) ? text : void 0;
140
+ }
141
+ function extractionAttributes(value) {
142
+ if (!isPlainRecord(value)) return void 0;
143
+ const attributes = {};
144
+ for (const [key, candidate] of Object.entries(value)) {
145
+ const normalizedKey = extractionText(key);
146
+ const normalizedValue = extractionText(candidate);
147
+ if (normalizedKey !== void 0 && normalizedValue !== void 0) {
148
+ attributes[normalizedKey] = normalizedValue;
149
+ }
150
+ }
151
+ return Object.keys(attributes).length > 0 ? attributes : void 0;
152
+ }
153
+ function extractionEntityType(value) {
154
+ const type = extractionText(value);
155
+ if (type === "person" || type === "project" || type === "tool" || type === "company" || type === "place" || type === "other") {
156
+ return type;
157
+ }
158
+ return void 0;
159
+ }
124
160
  function normalizeQuestion(question) {
125
161
  const priority = Number.isFinite(question.priority) ? Math.max(0, Math.min(1, question.priority)) : 0.5;
126
162
  return {
@@ -240,82 +276,111 @@ var ExtractionEngine = class {
240
276
  return !!parsed && typeof parsed === "object" && !Array.isArray(parsed) && ("facts" in parsed || "entities" in parsed || "profileUpdates" in parsed || "questions" in parsed || "relationships" in parsed || "identityReflection" in parsed);
241
277
  }
242
278
  normalizeExtractionResultPayload(parsed) {
243
- const entities = Array.isArray(parsed?.entities) ? parsed.entities.map((e) => this.normalizeEntityUpdate(e)).filter((e) => e.name.length > 0) : [];
244
- const facts = Array.isArray(parsed?.facts) ? parsed.facts.map((f) => ({
245
- category: typeof f?.category === "string" ? f.category : "fact",
246
- content: typeof f?.content === "string" ? f.content : typeof f?.text === "string" ? f.text : "",
247
- confidence: typeof f?.confidence === "number" ? f.confidence : 0.7,
248
- tags: Array.isArray(f?.tags) ? f.tags.filter((t) => typeof t === "string") : [],
249
- entityRef: typeof f?.entityRef === "string" ? f.entityRef : void 0,
250
- promptedByQuestion: typeof f?.promptedByQuestion === "string" ? f.promptedByQuestion : void 0,
251
- scope: f?.scope === "global" || f?.scope === "project" ? f.scope : void 0,
252
- structuredAttributes: f?.structuredAttributes && typeof f.structuredAttributes === "object" && !Array.isArray(f.structuredAttributes) ? Object.fromEntries(
253
- Object.entries(f.structuredAttributes).filter(([k, v]) => typeof k === "string" && typeof v === "string")
254
- ) : void 0,
255
- procedureSteps: Array.isArray(f?.procedureSteps) ? normalizeProcedureSteps(f.procedureSteps) : void 0,
256
- reasoningTrace: (() => {
257
- const candidate = f?.reasoningTrace && typeof f.reasoningTrace === "object" && !Array.isArray(f.reasoningTrace) ? f.reasoningTrace : f?.reasoning_trace && typeof f.reasoning_trace === "object" && !Array.isArray(f.reasoning_trace) ? f.reasoning_trace : null;
258
- return candidate ? normalizeReasoningTrace(candidate) ?? void 0 : void 0;
259
- })(),
260
- // Issue #1575 PR 2: the LLM provides a verbatim supporting quote
261
- // per fact. Optional/nullable per repo gotcha 6 (OpenAI Responses
262
- // API emits null for absent optional fields). The post-parse
263
- // validator (buildFactProvenance) locates this quote in the
264
- // buffered turns and builds verified ProvenanceSource[] entries.
265
- quote: typeof f?.quote === "string" && f.quote.trim().length > 0 ? f.quote : void 0,
266
- eventTime: typeof f?.eventTime === "string" && f.eventTime.trim().length > 0 ? f.eventTime.trim() : typeof f?.event_time === "string" && f.event_time.trim().length > 0 ? f.event_time.trim() : void 0
267
- })).filter((f) => f.content.length > 0) : [];
268
- const questions = Array.isArray(parsed?.questions) ? parsed.questions.map((q) => {
269
- if (typeof q === "string") return { question: q, context: "", priority: 0.5 };
279
+ const entities = Array.isArray(parsed?.entities) ? parsed.entities.map((candidate) => this.normalizeEntityUpdate(candidate)).filter((entity) => entity !== void 0 && entity.name.length > 0) : [];
280
+ const facts = Array.isArray(parsed?.facts) ? parsed.facts.map((candidate) => {
281
+ const f = isPlainRecord(candidate) ? candidate : {};
282
+ const category = typeof f.category === "string" ? f.category.trim() : "fact";
283
+ const reasoningTraceInput = isPlainRecord(f.reasoningTrace) ? f.reasoningTrace : isPlainRecord(f?.reasoning_trace) ? f.reasoning_trace : void 0;
284
+ if (!isMemoryCategory(category)) return void 0;
285
+ const procedureSteps = Array.isArray(f.procedureSteps) ? normalizeProcedureSteps(f.procedureSteps) : void 0;
286
+ const reasoningTrace = reasoningTraceInput ? normalizeReasoningTrace(reasoningTraceInput) ?? void 0 : void 0;
287
+ if (containsExtractionPlaceholder(procedureSteps) || containsExtractionPlaceholder(reasoningTrace)) {
288
+ return void 0;
289
+ }
270
290
  return {
271
- question: typeof q?.question === "string" ? q.question : typeof q?.text === "string" ? q.text : "",
272
- context: typeof q?.context === "string" ? q.context : "",
273
- priority: typeof q?.priority === "number" ? q.priority : 0.5
291
+ category,
292
+ content: extractionText(f.content) ?? extractionText(f.text) ?? "",
293
+ confidence: typeof f.confidence === "number" ? f.confidence : 0.7,
294
+ tags: Array.isArray(f.tags) ? f.tags.flatMap((tag) => {
295
+ const text = extractionText(tag);
296
+ return text === void 0 ? [] : [text];
297
+ }) : [],
298
+ entityRef: extractionText(f.entityRef),
299
+ promptedByQuestion: extractionText(f.promptedByQuestion),
300
+ scope: f.scope === "global" || f.scope === "project" ? f.scope : void 0,
301
+ structuredAttributes: extractionAttributes(f.structuredAttributes),
302
+ procedureSteps,
303
+ reasoningTrace,
304
+ quote: extractionText(f.quote),
305
+ eventTime: extractionText(f.eventTime) ?? extractionText(f.event_time)
274
306
  };
275
- }).filter((q) => q.question.length > 0) : [];
307
+ }).filter((fact) => fact !== void 0 && fact.content.length > 0) : [];
308
+ const questions = Array.isArray(parsed?.questions) ? parsed.questions.flatMap((candidate) => {
309
+ const record = isPlainRecord(candidate) ? candidate : void 0;
310
+ const question = extractionText(record?.question) ?? extractionText(record?.text) ?? extractionText(candidate);
311
+ if (question === void 0) return [];
312
+ return [{
313
+ question,
314
+ context: extractionText(record?.context) ?? "",
315
+ priority: typeof record?.priority === "number" ? record.priority : 0.5
316
+ }];
317
+ }) : [];
318
+ const profileUpdates = Array.isArray(parsed?.profileUpdates) ? parsed.profileUpdates.flatMap((candidate) => {
319
+ const update = extractionText(candidate);
320
+ return update === void 0 ? [] : [update];
321
+ }) : [];
322
+ const relationships = Array.isArray(parsed?.relationships) ? parsed.relationships.flatMap((candidate) => {
323
+ const relationship = isPlainRecord(candidate) ? candidate : void 0;
324
+ const source = extractionText(relationship?.source);
325
+ const target = extractionText(relationship?.target);
326
+ const label = extractionText(relationship?.label);
327
+ if (source === void 0 || target === void 0 || label === void 0) return [];
328
+ return [{
329
+ source,
330
+ target,
331
+ label,
332
+ promptedByQuestion: extractionText(relationship?.promptedByQuestion)
333
+ }];
334
+ }) : void 0;
276
335
  return {
277
336
  facts,
278
337
  entities,
279
- profileUpdates: Array.isArray(parsed?.profileUpdates) ? parsed.profileUpdates.filter((u) => typeof u === "string" && u.trim().length > 0) : [],
338
+ profileUpdates,
280
339
  questions,
281
- identityReflection: parsed?.identityReflection ?? void 0,
282
- relationships: Array.isArray(parsed?.relationships) ? parsed.relationships.filter(
283
- (r) => typeof r?.source === "string" && typeof r?.target === "string" && typeof r?.label === "string"
284
- ).map((r) => ({
285
- source: r.source,
286
- target: r.target,
287
- label: r.label,
288
- promptedByQuestion: typeof r?.promptedByQuestion === "string" ? r.promptedByQuestion : void 0
289
- })) : void 0
340
+ identityReflection: extractionText(parsed?.identityReflection),
341
+ relationships
290
342
  };
291
343
  }
292
344
  normalizeEntityUpdate(entity) {
293
- const rawUpdates = isPlainRecord(entity?.updates) ? entity.updates : null;
294
- const directFacts = Array.isArray(entity?.facts) ? entity.facts.filter((fact) => typeof fact === "string").map((fact) => fact.trim()).filter((fact) => fact.length > 0) : [];
295
- const updateFacts = rawUpdates && Array.isArray(rawUpdates.facts) ? rawUpdates.facts.filter((fact) => typeof fact === "string").map((fact) => fact.trim()).filter((fact) => fact.length > 0) : [];
296
- const scalarUpdateFacts = rawUpdates ? Object.keys(rawUpdates).sort((a, b) => a.localeCompare(b)).filter((key) => !["facts", "name", "promptedByQuestion", "structuredSections", "type"].includes(key)).flatMap((key) => {
297
- const value = rawUpdates[key];
298
- if (typeof value === "string" && value.trim().length > 0) {
299
- return [`${key}: ${value.trim()}`];
345
+ const record = isPlainRecord(entity) ? entity : {};
346
+ const rawUpdates = isPlainRecord(record.updates) ? record.updates : void 0;
347
+ const normalizedTexts = (value) => Array.isArray(value) ? value.flatMap((candidate) => {
348
+ const text = extractionText(candidate);
349
+ return text === void 0 ? [] : [text];
350
+ }) : [];
351
+ const directFacts = normalizedTexts(record.facts);
352
+ const updateFacts = normalizedTexts(rawUpdates?.facts);
353
+ const scalarUpdateFacts = rawUpdates ? Object.entries(rawUpdates).sort(([left], [right]) => left.localeCompare(right)).flatMap(([key, value]) => {
354
+ if (["facts", "name", "promptedByQuestion", "structuredSections", "type"].includes(key)) {
355
+ return [];
300
356
  }
357
+ const normalizedKey = extractionText(key);
358
+ if (normalizedKey === void 0) return [];
359
+ const normalizedValue = extractionText(value);
360
+ if (normalizedValue !== void 0) return [`${normalizedKey}: ${normalizedValue}`];
301
361
  if (typeof value === "number" || typeof value === "boolean") {
302
- return [`${key}: ${String(value)}`];
362
+ return [`${normalizedKey}: ${String(value)}`];
303
363
  }
304
364
  return [];
305
365
  }) : [];
306
- const structuredSectionsSource = Array.isArray(entity?.structuredSections) ? entity.structuredSections : Array.isArray(rawUpdates?.structuredSections) ? rawUpdates.structuredSections : [];
307
- const name = typeof entity?.name === "string" ? entity.name.trim() : typeof entity?.entityId === "string" ? entity.entityId.trim() : typeof rawUpdates?.name === "string" ? rawUpdates.name.trim() : "";
308
- const type = typeof entity?.type === "string" && entity.type.trim().length > 0 ? entity.type.trim() : typeof rawUpdates?.type === "string" && rawUpdates.type.trim().length > 0 ? rawUpdates.type.trim() : "other";
366
+ const structuredSectionsSource = Array.isArray(record.structuredSections) ? record.structuredSections : Array.isArray(rawUpdates?.structuredSections) ? rawUpdates.structuredSections : [];
367
+ const structuredSections = structuredSectionsSource.flatMap((candidate) => {
368
+ const section = isPlainRecord(candidate) ? candidate : {};
369
+ const key = extractionText(section.key);
370
+ const title = extractionText(section.title);
371
+ const facts = normalizedTexts(section.facts);
372
+ if (key === void 0 || title === void 0 || facts.length === 0) return [];
373
+ return [{ key, title, facts }];
374
+ });
375
+ const rawType = record.type ?? rawUpdates?.type;
376
+ const type = rawType === void 0 ? "other" : extractionEntityType(rawType);
377
+ if (type === void 0) return void 0;
309
378
  return {
310
- name,
379
+ name: extractionText(record.name) ?? extractionText(record.entityId) ?? extractionText(rawUpdates?.name) ?? "",
311
380
  type,
312
381
  facts: [...directFacts, ...updateFacts, ...scalarUpdateFacts],
313
- structuredSections: structuredSectionsSource.length > 0 ? structuredSectionsSource.map((section) => ({
314
- key: typeof section?.key === "string" ? section.key.trim() : "",
315
- title: typeof section?.title === "string" ? section.title.trim() : "",
316
- facts: Array.isArray(section?.facts) ? section.facts.filter((fact) => typeof fact === "string").map((fact) => fact.trim()).filter((fact) => fact.length > 0) : []
317
- })).filter((section) => section.key.length > 0 && section.title.length > 0 && section.facts.length > 0) : void 0,
318
- promptedByQuestion: typeof entity?.promptedByQuestion === "string" ? entity.promptedByQuestion : typeof rawUpdates?.promptedByQuestion === "string" ? rawUpdates.promptedByQuestion : void 0
382
+ structuredSections: structuredSections.length > 0 ? structuredSections : void 0,
383
+ promptedByQuestion: extractionText(record.promptedByQuestion) ?? extractionText(rawUpdates?.promptedByQuestion)
319
384
  };
320
385
  }
321
386
  parseJsonObject(content) {
@@ -403,7 +468,7 @@ var ExtractionEngine = class {
403
468
  const profileUpdates = (Array.isArray(result.profileUpdates) ? result.profileUpdates : []).map(
404
469
  (update) => typeof update === "string" ? update.trim() : typeof update?.content === "string" ? update.content.trim() : ""
405
470
  ).filter((update) => update.length > 0);
406
- const entityUpdates = (Array.isArray(result.entityUpdates) ? result.entityUpdates : []).map((entity) => this.normalizeEntityUpdate(entity)).filter((entity) => entity.name.length > 0);
471
+ const entityUpdates = (Array.isArray(result.entityUpdates) ? result.entityUpdates : []).map((entity) => this.normalizeEntityUpdate(entity)).filter((entity) => entity !== void 0 && entity.name.length > 0);
407
472
  return { items, profileUpdates, entityUpdates };
408
473
  }
409
474
  async applyProactiveQuestionPass(conversation, base) {
@@ -982,22 +1047,8 @@ var ExtractionEngine = class {
982
1047
  log.debug(
983
1048
  `extracted ${result.facts.length} facts, ${result.entities.length} entities, ${(result.questions ?? []).length} questions via fallback (${detailed.modelUsed})`
984
1049
  );
985
- const normalizedFacts = result.facts.map((f) => {
986
- if (!f) return f;
987
- const eventTime = typeof f.eventTime === "string" && f.eventTime.trim().length > 0 ? f.eventTime.trim() : typeof f.event_time === "string" && f.event_time.trim().length > 0 ? f.event_time.trim() : void 0;
988
- if (!f.reasoningTrace && eventTime === void 0) return f;
989
- return {
990
- ...f,
991
- ...f.reasoningTrace ? { reasoningTrace: normalizeReasoningTrace(f.reasoningTrace) ?? void 0 } : {},
992
- ...eventTime !== void 0 ? { eventTime } : {}
993
- };
994
- });
995
- const sanitized = this.sanitizeExtractionResult({
996
- ...result,
997
- facts: normalizedFacts,
998
- questions: result.questions ?? [],
999
- identityReflection: result.identityReflection ?? void 0
1000
- }, messageTimestamp);
1050
+ const normalized = this.normalizeExtractionResultPayload(result);
1051
+ const sanitized = this.sanitizeExtractionResult(normalized, messageTimestamp);
1001
1052
  const finalResult = await this.applyProactiveQuestionPass(conversation, sanitized);
1002
1053
  return this.attachProvenanceToResult(finalResult, boundedTurns);
1003
1054
  }
@@ -1197,37 +1248,14 @@ ${EXTRACTION_RESPONSE_SHAPE}`
1197
1248
  * Local LLMs sometimes hit token limits mid-JSON. This tries to salvage valid facts.
1198
1249
  */
1199
1250
  extractPartialFacts(jsonStr) {
1200
- const allowedCategories = /* @__PURE__ */ new Set([
1201
- "fact",
1202
- "preference",
1203
- "correction",
1204
- "entity",
1205
- "decision",
1206
- "relationship",
1207
- "principle",
1208
- "commitment",
1209
- "moment",
1210
- "skill",
1211
- "rule",
1212
- "procedure",
1213
- "reasoning_trace"
1214
- ]);
1215
- const allowedEntityTypes = /* @__PURE__ */ new Set([
1216
- "person",
1217
- "project",
1218
- "tool",
1219
- "company",
1220
- "place",
1221
- "other"
1222
- ]);
1223
1251
  const facts = [];
1224
1252
  const entities = [];
1225
1253
  try {
1226
1254
  const factRegex = /\{\s*"category"\s*:\s*"([^"]+)"\s*,\s*"content"\s*:\s*"([^"]+)"\s*,\s*"confidence"\s*:\s*([0-9.]+)/g;
1227
1255
  let match;
1228
1256
  while ((match = factRegex.exec(jsonStr)) !== null) {
1229
- const rawCat = match[1];
1230
- const category = allowedCategories.has(rawCat) ? rawCat : "fact";
1257
+ const category = match[1]?.trim() ?? "";
1258
+ if (!isMemoryCategory(category)) continue;
1231
1259
  facts.push({
1232
1260
  category,
1233
1261
  content: match[2].replace(/\\n/g, "\n").replace(/\\"/g, '"'),
@@ -1237,8 +1265,8 @@ ${EXTRACTION_RESPONSE_SHAPE}`
1237
1265
  }
1238
1266
  const entityRegex = /\{\s*"name"\s*:\s*"([^"]+)"\s*,\s*"type"\s*:\s*"([^"]+)"/g;
1239
1267
  while ((match = entityRegex.exec(jsonStr)) !== null) {
1240
- const rawType = match[2];
1241
- const type = allowedEntityTypes.has(rawType) ? rawType : "other";
1268
+ const type = extractionEntityType(match[2]);
1269
+ if (type === void 0) continue;
1242
1270
  entities.push({
1243
1271
  name: match[1],
1244
1272
  type,
@@ -1247,7 +1275,7 @@ ${EXTRACTION_RESPONSE_SHAPE}`
1247
1275
  }
1248
1276
  } catch {
1249
1277
  }
1250
- return { facts, entities, profileUpdates: [], questions: [] };
1278
+ return this.normalizeExtractionResultPayload({ facts, entities, profileUpdates: [], questions: [] });
1251
1279
  }
1252
1280
  /**
1253
1281
  * Bi-temporal event-time extraction instruction (#1578 PR2). Emitted on
@@ -2405,4 +2433,4 @@ export {
2405
2433
  shouldEnableLocalExtractionThinking,
2406
2434
  ExtractionEngine
2407
2435
  };
2408
- //# sourceMappingURL=chunk-PXJ4A6IK.js.map
2436
+ //# sourceMappingURL=chunk-BUK2FXOL.js.map