@graphorin/memory 0.5.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/CHANGELOG.md +15 -0
- package/LICENSE +21 -0
- package/README.md +401 -0
- package/dist/conflict/index.d.ts +3 -0
- package/dist/conflict/index.js +7 -0
- package/dist/conflict/locale-packs/en.js +262 -0
- package/dist/conflict/locale-packs/en.js.map +1 -0
- package/dist/conflict/locale-packs/index.js +4 -0
- package/dist/conflict/locale-packs/types.d.ts +103 -0
- package/dist/conflict/locale-packs/types.d.ts.map +1 -0
- package/dist/conflict/locale-packs/types.js +55 -0
- package/dist/conflict/locale-packs/types.js.map +1 -0
- package/dist/conflict/pipeline.d.ts +35 -0
- package/dist/conflict/pipeline.d.ts.map +1 -0
- package/dist/conflict/pipeline.js +257 -0
- package/dist/conflict/pipeline.js.map +1 -0
- package/dist/conflict/stages/helpers.js +66 -0
- package/dist/conflict/stages/helpers.js.map +1 -0
- package/dist/conflict/stages/stage1-exact-dedup.js +21 -0
- package/dist/conflict/stages/stage1-exact-dedup.js.map +1 -0
- package/dist/conflict/stages/stage2-embedding-three-zone.js +37 -0
- package/dist/conflict/stages/stage2-embedding-three-zone.js.map +1 -0
- package/dist/conflict/stages/stage3-heuristic-regex.js +45 -0
- package/dist/conflict/stages/stage3-heuristic-regex.js.map +1 -0
- package/dist/conflict/stages/stage4-subject-predicate.js +31 -0
- package/dist/conflict/stages/stage4-subject-predicate.js.map +1 -0
- package/dist/conflict/stages/stage5-defer-to-deep.js +22 -0
- package/dist/conflict/stages/stage5-defer-to-deep.js.map +1 -0
- package/dist/conflict/types.d.ts +222 -0
- package/dist/conflict/types.d.ts.map +1 -0
- package/dist/conflict/types.js +50 -0
- package/dist/conflict/types.js.map +1 -0
- package/dist/consolidator/budget.d.ts +1 -0
- package/dist/consolidator/budget.js +214 -0
- package/dist/consolidator/budget.js.map +1 -0
- package/dist/consolidator/decay.d.ts +35 -0
- package/dist/consolidator/decay.d.ts.map +1 -0
- package/dist/consolidator/decay.js +91 -0
- package/dist/consolidator/decay.js.map +1 -0
- package/dist/consolidator/dlq.js +58 -0
- package/dist/consolidator/dlq.js.map +1 -0
- package/dist/consolidator/errors.d.ts +54 -0
- package/dist/consolidator/errors.d.ts.map +1 -0
- package/dist/consolidator/errors.js +67 -0
- package/dist/consolidator/errors.js.map +1 -0
- package/dist/consolidator/idempotency.d.ts +1 -0
- package/dist/consolidator/idempotency.js +22 -0
- package/dist/consolidator/idempotency.js.map +1 -0
- package/dist/consolidator/index.d.ts +34 -0
- package/dist/consolidator/index.d.ts.map +1 -0
- package/dist/consolidator/index.js +163 -0
- package/dist/consolidator/index.js.map +1 -0
- package/dist/consolidator/lock.d.ts +2 -0
- package/dist/consolidator/lock.js +89 -0
- package/dist/consolidator/lock.js.map +1 -0
- package/dist/consolidator/noise-filter.d.ts +2 -0
- package/dist/consolidator/noise-filter.js +173 -0
- package/dist/consolidator/noise-filter.js.map +1 -0
- package/dist/consolidator/phases/deep.d.ts +4 -0
- package/dist/consolidator/phases/deep.js +198 -0
- package/dist/consolidator/phases/deep.js.map +1 -0
- package/dist/consolidator/phases/induce.d.ts +154 -0
- package/dist/consolidator/phases/induce.d.ts.map +1 -0
- package/dist/consolidator/phases/induce.js +253 -0
- package/dist/consolidator/phases/induce.js.map +1 -0
- package/dist/consolidator/phases/light.d.ts +4 -0
- package/dist/consolidator/phases/light.js +99 -0
- package/dist/consolidator/phases/light.js.map +1 -0
- package/dist/consolidator/phases/reflect.js +287 -0
- package/dist/consolidator/phases/reflect.js.map +1 -0
- package/dist/consolidator/phases/standard.d.ts +8 -0
- package/dist/consolidator/phases/standard.js +499 -0
- package/dist/consolidator/phases/standard.js.map +1 -0
- package/dist/consolidator/reconcile.js +192 -0
- package/dist/consolidator/reconcile.js.map +1 -0
- package/dist/consolidator/runtime.d.ts +62 -0
- package/dist/consolidator/runtime.d.ts.map +1 -0
- package/dist/consolidator/runtime.js +580 -0
- package/dist/consolidator/runtime.js.map +1 -0
- package/dist/consolidator/scheduler.d.ts +111 -0
- package/dist/consolidator/scheduler.d.ts.map +1 -0
- package/dist/consolidator/scheduler.js +71 -0
- package/dist/consolidator/scheduler.js.map +1 -0
- package/dist/consolidator/triggers.d.ts +40 -0
- package/dist/consolidator/triggers.d.ts.map +1 -0
- package/dist/consolidator/triggers.js +94 -0
- package/dist/consolidator/triggers.js.map +1 -0
- package/dist/consolidator/types.d.ts +397 -0
- package/dist/consolidator/types.d.ts.map +1 -0
- package/dist/consolidator/types.js +117 -0
- package/dist/consolidator/types.js.map +1 -0
- package/dist/context-engine/annotations.d.ts +118 -0
- package/dist/context-engine/annotations.d.ts.map +1 -0
- package/dist/context-engine/annotations.js +102 -0
- package/dist/context-engine/annotations.js.map +1 -0
- package/dist/context-engine/auto-recall.d.ts +52 -0
- package/dist/context-engine/auto-recall.d.ts.map +1 -0
- package/dist/context-engine/auto-recall.js +41 -0
- package/dist/context-engine/auto-recall.js.map +1 -0
- package/dist/context-engine/compaction/clear-tool-results.d.ts +55 -0
- package/dist/context-engine/compaction/clear-tool-results.d.ts.map +1 -0
- package/dist/context-engine/compaction/clear-tool-results.js +97 -0
- package/dist/context-engine/compaction/clear-tool-results.js.map +1 -0
- package/dist/context-engine/compaction/compactor.d.ts +52 -0
- package/dist/context-engine/compaction/compactor.d.ts.map +1 -0
- package/dist/context-engine/compaction/compactor.js +196 -0
- package/dist/context-engine/compaction/compactor.js.map +1 -0
- package/dist/context-engine/compaction/hooks/reanchor-persona-block.d.ts +15 -0
- package/dist/context-engine/compaction/hooks/reanchor-persona-block.d.ts.map +1 -0
- package/dist/context-engine/compaction/hooks/reanchor-persona-block.js +27 -0
- package/dist/context-engine/compaction/hooks/reanchor-persona-block.js.map +1 -0
- package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.d.ts +18 -0
- package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.d.ts.map +1 -0
- package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.js +47 -0
- package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.js.map +1 -0
- package/dist/context-engine/compaction/hooks/reanchor-project-rules.d.ts +15 -0
- package/dist/context-engine/compaction/hooks/reanchor-project-rules.d.ts.map +1 -0
- package/dist/context-engine/compaction/hooks/reanchor-project-rules.js +32 -0
- package/dist/context-engine/compaction/hooks/reanchor-project-rules.js.map +1 -0
- package/dist/context-engine/compaction/hooks/types.d.ts +42 -0
- package/dist/context-engine/compaction/hooks/types.d.ts.map +1 -0
- package/dist/context-engine/compaction/index.d.ts +9 -0
- package/dist/context-engine/compaction/index.js +9 -0
- package/dist/context-engine/compaction/templates/summary-9-section.d.ts +84 -0
- package/dist/context-engine/compaction/templates/summary-9-section.d.ts.map +1 -0
- package/dist/context-engine/compaction/templates/summary-9-section.js +84 -0
- package/dist/context-engine/compaction/templates/summary-9-section.js.map +1 -0
- package/dist/context-engine/compaction/thresholds.d.ts +68 -0
- package/dist/context-engine/compaction/thresholds.d.ts.map +1 -0
- package/dist/context-engine/compaction/thresholds.js +63 -0
- package/dist/context-engine/compaction/thresholds.js.map +1 -0
- package/dist/context-engine/compaction/types.d.ts +202 -0
- package/dist/context-engine/compaction/types.d.ts.map +1 -0
- package/dist/context-engine/engine.d.ts +258 -0
- package/dist/context-engine/engine.d.ts.map +1 -0
- package/dist/context-engine/engine.js +424 -0
- package/dist/context-engine/engine.js.map +1 -0
- package/dist/context-engine/index.d.ts +68 -0
- package/dist/context-engine/index.d.ts.map +1 -0
- package/dist/context-engine/index.js +24 -0
- package/dist/context-engine/locale-packs/en.d.ts +14 -0
- package/dist/context-engine/locale-packs/en.d.ts.map +1 -0
- package/dist/context-engine/locale-packs/en.js +87 -0
- package/dist/context-engine/locale-packs/en.js.map +1 -0
- package/dist/context-engine/locale-packs/index.js +4 -0
- package/dist/context-engine/locale-packs/resolver.d.ts +41 -0
- package/dist/context-engine/locale-packs/resolver.d.ts.map +1 -0
- package/dist/context-engine/locale-packs/resolver.js +119 -0
- package/dist/context-engine/locale-packs/resolver.js.map +1 -0
- package/dist/context-engine/locale-packs/types.d.ts +119 -0
- package/dist/context-engine/locale-packs/types.d.ts.map +1 -0
- package/dist/context-engine/locale-packs/types.js +24 -0
- package/dist/context-engine/locale-packs/types.js.map +1 -0
- package/dist/context-engine/metadata.d.ts +40 -0
- package/dist/context-engine/metadata.d.ts.map +1 -0
- package/dist/context-engine/metadata.js +66 -0
- package/dist/context-engine/metadata.js.map +1 -0
- package/dist/context-engine/preambles/inbound-en.d.ts +20 -0
- package/dist/context-engine/preambles/inbound-en.d.ts.map +1 -0
- package/dist/context-engine/preambles/inbound-en.js +23 -0
- package/dist/context-engine/preambles/inbound-en.js.map +1 -0
- package/dist/context-engine/privacy-filter.d.ts +108 -0
- package/dist/context-engine/privacy-filter.d.ts.map +1 -0
- package/dist/context-engine/privacy-filter.js +113 -0
- package/dist/context-engine/privacy-filter.js.map +1 -0
- package/dist/context-engine/templates/base-en.d.ts +19 -0
- package/dist/context-engine/templates/base-en.d.ts.map +1 -0
- package/dist/context-engine/templates/base-en.js +22 -0
- package/dist/context-engine/templates/base-en.js.map +1 -0
- package/dist/context-engine/templates/composer.d.ts +55 -0
- package/dist/context-engine/templates/composer.d.ts.map +1 -0
- package/dist/context-engine/templates/composer.js +51 -0
- package/dist/context-engine/templates/composer.js.map +1 -0
- package/dist/context-engine/token-budget.d.ts +99 -0
- package/dist/context-engine/token-budget.d.ts.map +1 -0
- package/dist/context-engine/token-budget.js +211 -0
- package/dist/context-engine/token-budget.js.map +1 -0
- package/dist/context-engine/token-counter.d.ts +54 -0
- package/dist/context-engine/token-counter.d.ts.map +1 -0
- package/dist/context-engine/token-counter.js +81 -0
- package/dist/context-engine/token-counter.js.map +1 -0
- package/dist/errors/index.d.ts +171 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +206 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/facade.d.ts +278 -0
- package/dist/facade.d.ts.map +1 -0
- package/dist/facade.js +265 -0
- package/dist/facade.js.map +1 -0
- package/dist/graph/entity-resolver.d.ts +135 -0
- package/dist/graph/entity-resolver.d.ts.map +1 -0
- package/dist/graph/entity-resolver.js +202 -0
- package/dist/graph/entity-resolver.js.map +1 -0
- package/dist/graph/index.d.ts +1 -0
- package/dist/index.d.ts +103 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/contextualize.d.ts +14 -0
- package/dist/internal/contextualize.d.ts.map +1 -0
- package/dist/internal/contextualize.js +110 -0
- package/dist/internal/contextualize.js.map +1 -0
- package/dist/internal/embedder-binding.js +50 -0
- package/dist/internal/embedder-binding.js.map +1 -0
- package/dist/internal/id.js +24 -0
- package/dist/internal/id.js.map +1 -0
- package/dist/internal/injection-heuristics.js +64 -0
- package/dist/internal/injection-heuristics.js.map +1 -0
- package/dist/internal/spans.js +35 -0
- package/dist/internal/spans.js.map +1 -0
- package/dist/internal/storage-adapter.d.ts +742 -0
- package/dist/internal/storage-adapter.d.ts.map +1 -0
- package/dist/migration/embedder-migration.d.ts +124 -0
- package/dist/migration/embedder-migration.d.ts.map +1 -0
- package/dist/migration/embedder-migration.js +109 -0
- package/dist/migration/embedder-migration.js.map +1 -0
- package/dist/migration/index.d.ts +2 -0
- package/dist/migration/index.js +3 -0
- package/dist/search/explain.d.ts +74 -0
- package/dist/search/explain.d.ts.map +1 -0
- package/dist/search/explain.js +50 -0
- package/dist/search/explain.js.map +1 -0
- package/dist/search/index.d.ts +6 -0
- package/dist/search/index.js +6 -0
- package/dist/search/iterative.d.ts +223 -0
- package/dist/search/iterative.d.ts.map +1 -0
- package/dist/search/iterative.js +330 -0
- package/dist/search/iterative.js.map +1 -0
- package/dist/search/query-transform.d.ts +112 -0
- package/dist/search/query-transform.d.ts.map +1 -0
- package/dist/search/query-transform.js +184 -0
- package/dist/search/query-transform.js.map +1 -0
- package/dist/search/rrf.d.ts +95 -0
- package/dist/search/rrf.d.ts.map +1 -0
- package/dist/search/rrf.js +166 -0
- package/dist/search/rrf.js.map +1 -0
- package/dist/search/types.d.ts +50 -0
- package/dist/search/types.d.ts.map +1 -0
- package/dist/tiers/episodic-memory.d.ts +142 -0
- package/dist/tiers/episodic-memory.d.ts.map +1 -0
- package/dist/tiers/episodic-memory.js +254 -0
- package/dist/tiers/episodic-memory.js.map +1 -0
- package/dist/tiers/index.d.ts +8 -0
- package/dist/tiers/index.js +9 -0
- package/dist/tiers/insight-memory.d.ts +71 -0
- package/dist/tiers/insight-memory.d.ts.map +1 -0
- package/dist/tiers/insight-memory.js +133 -0
- package/dist/tiers/insight-memory.js.map +1 -0
- package/dist/tiers/procedural-memory.d.ts +147 -0
- package/dist/tiers/procedural-memory.d.ts.map +1 -0
- package/dist/tiers/procedural-memory.js +267 -0
- package/dist/tiers/procedural-memory.js.map +1 -0
- package/dist/tiers/semantic-memory.d.ts +461 -0
- package/dist/tiers/semantic-memory.d.ts.map +1 -0
- package/dist/tiers/semantic-memory.js +631 -0
- package/dist/tiers/semantic-memory.js.map +1 -0
- package/dist/tiers/session-memory.d.ts +117 -0
- package/dist/tiers/session-memory.d.ts.map +1 -0
- package/dist/tiers/session-memory.js +163 -0
- package/dist/tiers/session-memory.js.map +1 -0
- package/dist/tiers/shared-memory.d.ts +29 -0
- package/dist/tiers/shared-memory.d.ts.map +1 -0
- package/dist/tiers/shared-memory.js +58 -0
- package/dist/tiers/shared-memory.js.map +1 -0
- package/dist/tiers/working-memory.d.ts +129 -0
- package/dist/tiers/working-memory.d.ts.map +1 -0
- package/dist/tiers/working-memory.js +229 -0
- package/dist/tiers/working-memory.js.map +1 -0
- package/dist/tools/block-tools.d.ts +98 -0
- package/dist/tools/block-tools.d.ts.map +1 -0
- package/dist/tools/block-tools.js +114 -0
- package/dist/tools/block-tools.js.map +1 -0
- package/dist/tools/fact-tools.d.ts +294 -0
- package/dist/tools/fact-tools.d.ts.map +1 -0
- package/dist/tools/fact-tools.js +293 -0
- package/dist/tools/fact-tools.js.map +1 -0
- package/dist/tools/index.d.ts +35 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +36 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/recall-tools.d.ts +209 -0
- package/dist/tools/recall-tools.d.ts.map +1 -0
- package/dist/tools/recall-tools.js +170 -0
- package/dist/tools/recall-tools.js.map +1 -0
- package/dist/tools/types.d.ts +40 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/package.json +119 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { withMemorySpan } from "../../internal/spans.js";
|
|
2
|
+
import { newMemoryId } from "../../internal/id.js";
|
|
3
|
+
|
|
4
|
+
//#region src/consolidator/phases/reflect.ts
|
|
5
|
+
/** ExpeL starting salience for a freshly-synthesized insight. */
|
|
6
|
+
const STARTING_SALIENCE = 2;
|
|
7
|
+
/** How many recent episode summaries to show the salient-questions prompt. */
|
|
8
|
+
const RECENT_CONTEXT_LIMIT = 20;
|
|
9
|
+
const QUESTIONS_SYSTEM_PROMPT = [
|
|
10
|
+
"You are the reflection step (salient-questions) of a long-running personal-assistant memory.",
|
|
11
|
+
"Given a list of recent memories, name the few most salient, high-level questions whose",
|
|
12
|
+
"answers would most improve understanding of the user. Do NOT answer them; ask only what the",
|
|
13
|
+
"memories collectively raise. Return strictly JSON: { \"questions\": string[] }."
|
|
14
|
+
].join(" ");
|
|
15
|
+
const INSIGHT_SYSTEM_PROMPT = [
|
|
16
|
+
"You are the reflection step (insight-synthesis) of a long-running personal-assistant memory.",
|
|
17
|
+
"Given a salient question and the supporting evidence, synthesize ONE concise, higher-order",
|
|
18
|
+
"insight that the evidence collectively supports. Ground it ONLY in the supplied evidence; if",
|
|
19
|
+
"the evidence is insufficient, return an empty string. Do NOT invent facts. Return strictly",
|
|
20
|
+
"JSON: { \"insight\": string }."
|
|
21
|
+
].join(" ");
|
|
22
|
+
/**
|
|
23
|
+
* Run the reflection pass. Idempotent w.r.t. failures — a parse miss or
|
|
24
|
+
* empty synthesis simply yields no insight. Never throws on model
|
|
25
|
+
* output; only storage errors propagate.
|
|
26
|
+
*/
|
|
27
|
+
async function runReflectionPass(deps) {
|
|
28
|
+
return withMemorySpan(deps.tracer, "memory.consolidate.reflect", deps.scope, { "consolidator.phase": "reflect" }, async (span) => {
|
|
29
|
+
const empty = (over = {}) => ({
|
|
30
|
+
triggered: false,
|
|
31
|
+
accumulatedImportance: 0,
|
|
32
|
+
questionsAsked: 0,
|
|
33
|
+
insightsCreated: 0,
|
|
34
|
+
tokens: 0,
|
|
35
|
+
costUsd: 0,
|
|
36
|
+
nextWatermark: deps.reflectionWatermark ?? null,
|
|
37
|
+
...over
|
|
38
|
+
});
|
|
39
|
+
if (deps.episodic === null) {
|
|
40
|
+
span.setAttributes({ "consolidator.reflect.skipped": "no-episodic" });
|
|
41
|
+
return empty();
|
|
42
|
+
}
|
|
43
|
+
if (deps.budget.snapshot().paused) {
|
|
44
|
+
span.setAttributes({ "consolidator.reflect.skipped": "budget" });
|
|
45
|
+
return empty();
|
|
46
|
+
}
|
|
47
|
+
const recentEpisodes = await deps.episodic.listRecent(deps.scope, 50, { includeQuarantined: true });
|
|
48
|
+
const watermark = deps.reflectionWatermark ?? null;
|
|
49
|
+
const fresh = watermark === null ? recentEpisodes : recentEpisodes.filter((e) => Date.parse(e.endedAt) > watermark);
|
|
50
|
+
const accumulated = fresh.reduce((sum, episode) => sum + (episode.importance ?? 0), 0);
|
|
51
|
+
span.setAttributes({
|
|
52
|
+
"consolidator.reflect.accumulated_importance": accumulated,
|
|
53
|
+
"consolidator.reflect.threshold": deps.importanceThreshold
|
|
54
|
+
});
|
|
55
|
+
if (accumulated < deps.importanceThreshold) {
|
|
56
|
+
span.setAttributes({ "consolidator.reflect.triggered": 0 });
|
|
57
|
+
return empty({ accumulatedImportance: accumulated });
|
|
58
|
+
}
|
|
59
|
+
const advancedWatermark = fresh.reduce((max, episode) => Math.max(max, Date.parse(episode.endedAt)), watermark ?? Number.NEGATIVE_INFINITY);
|
|
60
|
+
let tokens = 0;
|
|
61
|
+
let costUsd = 0;
|
|
62
|
+
const record = (usage) => {
|
|
63
|
+
const t = (usage.promptTokens ?? 0) + (usage.completionTokens ?? 0) + (usage.reasoningTokens ?? 0);
|
|
64
|
+
const c = deps.priceUsage?.({
|
|
65
|
+
promptTokens: usage.promptTokens ?? 0,
|
|
66
|
+
completionTokens: usage.completionTokens ?? 0
|
|
67
|
+
}) ?? 0;
|
|
68
|
+
tokens += t;
|
|
69
|
+
costUsd += c;
|
|
70
|
+
deps.budget.record({
|
|
71
|
+
phase: "deep",
|
|
72
|
+
tokens: t,
|
|
73
|
+
costUsd: c
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
const recentSummaries = fresh.slice(0, RECENT_CONTEXT_LIMIT).map((episode) => `- ${episode.summary}`).join("\n");
|
|
77
|
+
const questionsRes = await deps.provider.generate(buildQuestionsRequest(deps, recentSummaries));
|
|
78
|
+
record(questionsRes.usage);
|
|
79
|
+
const questions = parseQuestions(questionsRes.text).slice(0, Math.max(0, deps.maxQuestions));
|
|
80
|
+
span.setAttributes({ "consolidator.reflect.questions": questions.length });
|
|
81
|
+
const seenInsights = new Set((await deps.insights.list(deps.scope, {
|
|
82
|
+
includeQuarantined: true,
|
|
83
|
+
limit: 200
|
|
84
|
+
})).map((i) => normalizeInsightText(i.text)));
|
|
85
|
+
const nowFn = typeof deps.now === "function" ? deps.now : Date.now;
|
|
86
|
+
let insightsCreated = 0;
|
|
87
|
+
const created = [];
|
|
88
|
+
for (const question of questions) {
|
|
89
|
+
if (deps.budget.snapshot().paused) break;
|
|
90
|
+
const factHits = await deps.semantic.search(deps.scope, question, { topK: 5 });
|
|
91
|
+
const epHits = await deps.episodic.search(deps.scope, question, {
|
|
92
|
+
topK: 5,
|
|
93
|
+
includeQuarantined: true
|
|
94
|
+
});
|
|
95
|
+
const evidence = dedupeEvidence([...factHits.map((h) => ({
|
|
96
|
+
id: h.record.id,
|
|
97
|
+
text: h.record.text
|
|
98
|
+
})), ...epHits.map((h) => ({
|
|
99
|
+
id: h.record.id,
|
|
100
|
+
text: h.record.summary
|
|
101
|
+
}))]);
|
|
102
|
+
if (evidence.length === 0) continue;
|
|
103
|
+
if (deps.budget.snapshot().paused) break;
|
|
104
|
+
const insightRes = await deps.provider.generate(buildInsightRequest(deps, question, evidence));
|
|
105
|
+
record(insightRes.usage);
|
|
106
|
+
const text = parseInsight(insightRes.text);
|
|
107
|
+
if (text === null) continue;
|
|
108
|
+
const normalized = normalizeInsightText(text);
|
|
109
|
+
if (seenInsights.has(normalized)) continue;
|
|
110
|
+
seenInsights.add(normalized);
|
|
111
|
+
const iso = new Date(nowFn()).toISOString();
|
|
112
|
+
const insightId = newMemoryId("ins");
|
|
113
|
+
await deps.insights.insert({
|
|
114
|
+
id: insightId,
|
|
115
|
+
kind: "insight",
|
|
116
|
+
userId: deps.scope.userId,
|
|
117
|
+
...deps.scope.sessionId !== void 0 ? { sessionId: deps.scope.sessionId } : {},
|
|
118
|
+
...deps.scope.agentId !== void 0 ? { agentId: deps.scope.agentId } : {},
|
|
119
|
+
text,
|
|
120
|
+
cites: evidence.map((e) => e.id),
|
|
121
|
+
salience: STARTING_SALIENCE,
|
|
122
|
+
provenance: "reflection",
|
|
123
|
+
status: "quarantined",
|
|
124
|
+
sensitivity: "internal",
|
|
125
|
+
createdAt: iso,
|
|
126
|
+
updatedAt: iso
|
|
127
|
+
});
|
|
128
|
+
created.push(insightId);
|
|
129
|
+
insightsCreated += 1;
|
|
130
|
+
}
|
|
131
|
+
const freshIds = new Set(created);
|
|
132
|
+
for (const existing of await deps.insights.list(deps.scope, {
|
|
133
|
+
includeQuarantined: true,
|
|
134
|
+
limit: 500
|
|
135
|
+
})) {
|
|
136
|
+
if (freshIds.has(existing.id)) continue;
|
|
137
|
+
await deps.insights.bumpSalience(existing.id, -1, "reflection-pass-decay");
|
|
138
|
+
}
|
|
139
|
+
await deps.insights.prune(deps.scope);
|
|
140
|
+
span.setAttributes({
|
|
141
|
+
"consolidator.reflect.triggered": 1,
|
|
142
|
+
"consolidator.reflect.insights_created": insightsCreated,
|
|
143
|
+
"consolidator.reflect.tokens": tokens,
|
|
144
|
+
"consolidator.reflect.cost.usd": costUsd
|
|
145
|
+
});
|
|
146
|
+
return {
|
|
147
|
+
triggered: true,
|
|
148
|
+
accumulatedImportance: accumulated,
|
|
149
|
+
questionsAsked: questions.length,
|
|
150
|
+
insightsCreated,
|
|
151
|
+
tokens,
|
|
152
|
+
costUsd,
|
|
153
|
+
nextWatermark: Number.isFinite(advancedWatermark) ? advancedWatermark : watermark
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Normalise an insight for exact-dedup comparison — lowercased, punctuation
|
|
159
|
+
* stripped, whitespace collapsed. Two insights that differ only in casing or
|
|
160
|
+
* trailing punctuation collapse to the same key.
|
|
161
|
+
*
|
|
162
|
+
* @internal
|
|
163
|
+
*/
|
|
164
|
+
function normalizeInsightText(text) {
|
|
165
|
+
return text.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").replace(/\s+/gu, " ").trim();
|
|
166
|
+
}
|
|
167
|
+
function buildQuestionsRequest(deps, recentSummaries) {
|
|
168
|
+
return {
|
|
169
|
+
messages: [{
|
|
170
|
+
role: "user",
|
|
171
|
+
content: [
|
|
172
|
+
"Recent memories:",
|
|
173
|
+
recentSummaries.length > 0 ? recentSummaries : "(none)",
|
|
174
|
+
"",
|
|
175
|
+
`Name up to ${deps.maxQuestions} salient questions.`
|
|
176
|
+
].join("\n")
|
|
177
|
+
}],
|
|
178
|
+
systemMessage: QUESTIONS_SYSTEM_PROMPT,
|
|
179
|
+
temperature: 0,
|
|
180
|
+
maxTokens: 512,
|
|
181
|
+
metadata: scopeMetadata(deps.scope),
|
|
182
|
+
outputType: { kind: "structured" }
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function buildInsightRequest(deps, question, evidence) {
|
|
186
|
+
const evidenceBlock = evidence.map((e, i) => `[${i + 1}] (id=${e.id}) ${e.text}`).join("\n");
|
|
187
|
+
return {
|
|
188
|
+
messages: [{
|
|
189
|
+
role: "user",
|
|
190
|
+
content: [
|
|
191
|
+
`Question: ${question}`,
|
|
192
|
+
"",
|
|
193
|
+
"Evidence:",
|
|
194
|
+
evidenceBlock
|
|
195
|
+
].join("\n")
|
|
196
|
+
}],
|
|
197
|
+
systemMessage: INSIGHT_SYSTEM_PROMPT,
|
|
198
|
+
temperature: 0,
|
|
199
|
+
maxTokens: 512,
|
|
200
|
+
metadata: scopeMetadata(deps.scope),
|
|
201
|
+
outputType: { kind: "structured" }
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function scopeMetadata(scope) {
|
|
205
|
+
return {
|
|
206
|
+
userId: scope.userId,
|
|
207
|
+
...scope.sessionId !== void 0 ? { sessionId: scope.sessionId } : {},
|
|
208
|
+
...scope.agentId !== void 0 ? { agentId: scope.agentId } : {}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function dedupeEvidence(items) {
|
|
212
|
+
const seen = /* @__PURE__ */ new Set();
|
|
213
|
+
const out = [];
|
|
214
|
+
for (const e of items) {
|
|
215
|
+
if (e.id.length === 0 || seen.has(e.id)) continue;
|
|
216
|
+
seen.add(e.id);
|
|
217
|
+
out.push(e);
|
|
218
|
+
}
|
|
219
|
+
return out;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Parse the salient-questions response — accepts `{ questions: [...] }`
|
|
223
|
+
* or a bare `[...]`, tolerating chatty / fenced output. Returns the
|
|
224
|
+
* trimmed, non-empty string questions (possibly `[]`).
|
|
225
|
+
*
|
|
226
|
+
* @internal
|
|
227
|
+
*/
|
|
228
|
+
function parseQuestions(text) {
|
|
229
|
+
if (text === void 0 || text.length === 0) return [];
|
|
230
|
+
const parsed = tryParse(stripFence(text).trim());
|
|
231
|
+
if (parsed === void 0) return [];
|
|
232
|
+
const arr = Array.isArray(parsed) ? parsed : isRecord(parsed) && Array.isArray(parsed.questions) ? parsed.questions : null;
|
|
233
|
+
if (arr === null) return [];
|
|
234
|
+
return arr.filter((q) => typeof q === "string" && q.trim().length > 0).map((q) => q.trim());
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Parse the insight-synthesis response — accepts `{ insight: string }`
|
|
238
|
+
* (or `{ text: string }`), tolerating chatty / fenced output. Returns
|
|
239
|
+
* the trimmed insight text, or `null` when absent / empty (insufficient
|
|
240
|
+
* evidence ⇒ no insight).
|
|
241
|
+
*
|
|
242
|
+
* @internal
|
|
243
|
+
*/
|
|
244
|
+
function parseInsight(text) {
|
|
245
|
+
if (text === void 0 || text.length === 0) return null;
|
|
246
|
+
const parsed = tryParse(stripFence(text).trim());
|
|
247
|
+
if (!isRecord(parsed)) return null;
|
|
248
|
+
const raw = typeof parsed.insight === "string" ? parsed.insight : null;
|
|
249
|
+
const fallback = typeof parsed.text === "string" ? parsed.text : null;
|
|
250
|
+
const value = raw ?? fallback;
|
|
251
|
+
if (value === null || value.trim().length === 0) return null;
|
|
252
|
+
return value.trim();
|
|
253
|
+
}
|
|
254
|
+
function tryParse(candidate) {
|
|
255
|
+
if (candidate.length === 0) return void 0;
|
|
256
|
+
try {
|
|
257
|
+
return JSON.parse(candidate);
|
|
258
|
+
} catch {
|
|
259
|
+
const obj = sliceBetween(candidate, "{", "}");
|
|
260
|
+
if (obj !== null) try {
|
|
261
|
+
return JSON.parse(obj);
|
|
262
|
+
} catch {}
|
|
263
|
+
const arr = sliceBetween(candidate, "[", "]");
|
|
264
|
+
if (arr !== null) try {
|
|
265
|
+
return JSON.parse(arr);
|
|
266
|
+
} catch {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function isRecord(value) {
|
|
273
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
274
|
+
}
|
|
275
|
+
function stripFence(text) {
|
|
276
|
+
return /^```[^\n]*\n([\s\S]*?)\n```/u.exec(text.trim())?.[1] ?? text;
|
|
277
|
+
}
|
|
278
|
+
function sliceBetween(text, open, close) {
|
|
279
|
+
const start = text.indexOf(open);
|
|
280
|
+
const end = text.lastIndexOf(close);
|
|
281
|
+
if (start < 0 || end < start) return null;
|
|
282
|
+
return text.slice(start, end + 1);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
//#endregion
|
|
286
|
+
export { runReflectionPass };
|
|
287
|
+
//# sourceMappingURL=reflect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reflect.js","names":["created: string[]","out: Evidence[]"],"sources":["../../../src/consolidator/phases/reflect.ts"],"sourcesContent":["/**\n * Reflection pass (P1-1) — the deep phase's higher-order synthesis\n * step, following the Generative-Agents recipe. When the accumulated\n * importance of recent episodes crosses a threshold, ask the model for\n * the few most salient questions, retrieve evidence for each, and\n * synthesize an **insight** the evidence supports — recorded\n * quarantined, `provenance: 'reflection'`, and **citing the retrieved\n * memory ids** (citations are set by the framework from the actual\n * evidence, never chosen by the model, so they can never be\n * hallucinated). Insights start at ExpeL salience `2`; the pass prunes\n * any that have decayed to `0`.\n *\n * Runs after the deep-phase conflict drain (wired in the runtime\n * dispatch), reusing the same budget / lock / run-audit envelope. The\n * conflict-resolution logic in `deep.ts` is untouched.\n *\n * @packageDocumentation\n */\n\nimport type { Provider, ProviderRequest, SessionScope, Tracer } from '@graphorin/core';\nimport { newMemoryId } from '../../internal/id.js';\nimport { withMemorySpan } from '../../internal/spans.js';\nimport type { InsightMemoryStoreExt } from '../../internal/storage-adapter.js';\nimport type { EpisodicMemory } from '../../tiers/episodic-memory.js';\nimport type { SemanticMemory } from '../../tiers/semantic-memory.js';\nimport type { BudgetTracker } from '../budget.js';\n\n/** ExpeL starting salience for a freshly-synthesized insight. */\nconst STARTING_SALIENCE = 2;\n\n/** How many recent episode summaries to show the salient-questions prompt. */\nconst RECENT_CONTEXT_LIMIT = 20;\n\n/** Inputs accepted by {@link runReflectionPass}. */\nexport interface ReflectionDeps {\n readonly provider: Provider;\n readonly tracer: Tracer;\n readonly scope: SessionScope;\n /** Tier used to retrieve fact evidence for each salient question. */\n readonly semantic: SemanticMemory;\n /** Importance source + episode-evidence retrieval. `null` ⇒ pass is a no-op. */\n readonly episodic: EpisodicMemory | null;\n /** Insight write + salience surface. */\n readonly insights: InsightMemoryStoreExt;\n readonly budget: BudgetTracker;\n /** Reflection fires only when accumulated episode importance ≥ this. */\n readonly importanceThreshold: number;\n /**\n * `ended_at` (epoch ms) of the newest episode a prior pass already reflected\n * on (MCON-13). The gate accumulates importance only from strictly-newer\n * episodes, so a deep run with no new episodes never re-fires. `null` /\n * absent ⇒ nothing reflected yet (accumulate over all recent episodes).\n */\n readonly reflectionWatermark?: number | null;\n /** Upper bound on salient questions asked per pass. */\n readonly maxQuestions: number;\n readonly priceUsage?: (usage: { promptTokens: number; completionTokens: number }) => number;\n /** Override the wall clock — used by tests. */\n readonly now?: () => number;\n}\n\n/** Summary returned by {@link runReflectionPass}. */\nexport interface ReflectionOutcome {\n /** True when accumulated importance crossed the threshold and the pass ran. */\n readonly triggered: boolean;\n readonly accumulatedImportance: number;\n readonly questionsAsked: number;\n readonly insightsCreated: number;\n readonly tokens: number;\n readonly costUsd: number;\n /**\n * Watermark to persist for the next pass (MCON-13): the `ended_at` (epoch\n * ms) of the newest episode this pass reflected on, or the incoming\n * watermark unchanged when the pass did not fire. The runtime persists it\n * to `consolidator_state`.\n */\n readonly nextWatermark: number | null;\n}\n\nconst QUESTIONS_SYSTEM_PROMPT = [\n 'You are the reflection step (salient-questions) of a long-running personal-assistant memory.',\n 'Given a list of recent memories, name the few most salient, high-level questions whose',\n 'answers would most improve understanding of the user. Do NOT answer them; ask only what the',\n 'memories collectively raise. Return strictly JSON: { \"questions\": string[] }.',\n].join(' ');\n\nconst INSIGHT_SYSTEM_PROMPT = [\n 'You are the reflection step (insight-synthesis) of a long-running personal-assistant memory.',\n 'Given a salient question and the supporting evidence, synthesize ONE concise, higher-order',\n 'insight that the evidence collectively supports. Ground it ONLY in the supplied evidence; if',\n 'the evidence is insufficient, return an empty string. Do NOT invent facts. Return strictly',\n 'JSON: { \"insight\": string }.',\n].join(' ');\n\ninterface Evidence {\n readonly id: string;\n readonly text: string;\n}\n\n/**\n * Run the reflection pass. Idempotent w.r.t. failures — a parse miss or\n * empty synthesis simply yields no insight. Never throws on model\n * output; only storage errors propagate.\n */\nexport async function runReflectionPass(deps: ReflectionDeps): Promise<ReflectionOutcome> {\n return withMemorySpan(\n deps.tracer,\n 'memory.consolidate.reflect',\n deps.scope,\n { 'consolidator.phase': 'reflect' },\n async (span) => {\n const empty = (over: Partial<ReflectionOutcome> = {}): ReflectionOutcome => ({\n triggered: false,\n accumulatedImportance: 0,\n questionsAsked: 0,\n insightsCreated: 0,\n tokens: 0,\n costUsd: 0,\n nextWatermark: deps.reflectionWatermark ?? null,\n ...over,\n });\n\n if (deps.episodic === null) {\n span.setAttributes({ 'consolidator.reflect.skipped': 'no-episodic' });\n return empty();\n }\n if (deps.budget.snapshot().paused) {\n span.setAttributes({ 'consolidator.reflect.skipped': 'budget' });\n return empty();\n }\n\n // 1. Accumulated-importance gate. Recency, not relevance: list the most\n // recent episodes (MCON-1 — the old `'*'` FTS probe matched zero rows\n // on real SQLite). Auto-formed episodes (P1-2) land\n // quarantined, so importance accrues there — include them. Only count\n // episodes newer than the watermark so a pass with no new episodes\n // never re-fires the paid synthesis (MCON-13).\n const recentEpisodes = await deps.episodic.listRecent(deps.scope, 50, {\n includeQuarantined: true,\n });\n const watermark = deps.reflectionWatermark ?? null;\n const fresh =\n watermark === null\n ? recentEpisodes\n : recentEpisodes.filter((e) => Date.parse(e.endedAt) > watermark);\n const accumulated = fresh.reduce((sum, episode) => sum + (episode.importance ?? 0), 0);\n span.setAttributes({\n 'consolidator.reflect.accumulated_importance': accumulated,\n 'consolidator.reflect.threshold': deps.importanceThreshold,\n });\n if (accumulated < deps.importanceThreshold) {\n span.setAttributes({ 'consolidator.reflect.triggered': 0 });\n return empty({ accumulatedImportance: accumulated });\n }\n // The newest episode this pass will reflect on — persisted as the next\n // watermark so subsequent passes skip everything up to here.\n const advancedWatermark = fresh.reduce(\n (max, episode) => Math.max(max, Date.parse(episode.endedAt)),\n watermark ?? Number.NEGATIVE_INFINITY,\n );\n\n let tokens = 0;\n let costUsd = 0;\n const record = (usage: {\n promptTokens?: number;\n completionTokens?: number;\n reasoningTokens?: number;\n }): void => {\n const t =\n (usage.promptTokens ?? 0) + (usage.completionTokens ?? 0) + (usage.reasoningTokens ?? 0);\n const c =\n deps.priceUsage?.({\n promptTokens: usage.promptTokens ?? 0,\n completionTokens: usage.completionTokens ?? 0,\n }) ?? 0;\n tokens += t;\n costUsd += c;\n deps.budget.record({ phase: 'deep', tokens: t, costUsd: c });\n };\n\n // 2. Salient questions over the new (post-watermark) memories.\n const recentSummaries = fresh\n .slice(0, RECENT_CONTEXT_LIMIT)\n .map((episode) => `- ${episode.summary}`)\n .join('\\n');\n const questionsRes = await deps.provider.generate(\n buildQuestionsRequest(deps, recentSummaries),\n );\n record(questionsRes.usage);\n const questions = parseQuestions(questionsRes.text).slice(0, Math.max(0, deps.maxQuestions));\n span.setAttributes({ 'consolidator.reflect.questions': questions.length });\n\n // Pre-filter new insights against already-stored ones so a re-fire over\n // overlapping evidence does not write near-duplicates (MCON-13). Offline\n // and embedding-free: normalised exact match, the insight analogue of\n // the conflict pipeline's stage-1 dedup.\n const seenInsights = new Set(\n (await deps.insights.list(deps.scope, { includeQuarantined: true, limit: 200 })).map((i) =>\n normalizeInsightText(i.text),\n ),\n );\n\n const nowFn = typeof deps.now === 'function' ? deps.now : Date.now;\n let insightsCreated = 0;\n const created: string[] = [];\n for (const question of questions) {\n if (deps.budget.snapshot().paused) break;\n\n // 3. Retrieve evidence. Facts are trustworthy (active only);\n // episodes include the quarantined auto-formed ones.\n const factHits = await deps.semantic.search(deps.scope, question, { topK: 5 });\n const epHits = await deps.episodic.search(deps.scope, question, {\n topK: 5,\n includeQuarantined: true,\n });\n const evidence = dedupeEvidence([\n ...factHits.map((h) => ({ id: h.record.id, text: h.record.text })),\n ...epHits.map((h) => ({ id: h.record.id, text: h.record.summary })),\n ]);\n // Citations are mandatory — no evidence ⇒ no insight.\n if (evidence.length === 0) continue;\n\n // 4. Synthesize an insight grounded in that evidence.\n if (deps.budget.snapshot().paused) break;\n const insightRes = await deps.provider.generate(\n buildInsightRequest(deps, question, evidence),\n );\n record(insightRes.usage);\n const text = parseInsight(insightRes.text);\n if (text === null) continue;\n\n // Skip near-duplicates of an already-stored insight (and of one just\n // written this pass).\n const normalized = normalizeInsightText(text);\n if (seenInsights.has(normalized)) continue;\n seenInsights.add(normalized);\n\n const iso = new Date(nowFn()).toISOString();\n const insightId = newMemoryId('ins');\n await deps.insights.insert({\n id: insightId,\n kind: 'insight',\n userId: deps.scope.userId,\n ...(deps.scope.sessionId !== undefined ? { sessionId: deps.scope.sessionId } : {}),\n ...(deps.scope.agentId !== undefined ? { agentId: deps.scope.agentId } : {}),\n text,\n // cites = the actual retrieved evidence ids (framework-set).\n cites: evidence.map((e) => e.id),\n salience: STARTING_SALIENCE,\n provenance: 'reflection',\n status: 'quarantined',\n sensitivity: 'internal',\n createdAt: iso,\n updatedAt: iso,\n });\n created.push(insightId);\n insightsCreated += 1;\n }\n\n // 5. ExpeL forgetting (MCON-16): decay every existing insight by 1\n // per reflection pass. Retrieval bumps +1 (InsightMemory.search),\n // so an insight recalled since the last pass nets level-or-better\n // while an unused one slides toward the prune floor — starting\n // salience 2 ⇒ pruned after two idle passes. Fresh insights from\n // THIS pass are exempt (they were inserted above at full salience\n // and have had no retrieval window yet).\n const freshIds = new Set(created);\n for (const existing of await deps.insights.list(deps.scope, {\n includeQuarantined: true,\n limit: 500,\n })) {\n if (freshIds.has(existing.id)) continue;\n await deps.insights.bumpSalience(existing.id, -1, 'reflection-pass-decay');\n }\n await deps.insights.prune(deps.scope);\n\n span.setAttributes({\n 'consolidator.reflect.triggered': 1,\n 'consolidator.reflect.insights_created': insightsCreated,\n 'consolidator.reflect.tokens': tokens,\n 'consolidator.reflect.cost.usd': costUsd,\n });\n return {\n triggered: true,\n accumulatedImportance: accumulated,\n questionsAsked: questions.length,\n insightsCreated,\n tokens,\n costUsd,\n // Advance past everything reflected on this pass. `advancedWatermark`\n // is finite here (the gate fired ⇒ `fresh` is non-empty).\n nextWatermark: Number.isFinite(advancedWatermark) ? advancedWatermark : watermark,\n };\n },\n );\n}\n\n/**\n * Normalise an insight for exact-dedup comparison — lowercased, punctuation\n * stripped, whitespace collapsed. Two insights that differ only in casing or\n * trailing punctuation collapse to the same key.\n *\n * @internal\n */\nexport function normalizeInsightText(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}\\s]/gu, ' ')\n .replace(/\\s+/gu, ' ')\n .trim();\n}\n\nfunction buildQuestionsRequest(deps: ReflectionDeps, recentSummaries: string): ProviderRequest {\n const userBlock = [\n 'Recent memories:',\n recentSummaries.length > 0 ? recentSummaries : '(none)',\n '',\n `Name up to ${deps.maxQuestions} salient questions.`,\n ].join('\\n');\n return {\n messages: [{ role: 'user', content: userBlock }],\n systemMessage: QUESTIONS_SYSTEM_PROMPT,\n temperature: 0,\n // MCON-14: per-call output cap — a short question list.\n maxTokens: 512,\n metadata: scopeMetadata(deps.scope),\n outputType: { kind: 'structured' },\n };\n}\n\nfunction buildInsightRequest(\n deps: ReflectionDeps,\n question: string,\n evidence: ReadonlyArray<Evidence>,\n): ProviderRequest {\n const evidenceBlock = evidence.map((e, i) => `[${i + 1}] (id=${e.id}) ${e.text}`).join('\\n');\n const userBlock = [`Question: ${question}`, '', 'Evidence:', evidenceBlock].join('\\n');\n return {\n messages: [{ role: 'user', content: userBlock }],\n systemMessage: INSIGHT_SYSTEM_PROMPT,\n temperature: 0,\n // MCON-14: per-call output cap — one insight object.\n maxTokens: 512,\n metadata: scopeMetadata(deps.scope),\n outputType: { kind: 'structured' },\n };\n}\n\nfunction scopeMetadata(scope: SessionScope): Record<string, string> {\n return {\n userId: scope.userId,\n ...(scope.sessionId !== undefined ? { sessionId: scope.sessionId } : {}),\n ...(scope.agentId !== undefined ? { agentId: scope.agentId } : {}),\n };\n}\n\nfunction dedupeEvidence(items: ReadonlyArray<Evidence>): Evidence[] {\n const seen = new Set<string>();\n const out: Evidence[] = [];\n for (const e of items) {\n if (e.id.length === 0 || seen.has(e.id)) continue;\n seen.add(e.id);\n out.push(e);\n }\n return out;\n}\n\n/**\n * Parse the salient-questions response — accepts `{ questions: [...] }`\n * or a bare `[...]`, tolerating chatty / fenced output. Returns the\n * trimmed, non-empty string questions (possibly `[]`).\n *\n * @internal\n */\nexport function parseQuestions(text: string | undefined): string[] {\n if (text === undefined || text.length === 0) return [];\n const candidate = stripFence(text).trim();\n const parsed = tryParse(candidate);\n if (parsed === undefined) return [];\n const arr = Array.isArray(parsed)\n ? parsed\n : isRecord(parsed) && Array.isArray(parsed.questions)\n ? parsed.questions\n : null;\n if (arr === null) return [];\n return arr\n .filter((q): q is string => typeof q === 'string' && q.trim().length > 0)\n .map((q) => q.trim());\n}\n\n/**\n * Parse the insight-synthesis response — accepts `{ insight: string }`\n * (or `{ text: string }`), tolerating chatty / fenced output. Returns\n * the trimmed insight text, or `null` when absent / empty (insufficient\n * evidence ⇒ no insight).\n *\n * @internal\n */\nexport function parseInsight(text: string | undefined): string | null {\n if (text === undefined || text.length === 0) return null;\n const candidate = stripFence(text).trim();\n const parsed = tryParse(candidate);\n if (!isRecord(parsed)) return null;\n const raw = typeof parsed.insight === 'string' ? parsed.insight : null;\n const fallback = typeof parsed.text === 'string' ? parsed.text : null;\n const value = raw ?? fallback;\n if (value === null || value.trim().length === 0) return null;\n return value.trim();\n}\n\nfunction tryParse(candidate: string): unknown {\n if (candidate.length === 0) return undefined;\n try {\n return JSON.parse(candidate);\n } catch {\n const obj = sliceBetween(candidate, '{', '}');\n if (obj !== null) {\n try {\n return JSON.parse(obj);\n } catch {\n // fall through to array attempt\n }\n }\n const arr = sliceBetween(candidate, '[', ']');\n if (arr !== null) {\n try {\n return JSON.parse(arr);\n } catch {\n return undefined;\n }\n }\n return undefined;\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction stripFence(text: string): string {\n const match = /^```[^\\n]*\\n([\\s\\S]*?)\\n```/u.exec(text.trim());\n return match?.[1] ?? text;\n}\n\nfunction sliceBetween(text: string, open: string, close: string): string | null {\n const start = text.indexOf(open);\n const end = text.lastIndexOf(close);\n if (start < 0 || end < start) return null;\n return text.slice(start, end + 1);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "../../internal/storage-adapter.js";
|
|
2
|
+
import "../../internal/contextualize.js";
|
|
3
|
+
import "../../tiers/episodic-memory.js";
|
|
4
|
+
import "../../tiers/semantic-memory.js";
|
|
5
|
+
import "../types.js";
|
|
6
|
+
import "../budget.js";
|
|
7
|
+
import "../noise-filter.js";
|
|
8
|
+
import { Provider, SessionScope, Tracer } from "@graphorin/core";
|