agenr 1.8.1 → 1.8.2

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
@@ -2,6 +2,27 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.8.2] - 2026-04-12
6
+
7
+ Temporal parsing and ingest workflow polish patch release.
8
+
9
+ ### Changed
10
+
11
+ - **Relative time parsing now accepts small spelled-out amounts.** Episode temporal-window parsing now recognizes natural language queries such as "two days ago" for small relative ranges, improving recall routing for conversational phrasing.
12
+ - **Local Cursor scaffolding stays out of repo status.** `.gitignore` now excludes local Cursor rules and skills directories so release work and day-to-day development stay focused on product changes.
13
+
14
+ ### Fixed
15
+
16
+ - **Ingest progress propagation now reaches the CLI consistently.** The ingestion app and CLI layers now forward progress events end-to-end so long-running ingest runs surface stage updates reliably.
17
+
18
+ ### Validation
19
+
20
+ Changes since last push to `origin/master`:
21
+
22
+ - Update `.gitignore` to exclude cursor rules and skills directories
23
+ - Enhance temporal parsing with small spelled-out relative amounts
24
+ - Enhance ingestion process with progress event propagation
25
+
5
26
  ## [1.8.1] - 2026-04-11
6
27
 
7
28
  Ingest concurrency and progress reporting patch release.
@@ -7,7 +7,7 @@ import {
7
7
  parseTuiSessionKey,
8
8
  readOpenClawSessionsStore,
9
9
  storeEntriesDetailed
10
- } from "../../chunk-O45JQ6O3.js";
10
+ } from "../../chunk-PVYS6BMG.js";
11
11
  import {
12
12
  EMBEDDING_DIMENSIONS,
13
13
  ENTRY_TYPES,
@@ -24,7 +24,7 @@ import {
24
24
  resolveEmbeddingModel,
25
25
  runUnifiedRecall,
26
26
  validateTemporalValidityRange
27
- } from "../../chunk-LVDQXSHP.js";
27
+ } from "../../chunk-SQARNOYD.js";
28
28
  import {
29
29
  resolveClaimSlotPolicy
30
30
  } from "../../chunk-GUDCFFRV.js";
@@ -22,7 +22,7 @@ import {
22
22
  readOptionalString,
23
23
  readRequiredString,
24
24
  validateTemporalValidityRange
25
- } from "./chunk-LVDQXSHP.js";
25
+ } from "./chunk-SQARNOYD.js";
26
26
  import {
27
27
  compactClaimKey,
28
28
  describeClaimKeyNormalizationFailure,
@@ -2737,7 +2737,7 @@ async function extractClaimKeyDecision(entry, llm, config, options = {}) {
2737
2737
  async function getEntityHints(db) {
2738
2738
  return db.getDistinctClaimKeyPrefixes();
2739
2739
  }
2740
- async function runBatchClaimExtraction(results, ports, config, concurrency = 10, onWarning, onDiagnostic) {
2740
+ async function runBatchClaimExtraction(results, ports, config, concurrency = 10, onWarning, onDiagnostic, onProgress) {
2741
2741
  if (!config.enabled) {
2742
2742
  return /* @__PURE__ */ new Map();
2743
2743
  }
@@ -2748,6 +2748,8 @@ async function runBatchClaimExtraction(results, ports, config, concurrency = 10,
2748
2748
  const retryEntries = [];
2749
2749
  const stageSize = normalizeClaimExtractionConcurrency(concurrency);
2750
2750
  const orderedEntries = results.flatMap((result) => result.entries);
2751
+ const totalEligibleEntries = orderedEntries.filter((entry) => !entry.claim_key && config.eligibleTypes.includes(entry.type)).length;
2752
+ let completedPrimaryEntries = 0;
2751
2753
  for (let stageStart = 0; stageStart < orderedEntries.length; stageStart += stageSize) {
2752
2754
  const stageEntries = orderedEntries.slice(stageStart, stageStart + stageSize);
2753
2755
  const stageRequests = [];
@@ -2774,11 +2776,22 @@ async function runBatchClaimExtraction(results, ports, config, concurrency = 10,
2774
2776
  hintSnapshot: buildClaimExtractionHintSnapshot(hintState, entry)
2775
2777
  });
2776
2778
  }
2777
- const stageDecisions = await Promise.all(
2778
- stageRequests.map(async ({ entry, hintSnapshot }) => ({
2779
- entry,
2780
- decision: await extractBatchClaimKeyDecision(entry, llm, config, hintSnapshot, onWarning)
2781
- }))
2779
+ const stageDecisions = await executeClaimExtractionStageRequests(
2780
+ stageRequests,
2781
+ llm,
2782
+ config,
2783
+ onWarning,
2784
+ completedPrimaryEntries,
2785
+ totalEligibleEntries,
2786
+ (completedEntries, totalEntries) => {
2787
+ completedPrimaryEntries = completedEntries;
2788
+ onProgress?.({
2789
+ phase: "primary",
2790
+ completedEntries,
2791
+ totalEntries,
2792
+ totalEligibleEntries
2793
+ });
2794
+ }
2782
2795
  );
2783
2796
  for (const { entry, decision } of stageDecisions) {
2784
2797
  diagnostics.set(entry, decision.diagnostic);
@@ -2792,16 +2805,30 @@ async function runBatchClaimExtraction(results, ports, config, concurrency = 10,
2792
2805
  }
2793
2806
  }
2794
2807
  if (retryEntries.length > 0 && extractedEntries.size > 0) {
2795
- for (let stageStart = 0; stageStart < retryEntries.length; stageStart += stageSize) {
2796
- const stageRequests = retryEntries.slice(stageStart, stageStart + stageSize).filter((entry) => !entry.claim_key).map((entry) => ({
2808
+ const retryEligibleEntries = retryEntries.filter((entry) => !entry.claim_key);
2809
+ const totalRetryEntries = retryEligibleEntries.length;
2810
+ let completedRetryEntries = 0;
2811
+ for (let stageStart = 0; stageStart < retryEligibleEntries.length; stageStart += stageSize) {
2812
+ const stageRequests = retryEligibleEntries.slice(stageStart, stageStart + stageSize).map((entry) => ({
2797
2813
  entry,
2798
2814
  hintSnapshot: buildClaimExtractionHintSnapshot(hintState, entry)
2799
2815
  }));
2800
- const stageDecisions = await Promise.all(
2801
- stageRequests.map(async ({ entry, hintSnapshot }) => ({
2802
- entry,
2803
- decision: await extractBatchClaimKeyDecision(entry, llm, config, hintSnapshot, onWarning)
2804
- }))
2816
+ const stageDecisions = await executeClaimExtractionStageRequests(
2817
+ stageRequests,
2818
+ llm,
2819
+ config,
2820
+ onWarning,
2821
+ completedRetryEntries,
2822
+ totalRetryEntries,
2823
+ (completedEntries, totalEntries) => {
2824
+ completedRetryEntries = completedEntries;
2825
+ onProgress?.({
2826
+ phase: "retry",
2827
+ completedEntries,
2828
+ totalEntries,
2829
+ totalEligibleEntries
2830
+ });
2831
+ }
2805
2832
  );
2806
2833
  for (const { entry, decision } of stageDecisions) {
2807
2834
  diagnostics.set(entry, decision.diagnostic);
@@ -2824,6 +2851,20 @@ async function runBatchClaimExtraction(results, ports, config, concurrency = 10,
2824
2851
  }
2825
2852
  return extractedEntries;
2826
2853
  }
2854
+ async function executeClaimExtractionStageRequests(stageRequests, llm, config, onWarning, initialCompletedEntries, totalEntries, onProgress) {
2855
+ let completedEntries = initialCompletedEntries;
2856
+ return Promise.all(
2857
+ stageRequests.map(async ({ entry, hintSnapshot }) => {
2858
+ const decision = await extractBatchClaimKeyDecision(entry, llm, config, hintSnapshot, onWarning);
2859
+ completedEntries += 1;
2860
+ onProgress(completedEntries, totalEntries);
2861
+ return {
2862
+ entry,
2863
+ decision
2864
+ };
2865
+ })
2866
+ );
2867
+ }
2827
2868
  function normalizeClaimExtractionConcurrency(value) {
2828
2869
  if (!Number.isInteger(value) || value <= 0) {
2829
2870
  return 10;
@@ -4094,6 +4094,20 @@ function parseTimestamp(value) {
4094
4094
  // src/core/episode/temporal-window.ts
4095
4095
  var DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1e3;
4096
4096
  var DEFAULT_ANCHOR_RADIUS_DAYS = 3;
4097
+ var RELATIVE_NUMBER_WORDS = /* @__PURE__ */ new Map([
4098
+ ["one", 1],
4099
+ ["two", 2],
4100
+ ["three", 3],
4101
+ ["four", 4],
4102
+ ["five", 5],
4103
+ ["six", 6],
4104
+ ["seven", 7],
4105
+ ["eight", 8],
4106
+ ["nine", 9],
4107
+ ["ten", 10],
4108
+ ["eleven", 11],
4109
+ ["twelve", 12]
4110
+ ]);
4097
4111
  var MONTH_INDEX = /* @__PURE__ */ new Map([
4098
4112
  ["january", 0],
4099
4113
  ["february", 1],
@@ -4243,9 +4257,9 @@ function parseTemporalWindow(text, now = /* @__PURE__ */ new Date()) {
4243
4257
  now: referenceNow
4244
4258
  });
4245
4259
  }
4246
- const relativeMatch = lower.match(/\b(\d+)\s+(day|days|week|weeks|month|months)\s+ago\b/);
4260
+ const relativeMatch = lower.match(/\b(\d+|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)\s+(day|days|week|weeks|month|months)\s+ago\b/);
4247
4261
  if (relativeMatch?.[1] && relativeMatch[2]) {
4248
- const amount = Number(relativeMatch[1]);
4262
+ const amount = parseRelativeAmount(relativeMatch[1]);
4249
4263
  if (Number.isFinite(amount) && amount > 0) {
4250
4264
  const unit = relativeMatch[2];
4251
4265
  if (unit.startsWith("day")) {
@@ -4446,6 +4460,12 @@ function buildLocalDateAtNoon(year, month, day) {
4446
4460
  }
4447
4461
  return parsed;
4448
4462
  }
4463
+ function parseRelativeAmount(value) {
4464
+ if (/^\d+$/u.test(value)) {
4465
+ return Number(value);
4466
+ }
4467
+ return RELATIVE_NUMBER_WORDS.get(value) ?? Number.NaN;
4468
+ }
4449
4469
  function resolveWeekStartDay() {
4450
4470
  try {
4451
4471
  const locale = Intl.DateTimeFormat().resolvedOptions().locale;
package/dist/cli.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  tokenizeGroundingText,
28
28
  validateEntriesWithIndexes,
29
29
  validateSupersessionRules
30
- } from "./chunk-O45JQ6O3.js";
30
+ } from "./chunk-PVYS6BMG.js";
31
31
  import {
32
32
  DEFAULT_CLAIM_EXTRACTION_CONCURRENCY,
33
33
  DEFAULT_SURGEON_CONTEXT_LIMIT,
@@ -86,7 +86,7 @@ import {
86
86
  updateEntry,
87
87
  validateTemporalValidityRange,
88
88
  writeConfig
89
- } from "./chunk-LVDQXSHP.js";
89
+ } from "./chunk-SQARNOYD.js";
90
90
  import {
91
91
  compactClaimKey,
92
92
  describeClaimKeyNormalizationFailure,
@@ -1104,10 +1104,23 @@ async function dedupBatch(entries, llm, embedding, options = {}) {
1104
1104
  maxSimilarity: calculateClusterMaxSimilarity(cluster, embeddings)
1105
1105
  });
1106
1106
  }
1107
+ const totalArbitratedEntries = arbitrationTasks.reduce((sum, task) => sum + task.cluster.length, 0);
1108
+ let completedClusters = 0;
1109
+ let completedEntries = 0;
1107
1110
  const arbitrationResults = await runBoundedArbitrations(
1108
1111
  arbitrationTasks,
1109
1112
  concurrency,
1110
- async (task) => arbitrateCluster(task.clusterIndex, task.cluster, entries, llm, task.maxSimilarity)
1113
+ async (task) => arbitrateCluster(task.clusterIndex, task.cluster, entries, llm, task.maxSimilarity),
1114
+ (task) => {
1115
+ completedClusters += 1;
1116
+ completedEntries += task.cluster.length;
1117
+ options.onProgress?.({
1118
+ completedClusters,
1119
+ totalClusters: arbitrationTasks.length,
1120
+ completedEntries,
1121
+ totalEntries: totalArbitratedEntries
1122
+ });
1123
+ }
1111
1124
  );
1112
1125
  for (const arbitration of arbitrationResults) {
1113
1126
  clusterDetails.push(arbitration.detail);
@@ -1151,7 +1164,7 @@ function normalizeDedupConcurrency(value) {
1151
1164
  }
1152
1165
  return value;
1153
1166
  }
1154
- async function runBoundedArbitrations(tasks, concurrency, worker) {
1167
+ async function runBoundedArbitrations(tasks, concurrency, worker, onTaskComplete) {
1155
1168
  if (tasks.length === 0) {
1156
1169
  return [];
1157
1170
  }
@@ -1171,6 +1184,7 @@ async function runBoundedArbitrations(tasks, concurrency, worker) {
1171
1184
  return;
1172
1185
  }
1173
1186
  results[taskIndex] = await worker(task, taskIndex);
1187
+ onTaskComplete?.(task, taskIndex);
1174
1188
  }
1175
1189
  })
1176
1190
  );
@@ -1957,7 +1971,8 @@ async function ingestDiscoveredFiles(files, ports, options = {}) {
1957
1971
  {
1958
1972
  concurrency: options.concurrency ?? DEFAULT_INGEST_CONCURRENCY,
1959
1973
  skip: options.skipDedup,
1960
- verbose: options.verbose
1974
+ verbose: options.verbose,
1975
+ onProgress: options.onDedupProgress
1961
1976
  }
1962
1977
  );
1963
1978
  const preservedDedupResult = {
@@ -1997,7 +2012,8 @@ async function ingestDiscoveredFiles(files, ports, options = {}) {
1997
2012
  if (flattenedIndex >= 0) {
1998
2013
  claimKeyDiagnostics.set(flattenedIndex, diagnostic);
1999
2014
  }
2000
- }
2015
+ },
2016
+ options.onClaimExtractionProgress
2001
2017
  );
2002
2018
  for (const [entry, extractedClaimKey] of extractedClaimKeys) {
2003
2019
  applyClaimExtractionResultToEntry(entry, extractedClaimKey);
@@ -3354,7 +3370,13 @@ function registerIngestEntriesCommand(parent) {
3354
3370
  spinner5?.message(`Processing transcripts... (${completed}/${total} extracted)`);
3355
3371
  },
3356
3372
  onStageProgress: (event) => {
3357
- spinner5?.message(progressMessageForIngestStage(event, files.length));
3373
+ spinner5?.message(progressMessageForIngestStage(event));
3374
+ },
3375
+ onDedupProgress: (event) => {
3376
+ spinner5?.message(progressMessageForDedup(event));
3377
+ },
3378
+ onClaimExtractionProgress: (event) => {
3379
+ spinner5?.message(progressMessageForClaimExtraction(event));
3358
3380
  },
3359
3381
  onBulkWriteProgress: useVerboseBulkWriteProgress ? reportBulkWriteProgress : (event) => {
3360
3382
  spinner5?.message(progressMessageForBulkWrite(event.phase));
@@ -3749,16 +3771,27 @@ function emptyStoreResult2() {
3749
3771
  rejected: 0
3750
3772
  };
3751
3773
  }
3752
- function progressMessageForIngestStage(event, totalFiles) {
3774
+ function progressMessageForIngestStage(event) {
3753
3775
  switch (event.phase) {
3754
3776
  case "dedup_start":
3755
- return `Deduplicating ${event.totalEntries} ${pluralize2(event.totalEntries, "entry", "entries")} from ${totalFiles} ${pluralize2(totalFiles, "file")}...`;
3777
+ return "Deduplicating entries...";
3756
3778
  case "claim_extraction_start":
3757
- return `Extracting claim keys for ${event.totalEntries} ${pluralize2(event.totalEntries, "entry", "entries")}...`;
3779
+ return "Extracting claim keys...";
3758
3780
  case "store_start":
3759
3781
  return `Running store pipeline for ${event.totalEntries} ${pluralize2(event.totalEntries, "entry", "entries")}...`;
3760
3782
  }
3761
3783
  }
3784
+ function progressMessageForDedup(event) {
3785
+ return `Deduplicating entries... ${event.completedClusters}/${event.totalClusters} ${pluralize2(event.totalClusters, "cluster")} arbitrated (${event.completedEntries}/${event.totalEntries} entries covered)`;
3786
+ }
3787
+ function progressMessageForClaimExtraction(event) {
3788
+ switch (event.phase) {
3789
+ case "primary":
3790
+ return `Extracting claim keys... ${event.completedEntries}/${event.totalEntries} entries`;
3791
+ case "retry":
3792
+ return `Retrying unresolved claim keys... ${event.completedEntries}/${event.totalEntries} entries`;
3793
+ }
3794
+ }
3762
3795
  function progressMessageForBulkWrite(phase) {
3763
3796
  switch (phase) {
3764
3797
  case "prepare_start":
@@ -23,7 +23,7 @@ import {
23
23
  resolveEmbeddingApiKey,
24
24
  resolveEmbeddingModel,
25
25
  runUnifiedRecall
26
- } from "./chunk-LVDQXSHP.js";
26
+ } from "./chunk-SQARNOYD.js";
27
27
  import {
28
28
  recall
29
29
  } from "./chunk-GUDCFFRV.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agenr",
3
- "version": "1.8.1",
3
+ "version": "1.8.2",
4
4
  "description": "Agent memory - local-first knowledge infrastructure for AI agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,24 +12,6 @@
12
12
  "LICENSE",
13
13
  "CHANGELOG.md"
14
14
  ],
15
- "scripts": {
16
- "build": "pnpm run build:root && pnpm run build:plugin",
17
- "build:root": "tsup",
18
- "build:plugin": "pnpm --filter @agenr/agenr-plugin build",
19
- "build:debug": "pnpm run build:root:debug && pnpm run build:plugin:debug",
20
- "build:root:debug": "tsup --sourcemap",
21
- "build:plugin:debug": "pnpm --filter @agenr/agenr-plugin build:debug",
22
- "dev": "tsup --watch",
23
- "internal:recall-eval-server": "pnpm run build:root && node dist/internal-recall-eval-server.js",
24
- "check": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test",
25
- "typecheck": "tsc --noEmit",
26
- "typecheck:tests": "tsc --noEmit -p tsconfig.tests.json",
27
- "lint": "eslint .",
28
- "format": "prettier --write .",
29
- "format:check": "prettier --check .",
30
- "test": "vitest run",
31
- "test:watch": "vitest"
32
- },
33
15
  "dependencies": {
34
16
  "@mariozechner/pi-agent-core": "^0.63.1",
35
17
  "@mariozechner/pi-ai": "^0.63.2",
@@ -55,5 +37,23 @@
55
37
  "engines": {
56
38
  "node": ">=24"
57
39
  },
58
- "license": "AGPL-3.0"
59
- }
40
+ "license": "AGPL-3.0",
41
+ "scripts": {
42
+ "build": "pnpm run build:root && pnpm run build:plugin",
43
+ "build:root": "tsup",
44
+ "build:plugin": "pnpm --filter @agenr/agenr-plugin build",
45
+ "build:debug": "pnpm run build:root:debug && pnpm run build:plugin:debug",
46
+ "build:root:debug": "tsup --sourcemap",
47
+ "build:plugin:debug": "pnpm --filter @agenr/agenr-plugin build:debug",
48
+ "dev": "tsup --watch",
49
+ "internal:recall-eval-server": "pnpm run build:root && node dist/internal-recall-eval-server.js",
50
+ "check": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test",
51
+ "typecheck": "tsc --noEmit",
52
+ "typecheck:tests": "tsc --noEmit -p tsconfig.tests.json",
53
+ "lint": "eslint .",
54
+ "format": "prettier --write .",
55
+ "format:check": "prettier --check .",
56
+ "test": "vitest run",
57
+ "test:watch": "vitest"
58
+ }
59
+ }