agenr 0.8.8 → 0.8.9

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.9]
4
+
5
+ ### Added
6
+ - feat(extractor): broadened extraction prompt to capture personal user context (health, diet, family, occupation, location, values) even from casual or passing mentions; added 6-month durability test heuristic to distinguish durable personal facts from transient states (issue #173)
7
+ - feat(extractor): new few-shot examples for RELATIONSHIP, PREFERENCE, FACT, and EVENT types covering personal context scenarios with scoring rationale
8
+
9
+ ### Fixed
10
+ - fix(ingest): suppress redundant whole-file ignored-params warning; now fires once per ingest run via shared ExtractRunOnceFlags object instead of once per file (issue #168)
11
+ - fix(ingest): silence SQLITE_ERROR vector-index-not-found pre-fetch error during bulk ingest when vector index is intentionally absent; all other pre-fetch errors still log (issue #168)
12
+ - fix(ingest): detect .jsonl.reset.TIMESTAMP session files as JSONL adapter by extending suffix-stripping regex to handle both .deleted and .reset suffixes (issue #169)
13
+ - fix(consolidate): added merge system prompt constraint that expiry must be exactly permanent or temporary, never a date or timestamp; complements existing runtime fallback (issue #172)
14
+ - fix(daemon): daemon install plist now uses the runtime CLI path resolved from argv[1] via the injected argvFn, preventing hardcoded npm global paths from breaking pnpm installs (issue #174)
15
+
3
16
  ## [0.8.8]
4
17
 
5
18
  ### Fixed
@@ -170,6 +170,16 @@ interface ExtractChunkCompleteResult {
170
170
  durationMs?: number;
171
171
  warnings: string[];
172
172
  }
173
+ /**
174
+ * Shared mutable state object passed across multiple extractKnowledgeFromChunks
175
+ * calls (e.g., across files in a single ingest run) to suppress duplicate
176
+ * one-time warnings. When undefined, all warnings fire on every call,
177
+ * preserving backward-compatible behavior for callers that do not share state.
178
+ */
179
+ interface ExtractRunOnceFlags {
180
+ /** Set to true after the whole-file ignored-params warning fires once. */
181
+ hasWarnedWholeFileIgnoredParams?: boolean;
182
+ }
173
183
  declare function extractKnowledgeFromChunks(params: {
174
184
  file: string;
175
185
  chunks: TranscriptChunk[];
@@ -192,6 +202,8 @@ declare function extractKnowledgeFromChunks(params: {
192
202
  embeddingApiKey?: string;
193
203
  noPreFetch?: boolean;
194
204
  embedFn?: (texts: string[], apiKey: string) => Promise<number[][]>;
205
+ /** Shared warning state across calls; pass one object per ingest run to dedupe one-time logs. */
206
+ onceFlags?: ExtractRunOnceFlags;
195
207
  }): Promise<ExtractChunksResult>;
196
208
 
197
209
  interface ResolveLlmClientInput {
package/dist/cli-main.js CHANGED
@@ -7088,7 +7088,8 @@ function buildMergeContext(cluster) {
7088
7088
  "Merge the provided related entries into one canonical entry.",
7089
7089
  "Only include information explicitly stated in the source entries. Do not infer or add details not present.",
7090
7090
  "Prefer preserving temporal changes in the merged narrative.",
7091
- "Call merge_entries with your final merged result."
7091
+ "Call merge_entries with your final merged result.",
7092
+ 'expiry must be exactly the string "permanent" or "temporary" -- never a date, timestamp, or other value.'
7092
7093
  ].join("\n");
7093
7094
  let contentLimit;
7094
7095
  let payload = formatClusterEntries(cluster, contentLimit);
@@ -11164,7 +11165,7 @@ var vscodeCopilotAdapter = {
11164
11165
 
11165
11166
  // src/adapters/registry.ts
11166
11167
  async function detectAdapter(filePath) {
11167
- const strippedPath = filePath.replace(/\.deleted\.[^/\\]+$/, "");
11168
+ const strippedPath = filePath.replace(/\.(deleted|reset)\.[^/\\]+$/, "");
11168
11169
  const ext = path23.extname(strippedPath).toLowerCase();
11169
11170
  if (ext === ".jsonl") {
11170
11171
  const firstLine = await readFirstNonEmptyLine(filePath);
@@ -11725,15 +11726,15 @@ var SUBMIT_DEDUPED_KNOWLEDGE_TOOL = {
11725
11726
  // src/extractor.ts
11726
11727
  var SYSTEM_PROMPT = `You are a selective memory extraction engine. Extract only knowledge worth remembering beyond the immediate step.
11727
11728
 
11728
- Default action: SKIP. Most chunks should produce zero entries.
11729
+ Default action: SKIP. Most chunks should produce zero entries. Personal disclosures (health, diet, family structure, pets, occupation, location, values, relationships) are high-signal -- do not skip them just because the mention is brief or incidental. Apply the durability gate below to all entries including personal ones.
11729
11730
 
11730
11731
  ## Types
11731
11732
 
11732
- FACT \u2014 Verifiable information about a system, project, person, or concept.
11733
+ FACT \u2014 Verifiable information about a system, project, person, or concept. Personal facts count as first-class entries: health conditions, family members, pets, where the user lives or works, occupation, hobbies, recurring habits, and lifestyle circumstances.
11733
11734
  DECISION \u2014 A choice that constrains future options. Requires BOTH the choice AND the rationale. If rationale is missing, use fact or event instead.
11734
11735
  PREFERENCE \u2014 A stated or demonstrated preference that should influence future behavior.
11735
11736
  LESSON \u2014 An insight from experience that should change future behavior.
11736
- EVENT \u2014 A significant milestone, launch, or completion. NOT "the assistant ran git status."
11737
+ EVENT \u2014 A significant milestone, launch, completion, or one-time life moment. Includes project launches, deployments, merges; and personal milestones like starting a new job, relocating, major health events, or notable personal experiences. NOT recurring habits or routines (those are FACT or PREFERENCE). NOT "the assistant ran git status." NOT vague references with no anchoring detail. If a personal behavior is described as newly begun, prefer EVENT for the initiation; use FACT if the stable ongoing state is also established in the same context.
11737
11738
  RELATIONSHIP \u2014 A connection between named entities. Content must include both entities and the relation.
11738
11739
  TODO \u2014 A persistent future action not completed in this chunk and not a one-step session instruction.
11739
11740
 
@@ -11756,7 +11757,7 @@ Do NOT emit a completion event for:
11756
11757
  ## Durability Gate
11757
11758
 
11758
11759
  Only extract if useful in future conversations/tasks after the current immediate execution.
11759
- If uncertain whether durable, skip.
11760
+ If uncertain whether durable, apply the 6-month test: would knowing this fact 6 months from now still help an AI assist this person? Health conditions, family structure, location, diet, pets, occupation, values -> YES, extract. Current mood, today's plans, this week's weather -> NO, skip. Personal disclosures are durable by default -- if a user reveals something true about themselves, capture it even if mentioned casually or as an aside.
11760
11761
 
11761
11762
  ## Importance (1-10)
11762
11763
 
@@ -11843,8 +11844,9 @@ If you cannot name a concrete topic, skip the entry.
11843
11844
  5. Code-level implementation details likely to churn (unless architecture-level decision)
11844
11845
  6. One workflow split into multiple near-duplicate entries \u2014 merge into one
11845
11846
  7. Minor rephrases/duplicates of another extracted entry
11846
- 8. Greetings, acknowledgments, small talk
11847
+ 8. Pure pleasantries with no personal content: greetings, acknowledgments, filler phrases that reveal nothing about the user ("how are you", "thanks", "sounds good", "got it"). Do NOT suppress personal disclosures just because they appear inside casual phrasing or as asides. "I follow keto so no carbs -- anyway, back to the project" contains an extractable preference even though the user pivoted away. The pivot does not cancel the fact. Test: does the underlying disclosure reveal something true and durable about the user? If yes, extract it.
11847
11848
  9. Transient implementation status unless it represents a milestone, decision, or lesson
11849
+ 10. Transient personal states: current mood, today's fatigue, this week's busyness, temporary travel plans, passing weather references. These are not personal facts. Extract only if the condition is recurring or structural ("I'm always exhausted" may indicate a chronic health pattern worth capturing; "I'm tired today" does not).
11848
11850
 
11849
11851
  ## Explicit Memory Requests
11850
11852
 
@@ -11949,6 +11951,17 @@ RELATIONSHIP:
11949
11951
  "source_context": "Architecture discussion about memory integration"
11950
11952
  }
11951
11953
 
11954
+ RELATIONSHIP:
11955
+ {
11956
+ "type": "relationship",
11957
+ "subject": "user and sister Sarah",
11958
+ "content": "User's sister Sarah lives nearby and they see each other regularly. She helps with childcare.",
11959
+ "importance": 7,
11960
+ "expiry": "permanent",
11961
+ "tags": ["family", "personal", "relationship"],
11962
+ "source_context": "User mentioned sister while discussing weekend plans"
11963
+ }
11964
+
11952
11965
  TODO:
11953
11966
  {
11954
11967
  "type": "todo",
@@ -12015,6 +12028,39 @@ PREFERENCE:
12015
12028
  "source_context": "User mentioned scheduling preference during calendar discussion -- scored 6 not 8 because no parallel session needs to act on this immediately; it is a low-urgency convenience preference"
12016
12029
  }
12017
12030
 
12031
+ PREFERENCE:
12032
+ {
12033
+ "type": "preference",
12034
+ "subject": "user dietary preference",
12035
+ "content": "User follows a strict ketogenic diet and avoids carbohydrates. Do not suggest high-carb meals, recipes, or foods.",
12036
+ "importance": 7,
12037
+ "expiry": "permanent",
12038
+ "tags": ["diet", "keto", "personal", "health"],
12039
+ "source_context": "User mentioned diet as an aside mid-conversation -- scored 7 because future sessions about food, health, or restaurants need this; the casual phrasing does not reduce its durability"
12040
+ }
12041
+
12042
+ FACT:
12043
+ {
12044
+ "type": "fact",
12045
+ "subject": "user morning routine",
12046
+ "content": "User wakes at 6:15 AM and goes to the gym every weekday morning before work.",
12047
+ "importance": 6,
12048
+ "expiry": "permanent",
12049
+ "tags": ["routine", "health", "personal"],
12050
+ "source_context": "User described morning routine while discussing their schedule -- scored 6 not 8 because this is low-urgency biographical context; no parallel session needs to act on it immediately"
12051
+ }
12052
+
12053
+ EVENT:
12054
+ {
12055
+ "type": "event",
12056
+ "subject": "user job change",
12057
+ "content": "User started a new senior engineering role at a new company. This is a recent career change.",
12058
+ "importance": 7,
12059
+ "expiry": "permanent",
12060
+ "tags": ["career", "work", "personal"],
12061
+ "source_context": "User mentioned new job while discussing their schedule -- scored 7 not 9 because this is a significant life milestone worth preserving but does not require immediate cross-session action"
12062
+ }
12063
+
12018
12064
  // NOTE: These examples are drawn from OpenClaw transcripts (agent role labels, tool-verified claims). They provide soft cross-platform guidance for hedged-claim handling; mechanical enforcement (importance cap + unverified tag) is applied for openclaw, codex, and claude-code via applyConfidenceCap().
12019
12065
  Example: hedged agent claim (correct handling):
12020
12066
  {
@@ -12459,6 +12505,10 @@ var ParseResponseError = class extends Error {
12459
12505
  function normalize3(value) {
12460
12506
  return value.trim().toLowerCase();
12461
12507
  }
12508
+ function isVectorIndexNotFoundMessage(message) {
12509
+ const normalized = message.toLowerCase();
12510
+ return normalized.includes("vector index") && normalized.includes("not found");
12511
+ }
12462
12512
  async function preFetchRelated(chunkText2, db, embeddingApiKey, embedFn = embed, onVerbose) {
12463
12513
  const run = async () => {
12464
12514
  try {
@@ -12490,7 +12540,11 @@ async function preFetchRelated(chunkText2, db, embeddingApiKey, embedFn = embed,
12490
12540
  onVerbose?.(`[pre-fetch] ${above.length} above threshold ${PREFETCH_SIMILARITY_THRESHOLD}`);
12491
12541
  return above.slice(0, MAX_PREFETCH_RESULTS).map((candidate) => candidate.entry);
12492
12542
  } catch (error) {
12493
- onVerbose?.(`[pre-fetch] skipped: ${error instanceof Error ? error.message : String(error)}`);
12543
+ const message = error instanceof Error ? error.message : String(error);
12544
+ if (isVectorIndexNotFoundMessage(message)) {
12545
+ return [];
12546
+ }
12547
+ onVerbose?.(`[pre-fetch] skipped: ${message}`);
12494
12548
  return [];
12495
12549
  }
12496
12550
  };
@@ -13251,10 +13305,15 @@ async function extractKnowledgeFromChunks(params) {
13251
13305
  let effectiveNoPreFetch = wholeFileMode ? true : params.noPreFetch ?? false;
13252
13306
  if (wholeFileMode && params.verbose) {
13253
13307
  const ignoredParamsLine = "[whole-file] interChunkDelayMs and llmConcurrency have no effect in whole-file mode";
13254
- if (params.onVerbose) {
13255
- params.onVerbose(ignoredParamsLine);
13256
- } else {
13257
- console.warn(ignoredParamsLine);
13308
+ if (!params.onceFlags?.hasWarnedWholeFileIgnoredParams) {
13309
+ if (params.onVerbose) {
13310
+ params.onVerbose(ignoredParamsLine);
13311
+ } else {
13312
+ console.warn(ignoredParamsLine);
13313
+ }
13314
+ if (params.onceFlags) {
13315
+ params.onceFlags.hasWarnedWholeFileIgnoredParams = true;
13316
+ }
13258
13317
  }
13259
13318
  }
13260
13319
  if (wholeFileMode) {
@@ -14545,6 +14604,7 @@ async function runIngestCommand(inputPaths, options, deps) {
14545
14604
  let totalChunksFailed = 0;
14546
14605
  let filesWithChunkFailures = 0;
14547
14606
  const chunkStatsByFile = /* @__PURE__ */ new Map();
14607
+ const extractOnceFlags = { hasWarnedWholeFileIgnoredParams: false };
14548
14608
  let firstPassFailedIndexSet = /* @__PURE__ */ new Set();
14549
14609
  let bulkTeardownComplete = false;
14550
14610
  let bulkVectorRebuildDurationSeconds = null;
@@ -14699,6 +14759,7 @@ async function runIngestCommand(inputPaths, options, deps) {
14699
14759
  db: options.noPreFetch ? void 0 : db,
14700
14760
  embeddingApiKey: options.noPreFetch ? void 0 : embeddingApiKey ?? void 0,
14701
14761
  noPreFetch: options.noPreFetch === true,
14762
+ onceFlags: extractOnceFlags,
14702
14763
  onVerbose: verbose ? (line) => {
14703
14764
  clack4.log.info(line, clackOutput);
14704
14765
  } : void 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agenr",
3
- "version": "0.8.8",
3
+ "version": "0.8.9",
4
4
  "openclaw": {
5
5
  "extensions": [
6
6
  "dist/openclaw-plugin/index.js"