@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,196 @@
|
|
|
1
|
+
import { HEURISTIC_TOKEN_COUNTER, countMessageTokens, renderMessageText } from "../token-counter.js";
|
|
2
|
+
import { clearOldToolResults } from "./clear-tool-results.js";
|
|
3
|
+
import { SUMMARY_TEMPLATE_NAME, SUMMARY_TEMPLATE_VERSION, buildSummarizerPrompt, renderFinalSummary } from "./templates/summary-9-section.js";
|
|
4
|
+
import { scanImperativePatterns } from "@graphorin/observability/redaction";
|
|
5
|
+
|
|
6
|
+
//#region src/context-engine/compaction/compactor.ts
|
|
7
|
+
/**
|
|
8
|
+
* Default count of recent turns preserved verbatim by the
|
|
9
|
+
* `summarize-old-preserve-recent` strategy.
|
|
10
|
+
*
|
|
11
|
+
* @stable
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_PRESERVE_RECENT_TURNS = 6;
|
|
14
|
+
/** Opening marker of the inbound-untrusted envelope (tools sanitize layer). */
|
|
15
|
+
const UNTRUSTED_MARKER = "<<<untrusted_content";
|
|
16
|
+
/**
|
|
17
|
+
* Wall-clock budget for the CE-15 injection scan of the summarizer
|
|
18
|
+
* output. The scanner's 5ms default exists for the per-tool-result
|
|
19
|
+
* hot path; here a compaction already paid for an LLM call, and on a
|
|
20
|
+
* contended host (slow CI runners included) scheduler noise can cross
|
|
21
|
+
* 5ms between pattern iterations — making the scanner return `null`
|
|
22
|
+
* (verdict unknown) and the security check silently fail open.
|
|
23
|
+
*/
|
|
24
|
+
const COMPACTION_SCAN_BUDGET_MS = 50;
|
|
25
|
+
/** CE-15: does the compacted window carry inbound-untrusted envelopes? */
|
|
26
|
+
function windowContainsUntrusted(messages) {
|
|
27
|
+
return messages.some((message) => renderMessageText(message).includes(UNTRUSTED_MARKER));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* CE-15: wrap the LLM-authored summary body in a derived-trust
|
|
31
|
+
* envelope. Envelope marker sequences inside the body are neutralized
|
|
32
|
+
* first so summarizer output influenced by injected text cannot break
|
|
33
|
+
* out of the envelope and masquerade as authoritative system text.
|
|
34
|
+
*/
|
|
35
|
+
function wrapSummaryAsDerived(body) {
|
|
36
|
+
return `<<<untrusted_content trust="derived" tool="compaction-summarizer">>>\n${body.replaceAll("<<</untrusted_content>>>", "[[/untrusted_content]]").replaceAll(UNTRUSTED_MARKER, "[[untrusted_content")}\n<<</untrusted_content>>>`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Perform a compaction call. Returns the result envelope containing
|
|
40
|
+
* the produced summary, the dropped/preserved message slices, and
|
|
41
|
+
* the per-event metadata. Phase 12 / `agent.compact()` is the
|
|
42
|
+
* lifecycle owner; this function is the trim primitive.
|
|
43
|
+
*
|
|
44
|
+
* @stable
|
|
45
|
+
*/
|
|
46
|
+
async function executeCompaction(input) {
|
|
47
|
+
const counter = input.tokenCounter ?? HEURISTIC_TOKEN_COUNTER;
|
|
48
|
+
const now = input.now ?? Date.now;
|
|
49
|
+
const startTs = now();
|
|
50
|
+
const beforeTokens = await countMessageTokens(input.messages, counter);
|
|
51
|
+
if (input.strategy.kind === "custom") {
|
|
52
|
+
const ctx = {
|
|
53
|
+
runId: input.runId,
|
|
54
|
+
sessionId: input.sessionId,
|
|
55
|
+
agentId: input.agentId,
|
|
56
|
+
scope: input.scope,
|
|
57
|
+
source: input.source,
|
|
58
|
+
messages: input.messages,
|
|
59
|
+
beforeTokens,
|
|
60
|
+
thresholdTokens: input.thresholdTokens,
|
|
61
|
+
preserveRecentTurns: DEFAULT_PRESERVE_RECENT_TURNS,
|
|
62
|
+
providerTrust: input.providerTrust ?? "public-tls"
|
|
63
|
+
};
|
|
64
|
+
return await input.strategy.compact(ctx);
|
|
65
|
+
}
|
|
66
|
+
if (input.strategy.kind === "clear-old-tool-results") return executeClearStrategy(input, input.strategy, counter, now, startTs, beforeTokens);
|
|
67
|
+
const preserveRecentTurns = input.strategy.preserveRecentTurns ?? DEFAULT_PRESERVE_RECENT_TURNS;
|
|
68
|
+
const summarizerModelInput = input.strategy.summarizerModel;
|
|
69
|
+
const summarizerTimeoutMs = input.strategy.summarizerTimeoutMs;
|
|
70
|
+
const olderCount = Math.max(0, input.messages.length - preserveRecentTurns);
|
|
71
|
+
const olderMessages = input.messages.slice(0, olderCount);
|
|
72
|
+
const preservedMessages = input.messages.slice(olderCount);
|
|
73
|
+
if (olderMessages.length === 0) return Object.freeze({
|
|
74
|
+
summary: "",
|
|
75
|
+
summaryTokens: 0,
|
|
76
|
+
beforeTokens,
|
|
77
|
+
afterTokens: beforeTokens,
|
|
78
|
+
droppedMessageIds: Object.freeze([]),
|
|
79
|
+
droppedMessageIndices: Object.freeze([]),
|
|
80
|
+
preservedMessages: Object.freeze([...preservedMessages]),
|
|
81
|
+
trimmedMessages: Object.freeze([...preservedMessages]),
|
|
82
|
+
source: input.source,
|
|
83
|
+
durationMs: now() - startTs,
|
|
84
|
+
hooksFiredCount: 0,
|
|
85
|
+
summaryTrust: "trusted"
|
|
86
|
+
});
|
|
87
|
+
const template = {
|
|
88
|
+
preamble: input.localePack.compactionSummaryTemplate.preamble,
|
|
89
|
+
sections: input.localePack.compactionSummaryTemplate.sections
|
|
90
|
+
};
|
|
91
|
+
const summarizerInput = { prompt: buildSummarizerPrompt({
|
|
92
|
+
template,
|
|
93
|
+
olderMessages
|
|
94
|
+
}) };
|
|
95
|
+
if (summarizerModelInput !== void 0) summarizerInput.model = summarizerModelInput;
|
|
96
|
+
if (input.signal !== void 0) summarizerInput.signal = input.signal;
|
|
97
|
+
if (summarizerTimeoutMs !== void 0) summarizerInput.timeoutMs = summarizerTimeoutMs;
|
|
98
|
+
const summarized = await input.summarizer.summarize(summarizerInput);
|
|
99
|
+
const metadata = {
|
|
100
|
+
compactedAtIso: new Date(startTs).toISOString(),
|
|
101
|
+
compactedFromMessageIds: Object.freeze(olderMessages.map((_, idx) => `c${startTs}_msg_${idx}`)),
|
|
102
|
+
compactedFromMessageIndices: Object.freeze(olderMessages.map((_, idx) => idx)),
|
|
103
|
+
compactedFromTokens: await countMessageTokens(olderMessages, counter),
|
|
104
|
+
summaryTokens: summarized.usageTokens ?? await counter.countText(summarized.text),
|
|
105
|
+
summarizerModel: resolveSummarizerModelLabel(summarizerModelInput, input.summarizer.id),
|
|
106
|
+
templateName: SUMMARY_TEMPLATE_NAME,
|
|
107
|
+
templateVersion: SUMMARY_TEMPLATE_VERSION,
|
|
108
|
+
preserveRecentTurns
|
|
109
|
+
};
|
|
110
|
+
const summaryScan = scanImperativePatterns(summarized.text, void 0, COMPACTION_SCAN_BUDGET_MS);
|
|
111
|
+
const summaryTrust = windowContainsUntrusted(olderMessages) || summaryScan !== null && summaryScan.hits.length > 0 ? "untrusted-derived" : "trusted";
|
|
112
|
+
const finalSummary = renderFinalSummary({
|
|
113
|
+
template,
|
|
114
|
+
summaryFromLlm: summaryTrust === "untrusted-derived" ? wrapSummaryAsDerived(summarized.text) : summarized.text,
|
|
115
|
+
preservedMessages,
|
|
116
|
+
metadata
|
|
117
|
+
});
|
|
118
|
+
const summaryTokens = await counter.countText(finalSummary);
|
|
119
|
+
const summaryMessage = {
|
|
120
|
+
role: "system",
|
|
121
|
+
content: finalSummary
|
|
122
|
+
};
|
|
123
|
+
const trimmedMessages = Object.freeze([summaryMessage, ...preservedMessages]);
|
|
124
|
+
const afterTokens = await countMessageTokens(trimmedMessages, counter);
|
|
125
|
+
const durationMs = now() - startTs;
|
|
126
|
+
return Object.freeze({
|
|
127
|
+
summary: finalSummary,
|
|
128
|
+
summaryTokens,
|
|
129
|
+
beforeTokens,
|
|
130
|
+
afterTokens,
|
|
131
|
+
droppedMessageIds: metadata.compactedFromMessageIds,
|
|
132
|
+
droppedMessageIndices: metadata.compactedFromMessageIndices,
|
|
133
|
+
preservedMessages: Object.freeze([...preservedMessages]),
|
|
134
|
+
trimmedMessages,
|
|
135
|
+
source: input.source,
|
|
136
|
+
durationMs,
|
|
137
|
+
hooksFiredCount: 0,
|
|
138
|
+
summaryTrust
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* SOTA-1: clear the oldest tool results (zero-LLM), then summarize only if the
|
|
143
|
+
* cleared buffer is still over the threshold. The summarizer runs on the
|
|
144
|
+
* already-reduced window, so a few-tool-result buffer compacts for free.
|
|
145
|
+
*/
|
|
146
|
+
async function executeClearStrategy(input, strategy, counter, now, startTs, beforeTokens) {
|
|
147
|
+
const outcome = await clearOldToolResults(input.messages, {
|
|
148
|
+
...strategy.keepToolUses !== void 0 ? { keepToolUses: strategy.keepToolUses } : {},
|
|
149
|
+
...strategy.clearAtLeast !== void 0 ? { clearAtLeast: strategy.clearAtLeast } : {},
|
|
150
|
+
...strategy.excludeTools !== void 0 ? { excludeTools: strategy.excludeTools } : {},
|
|
151
|
+
...strategy.externalize !== void 0 ? { externalize: strategy.externalize } : {}
|
|
152
|
+
}, counter);
|
|
153
|
+
const afterClearTokens = await countMessageTokens(outcome.messages, counter);
|
|
154
|
+
const fallback = strategy.summarizeFallback;
|
|
155
|
+
if (fallback !== false && afterClearTokens > input.thresholdTokens) {
|
|
156
|
+
const summarized = await executeCompaction({
|
|
157
|
+
...input,
|
|
158
|
+
messages: outcome.messages,
|
|
159
|
+
strategy: {
|
|
160
|
+
kind: "summarize-old-preserve-recent",
|
|
161
|
+
...fallback ?? {}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
return Object.freeze({
|
|
165
|
+
...summarized,
|
|
166
|
+
beforeTokens,
|
|
167
|
+
droppedMessageIndices: Object.freeze([...outcome.clearedIndices, ...summarized.droppedMessageIndices.filter((i) => !outcome.clearedIndices.includes(i))]),
|
|
168
|
+
durationMs: now() - startTs
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
const clearedSet = new Set(outcome.clearedIndices);
|
|
172
|
+
const preservedMessages = outcome.messages.filter((_, i) => !clearedSet.has(i));
|
|
173
|
+
return Object.freeze({
|
|
174
|
+
summary: "",
|
|
175
|
+
summaryTokens: 0,
|
|
176
|
+
beforeTokens,
|
|
177
|
+
afterTokens: afterClearTokens,
|
|
178
|
+
droppedMessageIds: Object.freeze(outcome.clearedIndices.map((i) => `c${startTs}_cleared_${i}`)),
|
|
179
|
+
droppedMessageIndices: Object.freeze([...outcome.clearedIndices]),
|
|
180
|
+
preservedMessages: Object.freeze(preservedMessages),
|
|
181
|
+
trimmedMessages: Object.freeze([...outcome.messages]),
|
|
182
|
+
source: input.source,
|
|
183
|
+
durationMs: now() - startTs,
|
|
184
|
+
hooksFiredCount: 0,
|
|
185
|
+
summaryTrust: "trusted"
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
function resolveSummarizerModelLabel(modelInput, summarizerId) {
|
|
189
|
+
if (typeof modelInput === "string") return modelInput;
|
|
190
|
+
if (modelInput !== void 0) return "provider" in modelInput ? `${modelInput.provider.name}:${modelInput.model}` : `${modelInput.name}:${modelInput.modelId}`;
|
|
191
|
+
return summarizerId ?? null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
//#endregion
|
|
195
|
+
export { DEFAULT_PRESERVE_RECENT_TURNS, executeCompaction };
|
|
196
|
+
//# sourceMappingURL=compactor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compactor.js","names":["ctx: CompactionContext","summarizerInput: {\n prompt: string;\n model?: ModelSpec | string;\n signal?: AbortSignal;\n timeoutMs?: number;\n }","metadata: CompactionMetadataPayload","summaryTrust: 'trusted' | 'untrusted-derived'","summaryMessage: SystemMessage","trimmedMessages: ReadonlyArray<Message>"],"sources":["../../../src/context-engine/compaction/compactor.ts"],"sourcesContent":["/**\n * Auto-compaction subsystem core (RB-46 / suggested DEC-162 /\n * ADR-050). Trims the in-flight ContextEngine message buffer when\n * the assembled token count crosses a per-provider threshold; the\n * trim is summarized via the configured 9-section template; the\n * trim is followed by synchronous-await firing of registered\n * post-compaction hooks (lifecycle owned by Phase 12; this module\n * exposes the trim primitive).\n *\n * Disjoint from the Phase 10c consolidator: never writes to\n * `semantic_facts` / `episodic_episodes` / `procedural_rules`;\n * reads only `memory.metadata(...)` / `memory.semantic.search(...)`\n * / `memory.procedural.activate(...)` / `memory.working.read(...)`\n * (non-mutating).\n *\n * @packageDocumentation\n */\n\nimport type { Message, ModelSpec, SystemMessage } from '@graphorin/core';\nimport { scanImperativePatterns } from '@graphorin/observability/redaction';\nimport type { ContextLocalePack } from '../locale-packs/index.js';\nimport {\n type ContextTokenCounter,\n countMessageTokens,\n HEURISTIC_TOKEN_COUNTER,\n renderMessageText,\n} from '../token-counter.js';\nimport { clearOldToolResults } from './clear-tool-results.js';\nimport {\n buildSummarizerPrompt,\n type CompactionMetadataPayload,\n renderFinalSummary,\n SUMMARY_TEMPLATE_NAME,\n SUMMARY_TEMPLATE_VERSION,\n} from './templates/summary-9-section.js';\nimport type {\n CompactionContext,\n CompactionResult,\n CompactionSource,\n CompactionStrategy,\n CompactionSummarizer,\n} from './types.js';\n\n/**\n * Default count of recent turns preserved verbatim by the\n * `summarize-old-preserve-recent` strategy.\n *\n * @stable\n */\nexport const DEFAULT_PRESERVE_RECENT_TURNS = 6;\n\n/** Opening marker of the inbound-untrusted envelope (tools sanitize layer). */\nconst UNTRUSTED_MARKER = '<<<untrusted_content';\n\n/**\n * Wall-clock budget for the CE-15 injection scan of the summarizer\n * output. The scanner's 5ms default exists for the per-tool-result\n * hot path; here a compaction already paid for an LLM call, and on a\n * contended host (slow CI runners included) scheduler noise can cross\n * 5ms between pattern iterations — making the scanner return `null`\n * (verdict unknown) and the security check silently fail open.\n */\nconst COMPACTION_SCAN_BUDGET_MS = 50;\n\n/** CE-15: does the compacted window carry inbound-untrusted envelopes? */\nfunction windowContainsUntrusted(messages: ReadonlyArray<Message>): boolean {\n return messages.some((message) => renderMessageText(message).includes(UNTRUSTED_MARKER));\n}\n\n/**\n * CE-15: wrap the LLM-authored summary body in a derived-trust\n * envelope. Envelope marker sequences inside the body are neutralized\n * first so summarizer output influenced by injected text cannot break\n * out of the envelope and masquerade as authoritative system text.\n */\nfunction wrapSummaryAsDerived(body: string): string {\n const neutralized = body\n .replaceAll('<<</untrusted_content>>>', '[[/untrusted_content]]')\n .replaceAll(UNTRUSTED_MARKER, '[[untrusted_content');\n return `<<<untrusted_content trust=\"derived\" tool=\"compaction-summarizer\">>>\\n${neutralized}\\n<<</untrusted_content>>>`;\n}\n\n/**\n * Trim the in-flight buffer using the\n * `summarize-old-preserve-recent` strategy. Returns the trimmed\n * messages + summary metadata; the caller is responsible for\n * appending the summary to the message buffer (Phase 12 owns the\n * lifecycle; this module exposes the primitive).\n *\n * @stable\n */\nexport interface ExecuteCompactionInput {\n readonly messages: ReadonlyArray<Message>;\n readonly source: CompactionSource;\n readonly strategy: CompactionStrategy;\n readonly localePack: ContextLocalePack;\n readonly summarizer: CompactionSummarizer;\n readonly tokenCounter?: ContextTokenCounter;\n readonly thresholdTokens: number;\n readonly runId: string;\n readonly sessionId: string;\n readonly agentId: string;\n readonly scope: import('@graphorin/core').SessionScope;\n readonly providerTrust?: import('@graphorin/core').LocalProviderTrust;\n readonly now?: () => number;\n readonly signal?: AbortSignal;\n}\n\n/**\n * Perform a compaction call. Returns the result envelope containing\n * the produced summary, the dropped/preserved message slices, and\n * the per-event metadata. Phase 12 / `agent.compact()` is the\n * lifecycle owner; this function is the trim primitive.\n *\n * @stable\n */\nexport async function executeCompaction(input: ExecuteCompactionInput): Promise<CompactionResult> {\n const counter = input.tokenCounter ?? HEURISTIC_TOKEN_COUNTER;\n const now = input.now ?? Date.now;\n const startTs = now();\n const beforeTokens = await countMessageTokens(input.messages, counter);\n\n if (input.strategy.kind === 'custom') {\n const ctx: CompactionContext = {\n runId: input.runId,\n sessionId: input.sessionId,\n agentId: input.agentId,\n scope: input.scope,\n source: input.source,\n messages: input.messages,\n beforeTokens,\n thresholdTokens: input.thresholdTokens,\n preserveRecentTurns: DEFAULT_PRESERVE_RECENT_TURNS,\n providerTrust: input.providerTrust ?? 'public-tls',\n };\n const result = await input.strategy.compact(ctx);\n return result;\n }\n\n if (input.strategy.kind === 'clear-old-tool-results') {\n return executeClearStrategy(input, input.strategy, counter, now, startTs, beforeTokens);\n }\n\n const preserveRecentTurns = input.strategy.preserveRecentTurns ?? DEFAULT_PRESERVE_RECENT_TURNS;\n const summarizerModelInput = input.strategy.summarizerModel;\n const summarizerTimeoutMs = input.strategy.summarizerTimeoutMs;\n\n // Slice the older portion + preserve the recent N turns. We treat\n // a \"turn\" as a single message for the v0.1 surface; the spec\n // notes more nuanced segmentation is post-MVP per Q-121.\n const olderCount = Math.max(0, input.messages.length - preserveRecentTurns);\n const olderMessages = input.messages.slice(0, olderCount);\n const preservedMessages = input.messages.slice(olderCount);\n\n if (olderMessages.length === 0) {\n return Object.freeze({\n summary: '',\n summaryTokens: 0,\n beforeTokens,\n afterTokens: beforeTokens,\n droppedMessageIds: Object.freeze([]),\n droppedMessageIndices: Object.freeze([]),\n preservedMessages: Object.freeze([...preservedMessages]),\n trimmedMessages: Object.freeze([...preservedMessages]),\n source: input.source,\n durationMs: now() - startTs,\n hooksFiredCount: 0,\n summaryTrust: 'trusted',\n });\n }\n\n const template = {\n preamble: input.localePack.compactionSummaryTemplate.preamble,\n sections: input.localePack.compactionSummaryTemplate.sections,\n };\n const prompt = buildSummarizerPrompt({ template, olderMessages });\n\n const summarizerInput: {\n prompt: string;\n model?: ModelSpec | string;\n signal?: AbortSignal;\n timeoutMs?: number;\n } = { prompt };\n if (summarizerModelInput !== undefined) summarizerInput.model = summarizerModelInput;\n if (input.signal !== undefined) summarizerInput.signal = input.signal;\n if (summarizerTimeoutMs !== undefined) summarizerInput.timeoutMs = summarizerTimeoutMs;\n const summarized = await input.summarizer.summarize(summarizerInput);\n\n // Section 9 metadata payload — stable shape so consumers can\n // deserialize and reason about a compaction event.\n const metadata: CompactionMetadataPayload = {\n compactedAtIso: new Date(startTs).toISOString(),\n // CE-14: positional labels, prefixed with the compaction instant so\n // they no longer collide across compaction events (core Message has\n // no id — these are indices into THIS compaction's dropped slice).\n compactedFromMessageIds: Object.freeze(olderMessages.map((_, idx) => `c${startTs}_msg_${idx}`)),\n compactedFromMessageIndices: Object.freeze(olderMessages.map((_, idx) => idx)),\n compactedFromTokens: await countMessageTokens(olderMessages, counter),\n summaryTokens: summarized.usageTokens ?? (await counter.countText(summarized.text)),\n summarizerModel: resolveSummarizerModelLabel(summarizerModelInput, input.summarizer.id),\n templateName: SUMMARY_TEMPLATE_NAME,\n templateVersion: SUMMARY_TEMPLATE_VERSION,\n preserveRecentTurns,\n };\n\n // CE-15: the summary must not launder untrusted content into a\n // trusted system message. When the compacted window carried\n // untrusted envelopes — or the injection heuristics flag the\n // summarizer output itself — the LLM-authored body is committed\n // inside a derived-trust envelope (sticky across re-compactions:\n // the envelope re-triggers this detection next time around).\n const summaryScan = scanImperativePatterns(summarized.text, undefined, COMPACTION_SCAN_BUDGET_MS);\n const summaryTrust: 'trusted' | 'untrusted-derived' =\n windowContainsUntrusted(olderMessages) || (summaryScan !== null && summaryScan.hits.length > 0)\n ? 'untrusted-derived'\n : 'trusted';\n const finalSummary = renderFinalSummary({\n template,\n summaryFromLlm:\n summaryTrust === 'untrusted-derived'\n ? wrapSummaryAsDerived(summarized.text)\n : summarized.text,\n preservedMessages,\n metadata,\n });\n const summaryTokens = await counter.countText(finalSummary);\n\n const summaryMessage: SystemMessage = {\n role: 'system',\n content: finalSummary,\n };\n const trimmedMessages: ReadonlyArray<Message> = Object.freeze([\n summaryMessage,\n ...preservedMessages,\n ]);\n const afterTokens = await countMessageTokens(trimmedMessages, counter);\n const durationMs = now() - startTs;\n\n return Object.freeze({\n summary: finalSummary,\n summaryTokens,\n beforeTokens,\n afterTokens,\n droppedMessageIds: metadata.compactedFromMessageIds,\n droppedMessageIndices: metadata.compactedFromMessageIndices,\n preservedMessages: Object.freeze([...preservedMessages]),\n trimmedMessages,\n source: input.source,\n durationMs,\n hooksFiredCount: 0,\n summaryTrust,\n });\n}\n\n/**\n * SOTA-1: clear the oldest tool results (zero-LLM), then summarize only if the\n * cleared buffer is still over the threshold. The summarizer runs on the\n * already-reduced window, so a few-tool-result buffer compacts for free.\n */\nasync function executeClearStrategy(\n input: ExecuteCompactionInput,\n strategy: Extract<CompactionStrategy, { kind: 'clear-old-tool-results' }>,\n counter: ContextTokenCounter,\n now: () => number,\n startTs: number,\n beforeTokens: number,\n): Promise<CompactionResult> {\n const outcome = await clearOldToolResults(\n input.messages,\n {\n ...(strategy.keepToolUses !== undefined ? { keepToolUses: strategy.keepToolUses } : {}),\n ...(strategy.clearAtLeast !== undefined ? { clearAtLeast: strategy.clearAtLeast } : {}),\n ...(strategy.excludeTools !== undefined ? { excludeTools: strategy.excludeTools } : {}),\n ...(strategy.externalize !== undefined ? { externalize: strategy.externalize } : {}),\n },\n counter,\n );\n const afterClearTokens = await countMessageTokens(outcome.messages, counter);\n\n const fallback = strategy.summarizeFallback;\n if (fallback !== false && afterClearTokens > input.thresholdTokens) {\n // Clearing did not reclaim enough — summarize the ALREADY-cleared buffer.\n const summarized = await executeCompaction({\n ...input,\n messages: outcome.messages,\n strategy: { kind: 'summarize-old-preserve-recent', ...(fallback ?? {}) },\n });\n return Object.freeze({\n ...summarized,\n // Report against the original buffer + carry the cleared indices.\n beforeTokens,\n droppedMessageIndices: Object.freeze([\n ...outcome.clearedIndices,\n ...summarized.droppedMessageIndices.filter((i) => !outcome.clearedIndices.includes(i)),\n ]),\n durationMs: now() - startTs,\n });\n }\n\n // Zero-LLM clear-only outcome: content replaced in place, nothing summarized.\n const clearedSet = new Set(outcome.clearedIndices);\n const preservedMessages = outcome.messages.filter((_, i) => !clearedSet.has(i));\n return Object.freeze({\n summary: '',\n summaryTokens: 0,\n beforeTokens,\n afterTokens: afterClearTokens,\n droppedMessageIds: Object.freeze(outcome.clearedIndices.map((i) => `c${startTs}_cleared_${i}`)),\n droppedMessageIndices: Object.freeze([...outcome.clearedIndices]),\n preservedMessages: Object.freeze(preservedMessages),\n trimmedMessages: Object.freeze([...outcome.messages]),\n source: input.source,\n durationMs: now() - startTs,\n hooksFiredCount: 0,\n summaryTrust: 'trusted',\n });\n}\n\nfunction resolveSummarizerModelLabel(\n modelInput: ModelSpec | string | undefined,\n summarizerId: string | undefined,\n): string | null {\n if (typeof modelInput === 'string') return modelInput;\n if (modelInput !== undefined) {\n const provider =\n 'provider' in modelInput\n ? `${modelInput.provider.name}:${modelInput.model}`\n : `${modelInput.name}:${modelInput.modelId}`;\n return provider;\n }\n return summarizerId ?? null;\n}\n"],"mappings":";;;;;;;;;;;;AAiDA,MAAa,gCAAgC;;AAG7C,MAAM,mBAAmB;;;;;;;;;AAUzB,MAAM,4BAA4B;;AAGlC,SAAS,wBAAwB,UAA2C;AAC1E,QAAO,SAAS,MAAM,YAAY,kBAAkB,QAAQ,CAAC,SAAS,iBAAiB,CAAC;;;;;;;;AAS1F,SAAS,qBAAqB,MAAsB;AAIlD,QAAO,yEAHa,KACjB,WAAW,4BAA4B,yBAAyB,CAChE,WAAW,kBAAkB,sBAAsB,CACsC;;;;;;;;;;AAqC9F,eAAsB,kBAAkB,OAA0D;CAChG,MAAM,UAAU,MAAM,gBAAgB;CACtC,MAAM,MAAM,MAAM,OAAO,KAAK;CAC9B,MAAM,UAAU,KAAK;CACrB,MAAM,eAAe,MAAM,mBAAmB,MAAM,UAAU,QAAQ;AAEtE,KAAI,MAAM,SAAS,SAAS,UAAU;EACpC,MAAMA,MAAyB;GAC7B,OAAO,MAAM;GACb,WAAW,MAAM;GACjB,SAAS,MAAM;GACf,OAAO,MAAM;GACb,QAAQ,MAAM;GACd,UAAU,MAAM;GAChB;GACA,iBAAiB,MAAM;GACvB,qBAAqB;GACrB,eAAe,MAAM,iBAAiB;GACvC;AAED,SADe,MAAM,MAAM,SAAS,QAAQ,IAAI;;AAIlD,KAAI,MAAM,SAAS,SAAS,yBAC1B,QAAO,qBAAqB,OAAO,MAAM,UAAU,SAAS,KAAK,SAAS,aAAa;CAGzF,MAAM,sBAAsB,MAAM,SAAS,uBAAuB;CAClE,MAAM,uBAAuB,MAAM,SAAS;CAC5C,MAAM,sBAAsB,MAAM,SAAS;CAK3C,MAAM,aAAa,KAAK,IAAI,GAAG,MAAM,SAAS,SAAS,oBAAoB;CAC3E,MAAM,gBAAgB,MAAM,SAAS,MAAM,GAAG,WAAW;CACzD,MAAM,oBAAoB,MAAM,SAAS,MAAM,WAAW;AAE1D,KAAI,cAAc,WAAW,EAC3B,QAAO,OAAO,OAAO;EACnB,SAAS;EACT,eAAe;EACf;EACA,aAAa;EACb,mBAAmB,OAAO,OAAO,EAAE,CAAC;EACpC,uBAAuB,OAAO,OAAO,EAAE,CAAC;EACxC,mBAAmB,OAAO,OAAO,CAAC,GAAG,kBAAkB,CAAC;EACxD,iBAAiB,OAAO,OAAO,CAAC,GAAG,kBAAkB,CAAC;EACtD,QAAQ,MAAM;EACd,YAAY,KAAK,GAAG;EACpB,iBAAiB;EACjB,cAAc;EACf,CAAC;CAGJ,MAAM,WAAW;EACf,UAAU,MAAM,WAAW,0BAA0B;EACrD,UAAU,MAAM,WAAW,0BAA0B;EACtD;CAGD,MAAMC,kBAKF,EAAE,QAPS,sBAAsB;EAAE;EAAU;EAAe,CAAC,EAOnD;AACd,KAAI,yBAAyB,OAAW,iBAAgB,QAAQ;AAChE,KAAI,MAAM,WAAW,OAAW,iBAAgB,SAAS,MAAM;AAC/D,KAAI,wBAAwB,OAAW,iBAAgB,YAAY;CACnE,MAAM,aAAa,MAAM,MAAM,WAAW,UAAU,gBAAgB;CAIpE,MAAMC,WAAsC;EAC1C,gBAAgB,IAAI,KAAK,QAAQ,CAAC,aAAa;EAI/C,yBAAyB,OAAO,OAAO,cAAc,KAAK,GAAG,QAAQ,IAAI,QAAQ,OAAO,MAAM,CAAC;EAC/F,6BAA6B,OAAO,OAAO,cAAc,KAAK,GAAG,QAAQ,IAAI,CAAC;EAC9E,qBAAqB,MAAM,mBAAmB,eAAe,QAAQ;EACrE,eAAe,WAAW,eAAgB,MAAM,QAAQ,UAAU,WAAW,KAAK;EAClF,iBAAiB,4BAA4B,sBAAsB,MAAM,WAAW,GAAG;EACvF,cAAc;EACd,iBAAiB;EACjB;EACD;CAQD,MAAM,cAAc,uBAAuB,WAAW,MAAM,QAAW,0BAA0B;CACjG,MAAMC,eACJ,wBAAwB,cAAc,IAAK,gBAAgB,QAAQ,YAAY,KAAK,SAAS,IACzF,sBACA;CACN,MAAM,eAAe,mBAAmB;EACtC;EACA,gBACE,iBAAiB,sBACb,qBAAqB,WAAW,KAAK,GACrC,WAAW;EACjB;EACA;EACD,CAAC;CACF,MAAM,gBAAgB,MAAM,QAAQ,UAAU,aAAa;CAE3D,MAAMC,iBAAgC;EACpC,MAAM;EACN,SAAS;EACV;CACD,MAAMC,kBAA0C,OAAO,OAAO,CAC5D,gBACA,GAAG,kBACJ,CAAC;CACF,MAAM,cAAc,MAAM,mBAAmB,iBAAiB,QAAQ;CACtE,MAAM,aAAa,KAAK,GAAG;AAE3B,QAAO,OAAO,OAAO;EACnB,SAAS;EACT;EACA;EACA;EACA,mBAAmB,SAAS;EAC5B,uBAAuB,SAAS;EAChC,mBAAmB,OAAO,OAAO,CAAC,GAAG,kBAAkB,CAAC;EACxD;EACA,QAAQ,MAAM;EACd;EACA,iBAAiB;EACjB;EACD,CAAC;;;;;;;AAQJ,eAAe,qBACb,OACA,UACA,SACA,KACA,SACA,cAC2B;CAC3B,MAAM,UAAU,MAAM,oBACpB,MAAM,UACN;EACE,GAAI,SAAS,iBAAiB,SAAY,EAAE,cAAc,SAAS,cAAc,GAAG,EAAE;EACtF,GAAI,SAAS,iBAAiB,SAAY,EAAE,cAAc,SAAS,cAAc,GAAG,EAAE;EACtF,GAAI,SAAS,iBAAiB,SAAY,EAAE,cAAc,SAAS,cAAc,GAAG,EAAE;EACtF,GAAI,SAAS,gBAAgB,SAAY,EAAE,aAAa,SAAS,aAAa,GAAG,EAAE;EACpF,EACD,QACD;CACD,MAAM,mBAAmB,MAAM,mBAAmB,QAAQ,UAAU,QAAQ;CAE5E,MAAM,WAAW,SAAS;AAC1B,KAAI,aAAa,SAAS,mBAAmB,MAAM,iBAAiB;EAElE,MAAM,aAAa,MAAM,kBAAkB;GACzC,GAAG;GACH,UAAU,QAAQ;GAClB,UAAU;IAAE,MAAM;IAAiC,GAAI,YAAY,EAAE;IAAG;GACzE,CAAC;AACF,SAAO,OAAO,OAAO;GACnB,GAAG;GAEH;GACA,uBAAuB,OAAO,OAAO,CACnC,GAAG,QAAQ,gBACX,GAAG,WAAW,sBAAsB,QAAQ,MAAM,CAAC,QAAQ,eAAe,SAAS,EAAE,CAAC,CACvF,CAAC;GACF,YAAY,KAAK,GAAG;GACrB,CAAC;;CAIJ,MAAM,aAAa,IAAI,IAAI,QAAQ,eAAe;CAClD,MAAM,oBAAoB,QAAQ,SAAS,QAAQ,GAAG,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;AAC/E,QAAO,OAAO,OAAO;EACnB,SAAS;EACT,eAAe;EACf;EACA,aAAa;EACb,mBAAmB,OAAO,OAAO,QAAQ,eAAe,KAAK,MAAM,IAAI,QAAQ,WAAW,IAAI,CAAC;EAC/F,uBAAuB,OAAO,OAAO,CAAC,GAAG,QAAQ,eAAe,CAAC;EACjE,mBAAmB,OAAO,OAAO,kBAAkB;EACnD,iBAAiB,OAAO,OAAO,CAAC,GAAG,QAAQ,SAAS,CAAC;EACrD,QAAQ,MAAM;EACd,YAAY,KAAK,GAAG;EACpB,iBAAiB;EACjB,cAAc;EACf,CAAC;;AAGJ,SAAS,4BACP,YACA,cACe;AACf,KAAI,OAAO,eAAe,SAAU,QAAO;AAC3C,KAAI,eAAe,OAKjB,QAHE,cAAc,aACV,GAAG,WAAW,SAAS,KAAK,GAAG,WAAW,UAC1C,GAAG,WAAW,KAAK,GAAG,WAAW;AAGzC,QAAO,gBAAgB"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { NamedPostCompactionHook } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/context-engine/compaction/hooks/reanchor-persona-block.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Build a `reanchorPersonaBlock` hook.
|
|
7
|
+
*
|
|
8
|
+
* @stable
|
|
9
|
+
*/
|
|
10
|
+
declare function reanchorPersonaBlock(options?: {
|
|
11
|
+
readonly blockLabel?: string;
|
|
12
|
+
}): NamedPostCompactionHook;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { reanchorPersonaBlock };
|
|
15
|
+
//# sourceMappingURL=reanchor-persona-block.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reanchor-persona-block.d.ts","names":[],"sources":["../../../../src/context-engine/compaction/hooks/reanchor-persona-block.ts"],"sourcesContent":[],"mappings":";;;;;;;;;iBAiBgB,oBAAA;;IAEb"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src/context-engine/compaction/hooks/reanchor-persona-block.ts
|
|
2
|
+
/**
|
|
3
|
+
* Build a `reanchorPersonaBlock` hook.
|
|
4
|
+
*
|
|
5
|
+
* @stable
|
|
6
|
+
*/
|
|
7
|
+
function reanchorPersonaBlock(options = {}) {
|
|
8
|
+
const blockLabel = options.blockLabel ?? "persona";
|
|
9
|
+
return {
|
|
10
|
+
id: "reanchorPersonaBlock",
|
|
11
|
+
async resolveContent(deps) {
|
|
12
|
+
const value = await deps.memory.working.read(deps.scope, blockLabel);
|
|
13
|
+
if (value === null || value.length === 0) return [];
|
|
14
|
+
return [{
|
|
15
|
+
type: "text",
|
|
16
|
+
text: `<memory_blocks anchor="post-compaction">\n <block label="${escapeXml(blockLabel)}">\n ${escapeXml(value)}\n </block>\n</memory_blocks>`
|
|
17
|
+
}];
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function escapeXml(value) {
|
|
22
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
//#endregion
|
|
26
|
+
export { reanchorPersonaBlock };
|
|
27
|
+
//# sourceMappingURL=reanchor-persona-block.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reanchor-persona-block.js","names":[],"sources":["../../../../src/context-engine/compaction/hooks/reanchor-persona-block.ts"],"sourcesContent":["/**\n * `reanchorPersonaBlock({ blockLabel = 'persona' })` — built-in\n * post-compaction hook. Queries `memory.working.read(...)` for the\n * persona block and returns it as a system-content fragment for\n * re-injection.\n *\n * @packageDocumentation\n */\n\nimport type { MessageContent } from '@graphorin/core';\nimport type { HookDeps, NamedPostCompactionHook } from './types.js';\n\n/**\n * Build a `reanchorPersonaBlock` hook.\n *\n * @stable\n */\nexport function reanchorPersonaBlock(\n options: { readonly blockLabel?: string } = {},\n): NamedPostCompactionHook {\n const blockLabel = options.blockLabel ?? 'persona';\n return {\n id: 'reanchorPersonaBlock',\n async resolveContent(deps: HookDeps): Promise<ReadonlyArray<MessageContent>> {\n const value = await deps.memory.working.read(deps.scope, blockLabel);\n if (value === null || value.length === 0) return [];\n return [\n {\n type: 'text',\n text: `<memory_blocks anchor=\"post-compaction\">\\n <block label=\"${escapeXml(blockLabel)}\">\\n ${escapeXml(value)}\\n </block>\\n</memory_blocks>`,\n },\n ];\n },\n };\n}\n\nfunction escapeXml(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n"],"mappings":";;;;;;AAiBA,SAAgB,qBACd,UAA4C,EAAE,EACrB;CACzB,MAAM,aAAa,QAAQ,cAAc;AACzC,QAAO;EACL,IAAI;EACJ,MAAM,eAAe,MAAwD;GAC3E,MAAM,QAAQ,MAAM,KAAK,OAAO,QAAQ,KAAK,KAAK,OAAO,WAAW;AACpE,OAAI,UAAU,QAAQ,MAAM,WAAW,EAAG,QAAO,EAAE;AACnD,UAAO,CACL;IACE,MAAM;IACN,MAAM,6DAA6D,UAAU,WAAW,CAAC,UAAU,UAAU,MAAM,CAAC;IACrH,CACF;;EAEJ;;AAGH,SAAS,UAAU,OAAuB;AACxC,QAAO,MACJ,QAAQ,MAAM,QAAQ,CACtB,QAAQ,MAAM,OAAO,CACrB,QAAQ,MAAM,OAAO,CACrB,QAAQ,MAAM,SAAS"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ContextTokenCounter } from "../../token-counter.js";
|
|
2
|
+
import { NamedPostCompactionHook } from "./types.js";
|
|
3
|
+
|
|
4
|
+
//#region src/context-engine/compaction/hooks/reanchor-pinned-facts.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Build a `reanchorPinnedFacts` hook.
|
|
8
|
+
*
|
|
9
|
+
* @stable
|
|
10
|
+
*/
|
|
11
|
+
declare function reanchorPinnedFacts(options: {
|
|
12
|
+
readonly pinnedFactIds: ReadonlyArray<string>;
|
|
13
|
+
readonly maxTokens?: number;
|
|
14
|
+
readonly tokenCounter?: ContextTokenCounter;
|
|
15
|
+
}): NamedPostCompactionHook;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { reanchorPinnedFacts };
|
|
18
|
+
//# sourceMappingURL=reanchor-pinned-facts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reanchor-pinned-facts.d.ts","names":[],"sources":["../../../../src/context-engine/compaction/hooks/reanchor-pinned-facts.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;iBAkBgB,mBAAA;0BACU;;0BAEA;IACtB"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { HEURISTIC_TOKEN_COUNTER } from "../../token-counter.js";
|
|
2
|
+
|
|
3
|
+
//#region src/context-engine/compaction/hooks/reanchor-pinned-facts.ts
|
|
4
|
+
/**
|
|
5
|
+
* Build a `reanchorPinnedFacts` hook.
|
|
6
|
+
*
|
|
7
|
+
* @stable
|
|
8
|
+
*/
|
|
9
|
+
function reanchorPinnedFacts(options) {
|
|
10
|
+
const ids = options.pinnedFactIds;
|
|
11
|
+
const maxTokens = Math.max(0, options.maxTokens ?? 2e3);
|
|
12
|
+
const counter = options.tokenCounter ?? HEURISTIC_TOKEN_COUNTER;
|
|
13
|
+
return {
|
|
14
|
+
id: "reanchorPinnedFacts",
|
|
15
|
+
async resolveContent(deps) {
|
|
16
|
+
if (ids.length === 0) return [];
|
|
17
|
+
const lines = ["<memory_pinned_facts anchor=\"post-compaction\">"];
|
|
18
|
+
let runningTokens = 0;
|
|
19
|
+
for (const id of ids) {
|
|
20
|
+
const fact = await deps.memory.semantic.get(deps.scope, id).catch(() => null);
|
|
21
|
+
if (fact === null || fact.deletedAt !== void 0) continue;
|
|
22
|
+
const tagBlob = fact.tags !== void 0 ? ` tags="${escapeXml((fact.tags ?? []).join(","))}"` : "";
|
|
23
|
+
const fragment = ` <fact id="${escapeXml(fact.id)}"${tagBlob}>${escapeXml(fact.text)}</fact>`;
|
|
24
|
+
const fragmentTokens = await counter.countText(fragment);
|
|
25
|
+
if (maxTokens > 0 && runningTokens + fragmentTokens > maxTokens) {
|
|
26
|
+
lines.push(" <!-- additional pinned facts truncated to fit budget -->");
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
runningTokens += fragmentTokens;
|
|
30
|
+
lines.push(fragment);
|
|
31
|
+
}
|
|
32
|
+
if (lines.length === 1) return [];
|
|
33
|
+
lines.push("</memory_pinned_facts>");
|
|
34
|
+
return [{
|
|
35
|
+
type: "text",
|
|
36
|
+
text: lines.join("\n")
|
|
37
|
+
}];
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function escapeXml(value) {
|
|
42
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
export { reanchorPinnedFacts };
|
|
47
|
+
//# sourceMappingURL=reanchor-pinned-facts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reanchor-pinned-facts.js","names":[],"sources":["../../../../src/context-engine/compaction/hooks/reanchor-pinned-facts.ts"],"sourcesContent":["/**\n * `reanchorPinnedFacts({ pinnedFactIds, maxTokens })` — built-in\n * post-compaction hook. Looks up the explicitly-pinned fact IDs\n * via `memory.semantic.get(...)` and returns them as a\n * system-content fragment with their tags.\n *\n * @packageDocumentation\n */\n\nimport type { MessageContent } from '@graphorin/core';\nimport { type ContextTokenCounter, HEURISTIC_TOKEN_COUNTER } from '../../token-counter.js';\nimport type { HookDeps, NamedPostCompactionHook } from './types.js';\n\n/**\n * Build a `reanchorPinnedFacts` hook.\n *\n * @stable\n */\nexport function reanchorPinnedFacts(options: {\n readonly pinnedFactIds: ReadonlyArray<string>;\n readonly maxTokens?: number;\n readonly tokenCounter?: ContextTokenCounter;\n}): NamedPostCompactionHook {\n const ids = options.pinnedFactIds;\n const maxTokens = Math.max(0, options.maxTokens ?? 2000);\n const counter = options.tokenCounter ?? HEURISTIC_TOKEN_COUNTER;\n return {\n id: 'reanchorPinnedFacts',\n async resolveContent(deps: HookDeps): Promise<ReadonlyArray<MessageContent>> {\n if (ids.length === 0) return [];\n const lines = ['<memory_pinned_facts anchor=\"post-compaction\">'];\n let runningTokens = 0;\n for (const id of ids) {\n const fact = await deps.memory.semantic.get(deps.scope, id).catch(() => null);\n if (fact === null || fact.deletedAt !== undefined) continue;\n const tagBlob =\n fact.tags !== undefined ? ` tags=\"${escapeXml((fact.tags ?? []).join(','))}\"` : '';\n const fragment = ` <fact id=\"${escapeXml(fact.id)}\"${tagBlob}>${escapeXml(fact.text)}</fact>`;\n const fragmentTokens = await counter.countText(fragment);\n if (maxTokens > 0 && runningTokens + fragmentTokens > maxTokens) {\n lines.push(' <!-- additional pinned facts truncated to fit budget -->');\n break;\n }\n runningTokens += fragmentTokens;\n lines.push(fragment);\n }\n if (lines.length === 1) return [];\n lines.push('</memory_pinned_facts>');\n return [{ type: 'text', text: lines.join('\\n') }];\n },\n };\n}\n\nfunction escapeXml(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n"],"mappings":";;;;;;;;AAkBA,SAAgB,oBAAoB,SAIR;CAC1B,MAAM,MAAM,QAAQ;CACpB,MAAM,YAAY,KAAK,IAAI,GAAG,QAAQ,aAAa,IAAK;CACxD,MAAM,UAAU,QAAQ,gBAAgB;AACxC,QAAO;EACL,IAAI;EACJ,MAAM,eAAe,MAAwD;AAC3E,OAAI,IAAI,WAAW,EAAG,QAAO,EAAE;GAC/B,MAAM,QAAQ,CAAC,mDAAiD;GAChE,IAAI,gBAAgB;AACpB,QAAK,MAAM,MAAM,KAAK;IACpB,MAAM,OAAO,MAAM,KAAK,OAAO,SAAS,IAAI,KAAK,OAAO,GAAG,CAAC,YAAY,KAAK;AAC7E,QAAI,SAAS,QAAQ,KAAK,cAAc,OAAW;IACnD,MAAM,UACJ,KAAK,SAAS,SAAY,UAAU,WAAW,KAAK,QAAQ,EAAE,EAAE,KAAK,IAAI,CAAC,CAAC,KAAK;IAClF,MAAM,WAAW,eAAe,UAAU,KAAK,GAAG,CAAC,GAAG,QAAQ,GAAG,UAAU,KAAK,KAAK,CAAC;IACtF,MAAM,iBAAiB,MAAM,QAAQ,UAAU,SAAS;AACxD,QAAI,YAAY,KAAK,gBAAgB,iBAAiB,WAAW;AAC/D,WAAM,KAAK,6DAA6D;AACxE;;AAEF,qBAAiB;AACjB,UAAM,KAAK,SAAS;;AAEtB,OAAI,MAAM,WAAW,EAAG,QAAO,EAAE;AACjC,SAAM,KAAK,yBAAyB;AACpC,UAAO,CAAC;IAAE,MAAM;IAAQ,MAAM,MAAM,KAAK,KAAK;IAAE,CAAC;;EAEpD;;AAGH,SAAS,UAAU,OAAuB;AACxC,QAAO,MACJ,QAAQ,MAAM,QAAQ,CACtB,QAAQ,MAAM,OAAO,CACrB,QAAQ,MAAM,OAAO,CACrB,QAAQ,MAAM,SAAS"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { NamedPostCompactionHook } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/context-engine/compaction/hooks/reanchor-project-rules.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Build a `reanchorProjectRules` hook.
|
|
7
|
+
*
|
|
8
|
+
* @stable
|
|
9
|
+
*/
|
|
10
|
+
declare function reanchorProjectRules(options?: {
|
|
11
|
+
readonly ruleTagsAllowlist?: ReadonlyArray<string>;
|
|
12
|
+
}): NamedPostCompactionHook;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { reanchorProjectRules };
|
|
15
|
+
//# sourceMappingURL=reanchor-project-rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reanchor-project-rules.d.ts","names":[],"sources":["../../../../src/context-engine/compaction/hooks/reanchor-project-rules.ts"],"sourcesContent":[],"mappings":";;;;;;;;;iBAiBgB,oBAAA;+BAC0B;IACvC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region src/context-engine/compaction/hooks/reanchor-project-rules.ts
|
|
2
|
+
/**
|
|
3
|
+
* Build a `reanchorProjectRules` hook.
|
|
4
|
+
*
|
|
5
|
+
* @stable
|
|
6
|
+
*/
|
|
7
|
+
function reanchorProjectRules(options = {}) {
|
|
8
|
+
const allowlist = options.ruleTagsAllowlist !== void 0 ? new Set(options.ruleTagsAllowlist) : null;
|
|
9
|
+
return {
|
|
10
|
+
id: "reanchorProjectRules",
|
|
11
|
+
async resolveContent(deps) {
|
|
12
|
+
const procedural = deps.procedural ?? {};
|
|
13
|
+
const rules = await deps.memory.procedural.activate(deps.scope, procedural);
|
|
14
|
+
const filtered = allowlist === null ? rules : rules.filter((rule) => (rule.tags ?? []).some((tag) => allowlist.has(tag)));
|
|
15
|
+
if (filtered.length === 0) return [];
|
|
16
|
+
const lines = ["<memory_rules anchor=\"post-compaction\">"];
|
|
17
|
+
for (const rule of filtered) lines.push(` <rule priority="${rule.priority}" tags="${escapeXml((rule.tags ?? []).join(","))}">${escapeXml(rule.text)}</rule>`);
|
|
18
|
+
lines.push("</memory_rules>");
|
|
19
|
+
return [{
|
|
20
|
+
type: "text",
|
|
21
|
+
text: lines.join("\n")
|
|
22
|
+
}];
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function escapeXml(value) {
|
|
27
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
export { reanchorProjectRules };
|
|
32
|
+
//# sourceMappingURL=reanchor-project-rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reanchor-project-rules.js","names":[],"sources":["../../../../src/context-engine/compaction/hooks/reanchor-project-rules.ts"],"sourcesContent":["/**\n * `reanchorProjectRules({ ruleTagsAllowlist? })` — built-in\n * post-compaction hook. Queries `memory.procedural.activate(...)`\n * and returns the active rule set as a system-content fragment for\n * re-injection into the trimmed buffer.\n *\n * @packageDocumentation\n */\n\nimport type { MessageContent } from '@graphorin/core';\nimport type { HookDeps, NamedPostCompactionHook } from './types.js';\n\n/**\n * Build a `reanchorProjectRules` hook.\n *\n * @stable\n */\nexport function reanchorProjectRules(\n options: { readonly ruleTagsAllowlist?: ReadonlyArray<string> } = {},\n): NamedPostCompactionHook {\n const allowlist =\n options.ruleTagsAllowlist !== undefined ? new Set(options.ruleTagsAllowlist) : null;\n return {\n id: 'reanchorProjectRules',\n async resolveContent(deps: HookDeps): Promise<ReadonlyArray<MessageContent>> {\n const procedural = deps.procedural ?? {};\n const rules = await deps.memory.procedural.activate(deps.scope, procedural);\n const filtered =\n allowlist === null\n ? rules\n : rules.filter((rule) => (rule.tags ?? []).some((tag) => allowlist.has(tag)));\n if (filtered.length === 0) return [];\n const lines = ['<memory_rules anchor=\"post-compaction\">'];\n for (const rule of filtered) {\n lines.push(\n ` <rule priority=\"${rule.priority}\" tags=\"${escapeXml((rule.tags ?? []).join(','))}\">${escapeXml(rule.text)}</rule>`,\n );\n }\n lines.push('</memory_rules>');\n return [{ type: 'text', text: lines.join('\\n') }];\n },\n };\n}\n\nfunction escapeXml(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n"],"mappings":";;;;;;AAiBA,SAAgB,qBACd,UAAkE,EAAE,EAC3C;CACzB,MAAM,YACJ,QAAQ,sBAAsB,SAAY,IAAI,IAAI,QAAQ,kBAAkB,GAAG;AACjF,QAAO;EACL,IAAI;EACJ,MAAM,eAAe,MAAwD;GAC3E,MAAM,aAAa,KAAK,cAAc,EAAE;GACxC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,SAAS,KAAK,OAAO,WAAW;GAC3E,MAAM,WACJ,cAAc,OACV,QACA,MAAM,QAAQ,UAAU,KAAK,QAAQ,EAAE,EAAE,MAAM,QAAQ,UAAU,IAAI,IAAI,CAAC,CAAC;AACjF,OAAI,SAAS,WAAW,EAAG,QAAO,EAAE;GACpC,MAAM,QAAQ,CAAC,4CAA0C;AACzD,QAAK,MAAM,QAAQ,SACjB,OAAM,KACJ,qBAAqB,KAAK,SAAS,UAAU,WAAW,KAAK,QAAQ,EAAE,EAAE,KAAK,IAAI,CAAC,CAAC,IAAI,UAAU,KAAK,KAAK,CAAC,SAC9G;AAEH,SAAM,KAAK,kBAAkB;AAC7B,UAAO,CAAC;IAAE,MAAM;IAAQ,MAAM,MAAM,KAAK,KAAK;IAAE,CAAC;;EAEpD;;AAGH,SAAS,UAAU,OAAuB;AACxC,QAAO,MACJ,QAAQ,MAAM,QAAQ,CACtB,QAAQ,MAAM,OAAO,CACrB,QAAQ,MAAM,OAAO,CACrB,QAAQ,MAAM,SAAS"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { PostCompactionHookContext } from "../types.js";
|
|
2
|
+
import { Memory } from "../../../facade.js";
|
|
3
|
+
import { MessageContent, SessionScope } from "@graphorin/core";
|
|
4
|
+
|
|
5
|
+
//#region src/context-engine/compaction/hooks/types.d.ts
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Resolved dependency surface every built-in hook reads. Threaded
|
|
9
|
+
* through Phase 12's lifecycle; tests pass a fixture
|
|
10
|
+
* implementation.
|
|
11
|
+
*
|
|
12
|
+
* @stable
|
|
13
|
+
*/
|
|
14
|
+
interface HookDeps {
|
|
15
|
+
readonly memory: Memory;
|
|
16
|
+
readonly scope: SessionScope;
|
|
17
|
+
/** Optional context tags surfaced to the procedural-rules query. */
|
|
18
|
+
readonly procedural?: {
|
|
19
|
+
readonly topic?: string;
|
|
20
|
+
readonly tags?: ReadonlyArray<string>;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Tagged hook returned by every factory below. The `id` field is
|
|
25
|
+
* surfaced on the `context.compaction.hook.executed.total{hookName}`
|
|
26
|
+
* counter family.
|
|
27
|
+
*
|
|
28
|
+
* @stable
|
|
29
|
+
*/
|
|
30
|
+
interface NamedPostCompactionHook {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
/**
|
|
33
|
+
* `ctx` carries the REAL compaction outcome (CE-6) — result, scope,
|
|
34
|
+
* runId, sessionId, agentId, source — built by `compactNow` after the
|
|
35
|
+
* pipeline finishes. Record-form built-ins may ignore it; the
|
|
36
|
+
* function-form wrapper forwards it to the operator's hook verbatim.
|
|
37
|
+
*/
|
|
38
|
+
resolveContent(deps: HookDeps, ctx?: PostCompactionHookContext): Promise<ReadonlyArray<MessageContent>>;
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { HookDeps, NamedPostCompactionHook };
|
|
42
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../../../src/context-engine/compaction/hooks/types.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;UA6BiB,QAAA;mBACE;kBACD;;;;oBAEiD;;;;;;;;;;UAUlD,uBAAA;;;;;;;;uBASP,gBAAQ,4BAEb,QAAQ,cAAc"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CLEARED_TOOL_RESULT_MARKER, ClearToolResultsOptions, ClearToolResultsOutcome, clearOldToolResults } from "./clear-tool-results.js";
|
|
2
|
+
import { CompactionConfig, CompactionContext, CompactionResult, CompactionSource, CompactionStrategy, CompactionSummarizer, CompactionTriggerConfig, PostCompactionHook, PostCompactionHookContext } from "./types.js";
|
|
3
|
+
import { DEFAULT_PRESERVE_RECENT_TURNS, ExecuteCompactionInput, executeCompaction } from "./compactor.js";
|
|
4
|
+
import { HookDeps, NamedPostCompactionHook } from "./hooks/types.js";
|
|
5
|
+
import { reanchorPersonaBlock } from "./hooks/reanchor-persona-block.js";
|
|
6
|
+
import { reanchorPinnedFacts } from "./hooks/reanchor-pinned-facts.js";
|
|
7
|
+
import { reanchorProjectRules } from "./hooks/reanchor-project-rules.js";
|
|
8
|
+
import { CompactionMetadataPayload, RenderedTemplate, SUMMARY_TEMPLATE_NAME, SUMMARY_TEMPLATE_VERSION, buildSummarizerPrompt, renderFinalSummary } from "./templates/summary-9-section.js";
|
|
9
|
+
import { AutoCompactionDefault, DEFAULT_RESERVED_FOR_COMPACTION, DEFAULT_RESERVED_FOR_RESPONSE, DEFAULT_THRESHOLD_RATIO, resolveAutoCompactionDefault, resolveTriggerThreshold } from "./thresholds.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CLEARED_TOOL_RESULT_MARKER, DEFAULT_KEEP_TOOL_USES, clearOldToolResults } from "./clear-tool-results.js";
|
|
2
|
+
import { SUMMARY_TEMPLATE_NAME, SUMMARY_TEMPLATE_VERSION, buildSummarizerPrompt, renderFinalSummary } from "./templates/summary-9-section.js";
|
|
3
|
+
import { DEFAULT_PRESERVE_RECENT_TURNS, executeCompaction } from "./compactor.js";
|
|
4
|
+
import { reanchorPersonaBlock } from "./hooks/reanchor-persona-block.js";
|
|
5
|
+
import { reanchorPinnedFacts } from "./hooks/reanchor-pinned-facts.js";
|
|
6
|
+
import { reanchorProjectRules } from "./hooks/reanchor-project-rules.js";
|
|
7
|
+
import { DEFAULT_RESERVED_FOR_COMPACTION, DEFAULT_RESERVED_FOR_RESPONSE, DEFAULT_THRESHOLD_RATIO, resolveAutoCompactionDefault, resolveTriggerThreshold } from "./thresholds.js";
|
|
8
|
+
|
|
9
|
+
export { };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ContextLocalePack } from "../../locale-packs/types.js";
|
|
2
|
+
import { Message } from "@graphorin/core";
|
|
3
|
+
|
|
4
|
+
//#region src/context-engine/compaction/templates/summary-9-section.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Template version surfaced into the summary's section 9 metadata.
|
|
8
|
+
* Bumped when the section structure / preamble wording changes in
|
|
9
|
+
* a way that consumers may want to detect.
|
|
10
|
+
*
|
|
11
|
+
* @stable
|
|
12
|
+
*/
|
|
13
|
+
declare const SUMMARY_TEMPLATE_VERSION = "1.2";
|
|
14
|
+
/**
|
|
15
|
+
* Stable identifier of the bundled template.
|
|
16
|
+
*
|
|
17
|
+
* @stable
|
|
18
|
+
*/
|
|
19
|
+
declare const SUMMARY_TEMPLATE_NAME = "summary-9-section";
|
|
20
|
+
/**
|
|
21
|
+
* Preamble + section-headers payload extracted from a locale pack.
|
|
22
|
+
* Surfaced as a separate type so test fixtures can assert against
|
|
23
|
+
* the resolved template independently from the locale pack.
|
|
24
|
+
*
|
|
25
|
+
* @stable
|
|
26
|
+
*/
|
|
27
|
+
interface RenderedTemplate {
|
|
28
|
+
readonly preamble: string;
|
|
29
|
+
readonly sections: ContextLocalePack['compactionSummaryTemplate']['sections'];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build the prompt the summarizer LLM receives. The prompt
|
|
33
|
+
* contains:
|
|
34
|
+
*
|
|
35
|
+
* 1. The locale-resolved preamble.
|
|
36
|
+
* 2. The verbatim section list (1-9, with a note that section 8
|
|
37
|
+
* is filled by the harness).
|
|
38
|
+
* 3. A delimited dump of the older messages the harness is about
|
|
39
|
+
* to drop.
|
|
40
|
+
*
|
|
41
|
+
* The summarizer produces every section except the last two; those
|
|
42
|
+
* (recent turns + metadata) are stitched in by the harness before the
|
|
43
|
+
* result is committed to the in-flight buffer.
|
|
44
|
+
*
|
|
45
|
+
* @stable
|
|
46
|
+
*/
|
|
47
|
+
declare function buildSummarizerPrompt(input: {
|
|
48
|
+
readonly template: RenderedTemplate;
|
|
49
|
+
readonly olderMessages: ReadonlyArray<Message>;
|
|
50
|
+
}): string;
|
|
51
|
+
/**
|
|
52
|
+
* Section-9 metadata payload. Stable shape so consumers can
|
|
53
|
+
* deserialize and reason about a compaction event.
|
|
54
|
+
*
|
|
55
|
+
* @stable
|
|
56
|
+
*/
|
|
57
|
+
interface CompactionMetadataPayload {
|
|
58
|
+
readonly compactedAtIso: string;
|
|
59
|
+
readonly compactedFromMessageIds: ReadonlyArray<string>;
|
|
60
|
+
readonly compactedFromMessageIndices: ReadonlyArray<number>;
|
|
61
|
+
readonly compactedFromTokens: number;
|
|
62
|
+
readonly summaryTokens: number;
|
|
63
|
+
readonly summarizerModel: string | null;
|
|
64
|
+
readonly templateName: string;
|
|
65
|
+
readonly templateVersion: string;
|
|
66
|
+
readonly preserveRecentTurns: number;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Render the produced summary into the final text the harness commits
|
|
70
|
+
* to the in-flight buffer. The LLM-produced sections come from
|
|
71
|
+
* `summaryFromLlm`; the last two are stitched in mechanically — the
|
|
72
|
+
* preserved recent turns and the `metadata` block.
|
|
73
|
+
*
|
|
74
|
+
* @stable
|
|
75
|
+
*/
|
|
76
|
+
declare function renderFinalSummary(input: {
|
|
77
|
+
readonly template: RenderedTemplate;
|
|
78
|
+
readonly summaryFromLlm: string;
|
|
79
|
+
readonly preservedMessages: ReadonlyArray<Message>;
|
|
80
|
+
readonly metadata: CompactionMetadataPayload;
|
|
81
|
+
}): string;
|
|
82
|
+
//#endregion
|
|
83
|
+
export { CompactionMetadataPayload, RenderedTemplate, SUMMARY_TEMPLATE_NAME, SUMMARY_TEMPLATE_VERSION, buildSummarizerPrompt, renderFinalSummary };
|
|
84
|
+
//# sourceMappingURL=summary-9-section.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary-9-section.d.ts","names":[],"sources":["../../../../src/context-engine/compaction/templates/summary-9-section.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;cAmCa,wBAAA;;;;;;cAOA,qBAAA;;;;;;;;UASI,gBAAA;;qBAEI;;;;;;;;;;;;;;;;;;iBAmBL,qBAAA;qBACK;0BACK,cAAc;;;;;;;;UAkCvB,yBAAA;;oCAEmB;wCACI;;;;;;;;;;;;;;;;iBAiBxB,kBAAA;qBACK;;8BAES,cAAc;qBACvB"}
|