@graphorin/memory 0.6.0 → 0.7.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 +83 -0
- package/README.md +24 -7
- package/dist/conflict/index.js +0 -1
- package/dist/conflict/pipeline.js +0 -1
- package/dist/conflict/pipeline.js.map +1 -1
- package/dist/conflict/stages/stage3-heuristic-regex.js +0 -1
- package/dist/conflict/stages/stage3-heuristic-regex.js.map +1 -1
- package/dist/conflict/types.d.ts +1 -1
- package/dist/conflict/types.js.map +1 -1
- package/dist/consolidator/index.js +2 -0
- package/dist/consolidator/index.js.map +1 -1
- package/dist/consolidator/lock.js +7 -1
- package/dist/consolidator/lock.js.map +1 -1
- package/dist/consolidator/phases/deep.js +19 -12
- package/dist/consolidator/phases/deep.js.map +1 -1
- package/dist/consolidator/phases/induce.d.ts.map +1 -1
- package/dist/consolidator/phases/induce.js +2 -3
- package/dist/consolidator/phases/induce.js.map +1 -1
- package/dist/consolidator/phases/reflect.js +35 -8
- package/dist/consolidator/phases/reflect.js.map +1 -1
- package/dist/consolidator/phases/standard.js +199 -28
- package/dist/consolidator/phases/standard.js.map +1 -1
- package/dist/consolidator/reconcile.js +16 -15
- package/dist/consolidator/reconcile.js.map +1 -1
- package/dist/consolidator/runtime.d.ts.map +1 -1
- package/dist/consolidator/runtime.js +129 -47
- package/dist/consolidator/runtime.js.map +1 -1
- package/dist/consolidator/scheduler.d.ts +17 -3
- package/dist/consolidator/scheduler.d.ts.map +1 -1
- package/dist/consolidator/scheduler.js.map +1 -1
- package/dist/consolidator/types.d.ts +30 -1
- package/dist/consolidator/types.d.ts.map +1 -1
- package/dist/consolidator/types.js +10 -0
- package/dist/consolidator/types.js.map +1 -1
- package/dist/context-engine/compaction/clear-tool-results.d.ts +1 -1
- package/dist/context-engine/compaction/clear-tool-results.js +1 -1
- package/dist/context-engine/compaction/clear-tool-results.js.map +1 -1
- package/dist/context-engine/compaction/compactor.d.ts +1 -0
- package/dist/context-engine/compaction/compactor.d.ts.map +1 -1
- package/dist/context-engine/compaction/compactor.js +32 -8
- package/dist/context-engine/compaction/compactor.js.map +1 -1
- package/dist/context-engine/compaction/hooks/types.d.ts +1 -1
- package/dist/context-engine/compaction/index.d.ts +1 -0
- package/dist/context-engine/compaction/templates/marker.d.ts +33 -0
- package/dist/context-engine/compaction/templates/marker.d.ts.map +1 -0
- package/dist/context-engine/compaction/templates/marker.js +34 -0
- package/dist/context-engine/compaction/templates/marker.js.map +1 -0
- package/dist/context-engine/compaction/templates/summary-9-section.d.ts +1 -1
- package/dist/context-engine/compaction/templates/summary-9-section.d.ts.map +1 -1
- package/dist/context-engine/compaction/templates/summary-9-section.js +4 -3
- package/dist/context-engine/compaction/templates/summary-9-section.js.map +1 -1
- package/dist/context-engine/engine.d.ts +7 -176
- package/dist/context-engine/engine.d.ts.map +1 -1
- package/dist/context-engine/engine.js +0 -2
- package/dist/context-engine/engine.js.map +1 -1
- package/dist/context-engine/index.d.ts +8 -49
- package/dist/context-engine/io-types.d.ts +144 -0
- package/dist/context-engine/io-types.d.ts.map +1 -0
- package/dist/context-engine/locale-packs/types.d.ts +1 -1
- package/dist/context-engine/locale-packs/types.js.map +1 -1
- package/dist/context-engine/privacy-filter.d.ts +1 -2
- package/dist/context-engine/privacy-filter.d.ts.map +1 -1
- package/dist/context-engine/privacy-filter.js +1 -2
- package/dist/context-engine/privacy-filter.js.map +1 -1
- package/dist/errors/index.d.ts +2 -2
- package/dist/errors/index.js +2 -2
- package/dist/errors/index.js.map +1 -1
- package/dist/facade.d.ts +43 -52
- package/dist/facade.d.ts.map +1 -1
- package/dist/facade.js +5 -3
- package/dist/facade.js.map +1 -1
- package/dist/graph/entity-resolver.js +1 -1
- package/dist/graph/entity-resolver.js.map +1 -1
- package/dist/index.d.ts +17 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -7
- package/dist/index.js.map +1 -1
- package/dist/internal/envelope.js +57 -0
- package/dist/internal/envelope.js.map +1 -0
- package/dist/internal/injection-heuristics.js +0 -0
- package/dist/internal/injection-heuristics.js.map +1 -1
- package/dist/internal/llm-json.js +40 -0
- package/dist/internal/llm-json.js.map +1 -0
- package/dist/internal/storage-adapter.d.ts +23 -10
- package/dist/internal/storage-adapter.d.ts.map +1 -1
- package/dist/memory-interface.d.ts +131 -0
- package/dist/memory-interface.d.ts.map +1 -0
- package/dist/package.js +6 -0
- package/dist/package.js.map +1 -0
- package/dist/search/iterative.d.ts.map +1 -1
- package/dist/search/iterative.js +2 -3
- package/dist/search/iterative.js.map +1 -1
- package/dist/search/query-transform.d.ts.map +1 -1
- package/dist/search/query-transform.js +2 -3
- package/dist/search/query-transform.js.map +1 -1
- package/dist/tiers/episodic-memory.js +3 -3
- package/dist/tiers/episodic-memory.js.map +1 -1
- package/dist/tiers/index.d.ts +2 -2
- package/dist/tiers/insight-memory.js +1 -1
- package/dist/tiers/insight-memory.js.map +1 -1
- package/dist/tiers/procedural-memory.d.ts +1 -1
- package/dist/tiers/procedural-memory.js +2 -2
- package/dist/tiers/procedural-memory.js.map +1 -1
- package/dist/tiers/semantic-memory.d.ts +58 -4
- package/dist/tiers/semantic-memory.d.ts.map +1 -1
- package/dist/tiers/semantic-memory.js +103 -20
- package/dist/tiers/semantic-memory.js.map +1 -1
- package/dist/tools/block-tools.d.ts +19 -57
- package/dist/tools/block-tools.d.ts.map +1 -1
- package/dist/tools/block-tools.js.map +1 -1
- package/dist/tools/fact-tools.d.ts +53 -214
- package/dist/tools/fact-tools.d.ts.map +1 -1
- package/dist/tools/fact-tools.js +3 -2
- package/dist/tools/fact-tools.js.map +1 -1
- package/dist/tools/recall-tools.d.ts +34 -175
- package/dist/tools/recall-tools.d.ts.map +1 -1
- package/dist/tools/recall-tools.js +5 -4
- package/dist/tools/recall-tools.js.map +1 -1
- package/dist/tools/runbook-tools.d.ts +15 -58
- package/dist/tools/runbook-tools.d.ts.map +1 -1
- package/dist/tools/runbook-tools.js.map +1 -1
- package/dist/tools/types.d.ts +1 -1
- package/package.json +18 -17
- package/src/conflict/index.ts +29 -0
- package/src/conflict/locale-packs/en.ts +142 -0
- package/src/conflict/locale-packs/index.ts +17 -0
- package/src/conflict/locale-packs/types.ts +138 -0
- package/src/conflict/pipeline.ts +371 -0
- package/src/conflict/stages/helpers.ts +65 -0
- package/src/conflict/stages/stage1-exact-dedup.ts +40 -0
- package/src/conflict/stages/stage2-embedding-three-zone.ts +54 -0
- package/src/conflict/stages/stage3-heuristic-regex.ts +51 -0
- package/src/conflict/stages/stage4-subject-predicate.ts +46 -0
- package/src/conflict/stages/stage5-defer-to-deep.ts +34 -0
- package/src/conflict/types.ts +263 -0
- package/src/consolidator/budget.ts +312 -0
- package/src/consolidator/decay.ts +246 -0
- package/src/consolidator/dlq.ts +61 -0
- package/src/consolidator/errors.ts +87 -0
- package/src/consolidator/idempotency.ts +27 -0
- package/src/consolidator/index.ts +269 -0
- package/src/consolidator/lock.ts +141 -0
- package/src/consolidator/noise-filter.ts +235 -0
- package/src/consolidator/phases/deep.ts +308 -0
- package/src/consolidator/phases/induce.ts +423 -0
- package/src/consolidator/phases/learned-context.ts +233 -0
- package/src/consolidator/phases/light.ts +166 -0
- package/src/consolidator/phases/reflect.ts +504 -0
- package/src/consolidator/phases/standard.ts +1002 -0
- package/src/consolidator/reconcile.ts +273 -0
- package/src/consolidator/runtime.ts +1049 -0
- package/src/consolidator/scheduler.ts +182 -0
- package/src/consolidator/triggers.ts +134 -0
- package/src/consolidator/types.ts +622 -0
- package/src/context-engine/annotations.ts +161 -0
- package/src/context-engine/auto-recall.ts +92 -0
- package/src/context-engine/compaction/clear-tool-results.ts +232 -0
- package/src/context-engine/compaction/compactor.ts +403 -0
- package/src/context-engine/compaction/hooks/reanchor-persona-block.ts +55 -0
- package/src/context-engine/compaction/hooks/reanchor-pinned-facts.ts +66 -0
- package/src/context-engine/compaction/hooks/reanchor-project-rules.ts +57 -0
- package/src/context-engine/compaction/hooks/reanchor-recent-results.ts +116 -0
- package/src/context-engine/compaction/hooks/types.ts +68 -0
- package/src/context-engine/compaction/index.ts +60 -0
- package/src/context-engine/compaction/templates/marker.ts +33 -0
- package/src/context-engine/compaction/templates/summary-9-section.ts +223 -0
- package/src/context-engine/compaction/thresholds.ts +107 -0
- package/src/context-engine/compaction/types.ts +255 -0
- package/src/context-engine/engine.ts +970 -0
- package/src/context-engine/index.ts +118 -0
- package/src/context-engine/io-types.ts +162 -0
- package/src/context-engine/locale-packs/en.ts +107 -0
- package/src/context-engine/locale-packs/index.ts +19 -0
- package/src/context-engine/locale-packs/resolver.ts +197 -0
- package/src/context-engine/locale-packs/types.ts +177 -0
- package/src/context-engine/metadata.ts +114 -0
- package/src/context-engine/preambles/inbound-en.ts +19 -0
- package/src/context-engine/privacy-filter.ts +190 -0
- package/src/context-engine/templates/base-en.ts +19 -0
- package/src/context-engine/templates/composer.ts +91 -0
- package/src/context-engine/token-budget.ts +319 -0
- package/src/context-engine/token-counter.ts +157 -0
- package/src/errors/index.ts +273 -0
- package/src/facade.ts +750 -0
- package/src/graph/entity-resolver.ts +364 -0
- package/src/graph/index.ts +24 -0
- package/src/index.ts +259 -0
- package/src/internal/contextualize.ts +173 -0
- package/src/internal/embedder-binding.ts +53 -0
- package/src/internal/envelope.ts +70 -0
- package/src/internal/id.ts +23 -0
- package/src/internal/injection-heuristics.ts +0 -0
- package/src/internal/llm-json.ts +38 -0
- package/src/internal/spans.ts +35 -0
- package/src/internal/storage-adapter.ts +963 -0
- package/src/memory-interface.ts +158 -0
- package/src/migration/embedder-migration.ts +237 -0
- package/src/migration/index.ts +13 -0
- package/src/search/explain.ts +123 -0
- package/src/search/fit-weights.ts +109 -0
- package/src/search/graph-ppr.ts +38 -0
- package/src/search/index.ts +55 -0
- package/src/search/iterative.ts +658 -0
- package/src/search/query-transform.ts +285 -0
- package/src/search/rrf.ts +225 -0
- package/src/search/trust.ts +55 -0
- package/src/search/types.ts +50 -0
- package/src/tiers/episodic-memory.ts +463 -0
- package/src/tiers/index.ts +50 -0
- package/src/tiers/insight-memory.ts +208 -0
- package/src/tiers/procedural-memory.ts +482 -0
- package/src/tiers/semantic-memory.ts +1729 -0
- package/src/tiers/session-memory.ts +271 -0
- package/src/tiers/shared-memory.ts +62 -0
- package/src/tiers/working-memory.ts +368 -0
- package/src/tools/block-tools.ts +172 -0
- package/src/tools/fact-tools.ts +473 -0
- package/src/tools/index.ts +109 -0
- package/src/tools/recall-tools.ts +303 -0
- package/src/tools/runbook-tools.ts +107 -0
- package/src/tools/taint.ts +39 -0
- package/src/tools/types.ts +37 -0
- package/dist/conflict/locale-packs/index.js +0 -4
- package/dist/context-engine/compaction/index.js +0 -10
- package/dist/context-engine/index.d.ts.map +0 -1
- package/dist/context-engine/index.js +0 -25
- package/dist/context-engine/locale-packs/index.js +0 -4
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { ConsolidatorTriggerSpec } from "./types.js";
|
|
1
|
+
import { ConsolidatorTriggerReason, ConsolidatorTriggerSpec, PhaseOutcome } from "./types.js";
|
|
2
2
|
import { ParsedTrigger } from "./triggers.js";
|
|
3
|
-
import { Consolidator } from "./runtime.js";
|
|
4
3
|
import { SessionScope } from "@graphorin/core";
|
|
5
4
|
|
|
6
5
|
//#region src/consolidator/scheduler.d.ts
|
|
@@ -14,6 +13,21 @@ import { SessionScope } from "@graphorin/core";
|
|
|
14
13
|
* @stable
|
|
15
14
|
*/
|
|
16
15
|
type ConsolidatorCatchupPolicy = 'none' | 'last' | 'all';
|
|
16
|
+
/**
|
|
17
|
+
* Subset of the `Consolidator` runtime surface the bridge needs.
|
|
18
|
+
* Defined structurally - mirroring {@link SchedulerLike} - so this
|
|
19
|
+
* module never imports `./runtime.js` (which imports the bridge) and
|
|
20
|
+
* the consolidator module graph stays acyclic (issue #22). The full
|
|
21
|
+
* `Consolidator` is assignable as-is.
|
|
22
|
+
*
|
|
23
|
+
* @stable
|
|
24
|
+
*/
|
|
25
|
+
interface ConsolidatorLike {
|
|
26
|
+
trigger(reason: ConsolidatorTriggerReason, scope: SessionScope): Promise<PhaseOutcome | null>;
|
|
27
|
+
config(): {
|
|
28
|
+
readonly triggers: ReadonlyArray<ConsolidatorTriggerSpec>;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
17
31
|
/**
|
|
18
32
|
* Subset of the `@graphorin/triggers` Scheduler surface the bridge
|
|
19
33
|
* needs. Defined structurally so consumers can inject mocks in
|
|
@@ -105,7 +119,7 @@ interface RegisterTriggersResult {
|
|
|
105
119
|
*
|
|
106
120
|
* @stable
|
|
107
121
|
*/
|
|
108
|
-
declare function registerConsolidatorTriggers(consolidator:
|
|
122
|
+
declare function registerConsolidatorTriggers(consolidator: ConsolidatorLike, scheduler: SchedulerLike, options: RegisterTriggersOptions): Promise<RegisterTriggersResult>;
|
|
109
123
|
//#endregion
|
|
110
124
|
export { ConsolidatorCatchupPolicy, RegisterTriggersOptions, RegisterTriggersResult, SchedulerLike, TriggerDeclarationLike, registerConsolidatorTriggers };
|
|
111
125
|
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.d.ts","names":[],"sources":["../../src/consolidator/scheduler.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","names":[],"sources":["../../src/consolidator/scheduler.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AA8DA;;;;;AAeiB,KAnDL,yBAAA,GAmD4B,MAAA,GAAA,MAAA,GAAA,KAAA;;;;;;;AAkCxC;;;AAMoB,UAhFH,gBAAA,CAgFG;EAAa,OAAA,CAAA,MAAA,EA/Ef,yBA+Ee,EAAA,KAAA,EA/EmB,YA+EnB,CAAA,EA/EkC,OA+ElC,CA/E0C,YA+E1C,GAAA,IAAA,CAAA;EAoBX,MAAA,EAAA,EAAA;IACN,SAAA,QAAA,EAnGiB,aAmGjB,CAnG+B,uBAmG/B,CAAA;EACH,CAAA;;;;;;;;;UA1FI,aAAA;wBACO,yBAAyB;2BACtB;;;;;;;;;;UAWV,sBAAA;;;;mDAIkC;;6BAEtB;;;oBAGT;;;;;UAMH,uBAAA;;;;;;;kBAOC;;;;;;2BAMS;;;;;;kBAMT;;;;;;;qBAOG,cAAc;;;;;;;UAQlB,sBAAA;uBACM;;mBAEJ;;;oBAGC;;;;;;;;;;;;;;;;;;;iBAoBE,4BAAA,eACN,6BACH,wBACF,0BACR,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.js","names":["catchupPolicy: ConsolidatorCatchupPolicy","registered: Array<{ id: string; kind: ParsedTrigger['kind']; raw: string }>","skipped: Array<{ raw: string; reason: 'unsupported-by-scheduler' | 'filtered-out' }>","declaration: TriggerDeclarationLike"],"sources":["../../src/consolidator/scheduler.ts"],"sourcesContent":["/**\n * Bridge helpers - wire a {@link Consolidator} into the\n * `@graphorin/triggers` Scheduler so trigger declarations fire on\n * the documented schedule (DEC-150 - \"same code path lib + server\").\n *\n * The bridge is structural: any object that exposes the same shape\n * as `@graphorin/triggers`'s `Scheduler` (a `register(declaration)`\n * method) works. The structural import keeps `@graphorin/memory`\n * dependency-free at the type level - the workspace consumer\n * supplies the actual scheduler at runtime.\n *\n * @packageDocumentation\n */\n\nimport type { SessionScope } from '@graphorin/core';\nimport
|
|
1
|
+
{"version":3,"file":"scheduler.js","names":["catchupPolicy: ConsolidatorCatchupPolicy","registered: Array<{ id: string; kind: ParsedTrigger['kind']; raw: string }>","skipped: Array<{ raw: string; reason: 'unsupported-by-scheduler' | 'filtered-out' }>","declaration: TriggerDeclarationLike"],"sources":["../../src/consolidator/scheduler.ts"],"sourcesContent":["/**\n * Bridge helpers - wire a {@link Consolidator} into the\n * `@graphorin/triggers` Scheduler so trigger declarations fire on\n * the documented schedule (DEC-150 - \"same code path lib + server\").\n *\n * The bridge is structural: any object that exposes the same shape\n * as `@graphorin/triggers`'s `Scheduler` (a `register(declaration)`\n * method) works. The structural import keeps `@graphorin/memory`\n * dependency-free at the type level - the workspace consumer\n * supplies the actual scheduler at runtime.\n *\n * @packageDocumentation\n */\n\nimport type { SessionScope } from '@graphorin/core';\nimport { type ParsedTrigger, parseTriggerSpec, reasonFromTrigger } from './triggers.js';\nimport type { ConsolidatorTriggerReason, ConsolidatorTriggerSpec, PhaseOutcome } from './types.js';\n\n/**\n * Catch-up policy applied when a trigger missed one or more fires\n * while the scheduler was offline. Mirrors\n * `@graphorin/triggers`'s `CatchupPolicy` - duplicated here so the\n * memory package stays import-free at the type level.\n *\n * @stable\n */\nexport type ConsolidatorCatchupPolicy = 'none' | 'last' | 'all';\n\n/**\n * Subset of the `Consolidator` runtime surface the bridge needs.\n * Defined structurally - mirroring {@link SchedulerLike} - so this\n * module never imports `./runtime.js` (which imports the bridge) and\n * the consolidator module graph stays acyclic (issue #22). The full\n * `Consolidator` is assignable as-is.\n *\n * @stable\n */\nexport interface ConsolidatorLike {\n trigger(reason: ConsolidatorTriggerReason, scope: SessionScope): Promise<PhaseOutcome | null>;\n config(): { readonly triggers: ReadonlyArray<ConsolidatorTriggerSpec> };\n}\n\n/**\n * Subset of the `@graphorin/triggers` Scheduler surface the bridge\n * needs. Defined structurally so consumers can inject mocks in\n * tests without taking the package dependency.\n *\n * @stable\n */\nexport interface SchedulerLike {\n register(declaration: TriggerDeclarationLike): Promise<unknown>;\n unregister?(id: string): Promise<void>;\n}\n\n/**\n * Subset of `@graphorin/triggers`'s `TriggerDeclaration` accepted\n * by {@link SchedulerLike.register}. Field names mirror the\n * upstream shape exactly so the structural type is interchangeable\n * with the real export.\n *\n * @stable\n */\nexport interface TriggerDeclarationLike {\n readonly id: string;\n readonly kind: 'cron' | 'interval' | 'idle' | 'event';\n readonly spec: string;\n readonly callback: (payload?: unknown) => void | Promise<void>;\n readonly options: {\n readonly catchupPolicy?: ConsolidatorCatchupPolicy;\n readonly maxCatchupRuns?: number;\n readonly catchupWindowMs?: number;\n readonly tags?: ReadonlyArray<string>;\n readonly acknowledgeLibMode?: boolean;\n };\n}\n\n/** Options accepted by {@link registerConsolidatorTriggers}. */\nexport interface RegisterTriggersOptions {\n /**\n * Scope passed to `consolidator.trigger(...)` for every fire. The\n * consolidator only operates on a single user/session at a time\n * (DEC-005 single-user-per-process); multi-tenant deployments\n * register one scheduler-trigger pair per scope.\n */\n readonly scope: SessionScope;\n /**\n * Catch-up policy applied to every registered trigger. Defaults\n * to `'none'` per DEC-150 - safest for personal-assistant\n * scenarios.\n */\n readonly catchupPolicy?: ConsolidatorCatchupPolicy;\n /** Suppress the per-process library-mode WARN. Defaults to `true`. */\n readonly acknowledgeLibMode?: boolean;\n /** Optional id prefix - useful when multiple scopes share a scheduler. */\n readonly idPrefix?: string;\n /** Optional tags forwarded to the scheduler. */\n readonly tags?: ReadonlyArray<string>;\n /**\n * Filter - only register the supplied subset of triggers. By\n * default every parseable trigger declared on the consolidator is\n * registered (turn / event triggers are skipped because the\n * Scheduler has no way to fire them on its own).\n */\n readonly include?: ReadonlyArray<ConsolidatorTriggerSpec>;\n}\n\n/**\n * Outcome of {@link registerConsolidatorTriggers}.\n *\n * @stable\n */\nexport interface RegisterTriggersResult {\n readonly registered: ReadonlyArray<{\n readonly id: string;\n readonly kind: ParsedTrigger['kind'];\n readonly raw: string;\n }>;\n readonly skipped: ReadonlyArray<{\n readonly raw: string;\n readonly reason: 'unsupported-by-scheduler' | 'filtered-out';\n }>;\n}\n\n/**\n * Register every cron / idle / interval trigger declared on the\n * supplied consolidator with the scheduler. Each trigger fires\n * `consolidator.trigger(reason, scope)` so the lib-mode + server\n * paths converge on the same handler.\n *\n * Turn triggers (`turn:N`) and event triggers (`event:NAME`) are\n * skipped - the scheduler cannot count user turns autonomously, and\n * event triggers fire from the consumer's emit path. The runtime\n * caller is responsible for those (e.g. invoking\n * `consolidator.trigger(...)` from the agent loop).\n *\n * @stable\n */\nexport async function registerConsolidatorTriggers(\n consolidator: ConsolidatorLike,\n scheduler: SchedulerLike,\n options: RegisterTriggersOptions,\n): Promise<RegisterTriggersResult> {\n const config = consolidator.config();\n const include = options.include ?? config.triggers;\n const acknowledgeLibMode = options.acknowledgeLibMode ?? true;\n const catchupPolicy: ConsolidatorCatchupPolicy = options.catchupPolicy ?? 'none';\n const registered: Array<{ id: string; kind: ParsedTrigger['kind']; raw: string }> = [];\n const skipped: Array<{ raw: string; reason: 'unsupported-by-scheduler' | 'filtered-out' }> = [];\n const seenSet = new Set(include);\n for (const spec of config.triggers) {\n if (!seenSet.has(spec)) {\n skipped.push({ raw: spec, reason: 'filtered-out' });\n continue;\n }\n const parsed = parseTriggerSpec(spec);\n if (parsed.kind === 'turn' || parsed.kind === 'event' || parsed.kind === 'budget') {\n skipped.push({ raw: parsed.raw, reason: 'unsupported-by-scheduler' });\n continue;\n }\n const id = `${options.idPrefix ?? 'consolidator'}:${parsed.raw}`;\n const kind: 'cron' | 'idle' | 'interval' = parsed.kind === 'cron' ? 'cron' : 'idle';\n const declaration: TriggerDeclarationLike = {\n id,\n kind,\n spec: parsed.kind === 'cron' ? parsed.expression : String(parsed.idleMs),\n async callback() {\n await consolidator.trigger(reasonFromTrigger(parsed), options.scope);\n },\n options: {\n catchupPolicy,\n acknowledgeLibMode,\n ...(options.tags !== undefined ? { tags: options.tags } : {}),\n },\n };\n await scheduler.register(declaration);\n registered.push({ id, kind: parsed.kind, raw: parsed.raw });\n }\n return Object.freeze({\n registered: Object.freeze(registered),\n skipped: Object.freeze(skipped),\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAyIA,eAAsB,6BACpB,cACA,WACA,SACiC;CACjC,MAAM,SAAS,aAAa,QAAQ;CACpC,MAAM,UAAU,QAAQ,WAAW,OAAO;CAC1C,MAAM,qBAAqB,QAAQ,sBAAsB;CACzD,MAAMA,gBAA2C,QAAQ,iBAAiB;CAC1E,MAAMC,aAA8E,EAAE;CACtF,MAAMC,UAAuF,EAAE;CAC/F,MAAM,UAAU,IAAI,IAAI,QAAQ;AAChC,MAAK,MAAM,QAAQ,OAAO,UAAU;AAClC,MAAI,CAAC,QAAQ,IAAI,KAAK,EAAE;AACtB,WAAQ,KAAK;IAAE,KAAK;IAAM,QAAQ;IAAgB,CAAC;AACnD;;EAEF,MAAM,SAAS,iBAAiB,KAAK;AACrC,MAAI,OAAO,SAAS,UAAU,OAAO,SAAS,WAAW,OAAO,SAAS,UAAU;AACjF,WAAQ,KAAK;IAAE,KAAK,OAAO;IAAK,QAAQ;IAA4B,CAAC;AACrE;;EAEF,MAAM,KAAK,GAAG,QAAQ,YAAY,eAAe,GAAG,OAAO;EAE3D,MAAMC,cAAsC;GAC1C;GACA,MAHyC,OAAO,SAAS,SAAS,SAAS;GAI3E,MAAM,OAAO,SAAS,SAAS,OAAO,aAAa,OAAO,OAAO,OAAO;GACxE,MAAM,WAAW;AACf,UAAM,aAAa,QAAQ,kBAAkB,OAAO,EAAE,QAAQ,MAAM;;GAEtE,SAAS;IACP;IACA;IACA,GAAI,QAAQ,SAAS,SAAY,EAAE,MAAM,QAAQ,MAAM,GAAG,EAAE;IAC7D;GACF;AACD,QAAM,UAAU,SAAS,YAAY;AACrC,aAAW,KAAK;GAAE;GAAI,MAAM,OAAO;GAAM,KAAK,OAAO;GAAK,CAAC;;AAE7D,QAAO,OAAO,OAAO;EACnB,YAAY,OAAO,OAAO,WAAW;EACrC,SAAS,OAAO,OAAO,QAAQ;EAChC,CAAC"}
|
|
@@ -122,11 +122,25 @@ interface ConsolidatorConfig {
|
|
|
122
122
|
/**
|
|
123
123
|
* Weights for the multi-signal salience score (X-1) that orders both
|
|
124
124
|
* threshold archiving and capacity eviction. Defaults to
|
|
125
|
-
*
|
|
125
|
+
* `DEFAULT_SALIENCE_WEIGHTS`; with neutral importance, active
|
|
126
126
|
* status, and first-party provenance salience equals plain retention.
|
|
127
127
|
*/
|
|
128
128
|
readonly salienceWeights: SalienceWeights;
|
|
129
129
|
readonly maxStandardBatchSize: number;
|
|
130
|
+
/**
|
|
131
|
+
* Input-side transcript budget for the standard phase, in characters
|
|
132
|
+
* (W-081; chars/4 is the package's deterministic token proxy, so the
|
|
133
|
+
* 60k default approximates 15k tokens). `maxStandardBatchSize` bounds
|
|
134
|
+
* only the MESSAGE COUNT of a slice; without a size budget a batch of
|
|
135
|
+
* near-cap messages can render to hundreds of kilobytes, overflow a
|
|
136
|
+
* cheap-tier model context on every retry and wedge the cursor
|
|
137
|
+
* permanently. A transcript over this budget is half-split BEFORE the
|
|
138
|
+
* provider call (same convergent recursion as the
|
|
139
|
+
* `finishReason: 'length'` output handling); a single message that
|
|
140
|
+
* alone exceeds the budget is tail-truncated and recorded on the
|
|
141
|
+
* phase span. Conservative for CJK scripts by design.
|
|
142
|
+
*/
|
|
143
|
+
readonly maxTranscriptChars: number;
|
|
130
144
|
readonly maxDeepConflictsPerRun: number;
|
|
131
145
|
readonly dlqMaxRetries: number;
|
|
132
146
|
readonly dlqBaseBackoffMs: number;
|
|
@@ -177,6 +191,14 @@ interface ConsolidatorConfig {
|
|
|
177
191
|
readonly importanceThreshold: number;
|
|
178
192
|
/** Upper bound on salient questions reflection asks per pass. Defaults to `3`. */
|
|
179
193
|
readonly reflectionMaxQuestions: number;
|
|
194
|
+
/**
|
|
195
|
+
* W-082: cap on the unreviewed (quarantined) insight queue. Quarantined
|
|
196
|
+
* insights are exempt from reflection pass-decay - their decay clock
|
|
197
|
+
* starts at validation - and this bound keeps the review queue from
|
|
198
|
+
* growing without limit: beyond it the oldest quarantined insights are
|
|
199
|
+
* pruned. Defaults to `100`.
|
|
200
|
+
*/
|
|
201
|
+
readonly reflectionMaxQuarantinedInsights: number;
|
|
180
202
|
/**
|
|
181
203
|
* Contextual retrieval for facts written by the standard phase (P1-3).
|
|
182
204
|
* `'late-chunk'` (default at every tier) relies on the offline
|
|
@@ -390,6 +412,8 @@ interface CreateConsolidatorOptions {
|
|
|
390
412
|
/** Override the {@link ConsolidatorConfig.salienceWeights} default (X-1). */
|
|
391
413
|
readonly salienceWeights?: SalienceWeights;
|
|
392
414
|
readonly maxStandardBatchSize?: number;
|
|
415
|
+
/** Override the per-tier {@link ConsolidatorConfig.maxTranscriptChars} default (W-081). */
|
|
416
|
+
readonly maxTranscriptChars?: number;
|
|
393
417
|
readonly maxDeepConflictsPerRun?: number;
|
|
394
418
|
readonly dlqMaxRetries?: number;
|
|
395
419
|
readonly dlqBaseBackoffMs?: number;
|
|
@@ -409,6 +433,8 @@ interface CreateConsolidatorOptions {
|
|
|
409
433
|
readonly importanceThreshold?: number;
|
|
410
434
|
/** Override the {@link ConsolidatorConfig.reflectionMaxQuestions} default (P1-1). */
|
|
411
435
|
readonly reflectionMaxQuestions?: number;
|
|
436
|
+
/** Override the {@link ConsolidatorConfig.reflectionMaxQuarantinedInsights} default (W-082). */
|
|
437
|
+
readonly reflectionMaxQuarantinedInsights?: number;
|
|
412
438
|
/** Override the per-tier {@link ConsolidatorConfig.contextualRetrieval} default (P1-3). */
|
|
413
439
|
readonly contextualRetrieval?: ContextualRetrievalMode;
|
|
414
440
|
/** Override the per-tier {@link ConsolidatorConfig.learnedContext} default (D3). */
|
|
@@ -431,11 +457,14 @@ declare const CONSOLIDATOR_TIER_DEFAULTS: Readonly<Record<ConsolidatorTier, {
|
|
|
431
457
|
readonly cheapModel: string | null;
|
|
432
458
|
readonly deepModel: string | null;
|
|
433
459
|
readonly onExceed: OnBudgetExceed;
|
|
460
|
+
/** W-081: input transcript budget for one standard-phase slice. */
|
|
461
|
+
readonly maxTranscriptChars: number;
|
|
434
462
|
readonly formEpisodes: boolean;
|
|
435
463
|
readonly importanceScoring: boolean;
|
|
436
464
|
readonly reflection: boolean;
|
|
437
465
|
readonly importanceThreshold: number;
|
|
438
466
|
readonly reflectionMaxQuestions: number;
|
|
467
|
+
readonly reflectionMaxQuarantinedInsights: number;
|
|
439
468
|
readonly contextualRetrieval: ContextualRetrievalMode;
|
|
440
469
|
readonly learnedContext: boolean;
|
|
441
470
|
readonly learnedContextMaxChars: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/consolidator/types.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/consolidator/types.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;AA0OuD,KA9M3C,uBAAA,GA8M2C,QAAA,MAAA,EAAA,GAAA,QAAA,MAAA,EAAA,GAAA,QAAA,MAAA,EAAA,GAAA,SAAA,MAAA,EAAA,GAAA,UAAA,MAAA,EAAA;AAsBvD;AAcA;AAsBA;;;;;AAGmB,KA7PP,gBAAA,GA6PO,MAAA,GAAA,OAAA,GAAA,UAAA,GAAA,MAAA,GAAA,QAAA;;;;;AA6BnB;AAyBY,UA5SK,yBAAA,CA4SQ;EACd,SAAA,IAAA,EAAA,MAAA,GAAA,MAAA,GAAA,MAAA,GAAA,OAAA,GAAA,QAAA,GAAA,QAAA;EACS,SAAA,KAAA,CAAA,EAAA,MAAA,GAAA,MAAA;;;AAUpB;;;;;;;;;;;AAyCoB,UAhVH,oBAAA,CAgVG;EACU,SAAA,eAAA,EAAA,MAAA;EAAR,SAAA,aAAA,EAAA,MAAA;EACA;;;;;;EAmEI,SAAA,iBAAA,EAAA,MAAA;EAAY,SAAA,gBAAA,EAAA,MAAA;EAUzB;;;;;;;EACX,SAAA,UAAA,EAAA,MAAA;;;;;;;;;;;KAlYU,cAAA;;;;;;KAOA,iBAAA;;;;;;UAOK,kBAAA;qBACI,cAAc;iBAClB;mBACE,cAAc;qBACZ;qBACA;;;;;;;;;;;;;;;yBAeI;;;;;;;;;;;;;;;;;;;4BAmBG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAqFI;;;;;;;;;;;;;;;;;;;;;UAsBf,oBAAA;;;;;;;;;;;;;UAcA,0BAAA;;;;;;;;;;;;;;;;;;;;;UAsBA,kBAAA;iBACA;qBACI,cAAc;mBAChB,cAAc;;;;;;uBAMV;;qBAEF;;;;;;;mBAOF;;;;;;;;;;;;;UAcF,YAAA;kBACC;;;;;;;;;;;;;;;;;;;;;;;KAwBN,aAAA,aACD;kBACS;oBACE;;;;;;;UASL,yBAAA;;;;;;kBAMC;;;;;;;qBAOG;;;;;;;sBAOC;;;;;;;qBAOD;;;;;;sBAMC;oBACF;;;;;sBAKE,cAAc;kBAClB;oBACE,cAAc;sBACZ,QAAQ;sBACR;;;;;;;;;;;;;;;;;;;;;;2BAmBK;;;;;0BAKD;;;;0BAIA;;;;;;;6BAOG;;;;;;;;;;;;;;;;;;;;;;;;;;iCA0BI;;;;;;0BAMP;;;;;;;;;cAUb,4BAA4B,SACvC,OACE;qBAEqB;mBACF,cAAc;;;qBAGZ;;;;;;;;;gCASW"}
|
|
@@ -19,11 +19,13 @@ const CONSOLIDATOR_TIER_DEFAULTS = Object.freeze({
|
|
|
19
19
|
cheapModel: null,
|
|
20
20
|
deepModel: null,
|
|
21
21
|
onExceed: "pause",
|
|
22
|
+
maxTranscriptChars: 6e4,
|
|
22
23
|
formEpisodes: false,
|
|
23
24
|
importanceScoring: false,
|
|
24
25
|
reflection: false,
|
|
25
26
|
importanceThreshold: 3,
|
|
26
27
|
reflectionMaxQuestions: 3,
|
|
28
|
+
reflectionMaxQuarantinedInsights: 100,
|
|
27
29
|
contextualRetrieval: "late-chunk",
|
|
28
30
|
learnedContext: false,
|
|
29
31
|
learnedContextMaxChars: 1200
|
|
@@ -40,11 +42,13 @@ const CONSOLIDATOR_TIER_DEFAULTS = Object.freeze({
|
|
|
40
42
|
cheapModel: null,
|
|
41
43
|
deepModel: null,
|
|
42
44
|
onExceed: "pause",
|
|
45
|
+
maxTranscriptChars: 6e4,
|
|
43
46
|
formEpisodes: false,
|
|
44
47
|
importanceScoring: false,
|
|
45
48
|
reflection: false,
|
|
46
49
|
importanceThreshold: 3,
|
|
47
50
|
reflectionMaxQuestions: 3,
|
|
51
|
+
reflectionMaxQuarantinedInsights: 100,
|
|
48
52
|
contextualRetrieval: "late-chunk",
|
|
49
53
|
learnedContext: false,
|
|
50
54
|
learnedContextMaxChars: 1200
|
|
@@ -65,11 +69,13 @@ const CONSOLIDATOR_TIER_DEFAULTS = Object.freeze({
|
|
|
65
69
|
cheapModel: null,
|
|
66
70
|
deepModel: null,
|
|
67
71
|
onExceed: "log",
|
|
72
|
+
maxTranscriptChars: 6e4,
|
|
68
73
|
formEpisodes: true,
|
|
69
74
|
importanceScoring: true,
|
|
70
75
|
reflection: false,
|
|
71
76
|
importanceThreshold: 3,
|
|
72
77
|
reflectionMaxQuestions: 3,
|
|
78
|
+
reflectionMaxQuarantinedInsights: 100,
|
|
73
79
|
contextualRetrieval: "late-chunk",
|
|
74
80
|
learnedContext: false,
|
|
75
81
|
learnedContextMaxChars: 1200
|
|
@@ -90,11 +96,13 @@ const CONSOLIDATOR_TIER_DEFAULTS = Object.freeze({
|
|
|
90
96
|
cheapModel: null,
|
|
91
97
|
deepModel: null,
|
|
92
98
|
onExceed: "log",
|
|
99
|
+
maxTranscriptChars: 12e4,
|
|
93
100
|
formEpisodes: true,
|
|
94
101
|
importanceScoring: true,
|
|
95
102
|
reflection: true,
|
|
96
103
|
importanceThreshold: 3,
|
|
97
104
|
reflectionMaxQuestions: 3,
|
|
105
|
+
reflectionMaxQuarantinedInsights: 100,
|
|
98
106
|
contextualRetrieval: "late-chunk",
|
|
99
107
|
learnedContext: false,
|
|
100
108
|
learnedContextMaxChars: 1200
|
|
@@ -111,11 +119,13 @@ const CONSOLIDATOR_TIER_DEFAULTS = Object.freeze({
|
|
|
111
119
|
cheapModel: null,
|
|
112
120
|
deepModel: null,
|
|
113
121
|
onExceed: "pause",
|
|
122
|
+
maxTranscriptChars: 6e4,
|
|
114
123
|
formEpisodes: false,
|
|
115
124
|
importanceScoring: false,
|
|
116
125
|
reflection: false,
|
|
117
126
|
importanceThreshold: 3,
|
|
118
127
|
reflectionMaxQuestions: 3,
|
|
128
|
+
reflectionMaxQuarantinedInsights: 100,
|
|
119
129
|
contextualRetrieval: "late-chunk",
|
|
120
130
|
learnedContext: false,
|
|
121
131
|
learnedContextMaxChars: 1200
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":["CONSOLIDATOR_TIER_DEFAULTS: Readonly<\n Record<\n ConsolidatorTier,\n {\n readonly ceilings: ConsolidatorCeilings;\n readonly phases: ReadonlyArray<ConsolidatorPhase>;\n readonly cheapModel: string | null;\n readonly deepModel: string | null;\n readonly onExceed: OnBudgetExceed;\n readonly formEpisodes: boolean;\n readonly importanceScoring: boolean;\n readonly reflection: boolean;\n readonly importanceThreshold: number;\n readonly reflectionMaxQuestions: number;\n readonly contextualRetrieval: ContextualRetrievalMode;\n readonly learnedContext: boolean;\n readonly learnedContextMaxChars: number;\n }\n >\n>"],"sources":["../../src/consolidator/types.ts"],"sourcesContent":["/**\n * Public types for the Phase 10c consolidator runtime.\n *\n * The consolidator is the background pipeline that turns raw\n * conversation turns into long-lived facts and episodes. The runtime\n * has three phases (`light` / `standard` / `deep`), an explicit cost\n * envelope, an idempotent cursor, a wait-then-defer lock, and a\n * dead-letter queue for retry-able failures. The binding architecture\n * references are DEC-133 / DEC-134 / ADR-038.\n *\n * @packageDocumentation\n */\n\nimport type { Provider, SessionScope, Tracer } from '@graphorin/core';\nimport type { ContextualRetrievalMode } from '../internal/contextualize.js';\nimport type { MemoryStoreAdapter } from '../internal/storage-adapter.js';\nimport type { EpisodicMemory } from '../tiers/episodic-memory.js';\nimport type { SemanticMemory } from '../tiers/semantic-memory.js';\nimport type { WorkingMemory } from '../tiers/working-memory.js';\nimport type { SalienceWeights } from './decay.js';\n\n/**\n * Trigger discriminator. The `'turn:N'` and `'idle:Xm'` variants are\n * the production defaults per DEC-133. `'cron:EXPR'`, `'event:NAME'`\n * and `'budget:N'` are opt-in.\n *\n * @stable\n */\nexport type ConsolidatorTriggerSpec =\n | `turn:${number}`\n | `idle:${string}`\n | `cron:${string}`\n | `event:${string}`\n | `budget:${number}`;\n\n/**\n * Tier preset that selects a consolidator behaviour bundle. The\n * `'free'` preset is the default per DEC-144 / ADR-038 - no LLM call\n * fires until the operator opts in.\n *\n * @stable\n */\nexport type ConsolidatorTier = 'free' | 'cheap' | 'standard' | 'full' | 'custom';\n\n/**\n * Triggering reason surfaced through `Consolidator.trigger(...)`.\n *\n * @stable\n */\nexport interface ConsolidatorTriggerReason {\n readonly kind: 'turn' | 'idle' | 'cron' | 'event' | 'budget' | 'manual';\n readonly value?: string | number;\n}\n\n/**\n * Daily cost ceilings, tracked per budget window. How a breach is\n * handled depends on {@link OnBudgetExceed}: `'pause'` / `'throw'`\n * enforce, `'log'` (the shipped standard/full presets) only WARNs and\n * keeps running. The USD leg accumulates only when a `priceUsage`\n * pricer is configured (memory-consolidation-02) - without one every\n * call prices at $0 and `maxCostPerDay` can never trip. The default\n * ceiling shape per tier is captured in\n * {@link CONSOLIDATOR_TIER_DEFAULTS}.\n *\n * @stable\n */\nexport interface ConsolidatorCeilings {\n readonly maxTokensPerDay: number;\n readonly maxCostPerDay: number;\n /**\n * ADVISORY (MCON-8): the per-scope lock serializes runs, so effective\n * concurrency is always 1 per scope regardless of this value. The\n * field is retained for forward compatibility; it enforces nothing\n * today.\n */\n readonly maxConcurrentRuns: number;\n readonly maxRunDurationMs: number;\n /**\n * Minimum quiet period between non-manual runs per scope (MCON-8).\n * After each run the runtime persists `nextEligibleAt = now +\n * cooldownMs`; trigger-driven runs (`turn` / `idle` / `cron` /\n * `event` / `budget`) inside that window defer with reason\n * `'cooldown'`. Manual `fireNow(...)` and DLQ replays bypass it.\n */\n readonly cooldownMs: number;\n}\n\n/**\n * Behaviour applied by the budget enforcer when a ceiling is hit\n * mid-run. `'pause'` is the conservative default - the consolidator\n * skips subsequent runs until the next budget reset; `'log'` keeps\n * running with a WARN; `'throw'` raises a typed\n * {@link BudgetExceededError}.\n *\n * @stable\n */\nexport type OnBudgetExceed = 'pause' | 'log' | 'throw';\n\n/**\n * Phase identifier used by the runtime + audit rows.\n *\n * @stable\n */\nexport type ConsolidatorPhase = 'light' | 'standard' | 'deep';\n\n/**\n * Locked-down configuration accepted by `createConsolidator(...)`.\n *\n * @stable\n */\nexport interface ConsolidatorConfig {\n readonly triggers: ReadonlyArray<ConsolidatorTriggerSpec>;\n readonly tier: ConsolidatorTier;\n readonly phases: ReadonlyArray<ConsolidatorPhase>;\n readonly ceilings: ConsolidatorCeilings;\n readonly onExceed: OnBudgetExceed;\n /**\n * Advisory label for the standard phase's model - recorded on spans /\n * run telemetry only (MCON-7). Routing happens via\n * `CreateConsolidatorOptions.cheapProvider`; this string disables\n * nothing.\n */\n readonly cheapModel: string | null;\n /**\n * Advisory label for the deep phase's model - telemetry only\n * (MCON-7). Routing happens via\n * `CreateConsolidatorOptions.deepProvider`.\n */\n readonly deepModel: string | null;\n readonly budgetResetSemantics: 'utc' | 'local' | 'sliding-24h';\n readonly noiseFilters: ReadonlyArray<'default' | 'minimal' | 'none'>;\n readonly lockWaitMs: number;\n readonly decayTauDays: number;\n readonly decayArchiveThreshold: number;\n /**\n * Capacity-bounded eviction target for the light phase (X-1). When set,\n * each light pass archives the lowest-salience live facts in the LRU\n * decay window down to this many - **cost / staleness control, not an\n * accuracy lever**. `null` (the default at every tier) leaves storage\n * unbounded, so behaviour is identical to pre-X-1. Archiving is a soft,\n * recoverable move; nothing is hard-deleted.\n */\n readonly decayCapacity: number | null;\n /**\n * Weights for the multi-signal salience score (X-1) that orders both\n * threshold archiving and capacity eviction. Defaults to\n * {@link DEFAULT_SALIENCE_WEIGHTS}; with neutral importance, active\n * status, and first-party provenance salience equals plain retention.\n */\n readonly salienceWeights: SalienceWeights;\n readonly maxStandardBatchSize: number;\n readonly maxDeepConflictsPerRun: number;\n readonly dlqMaxRetries: number;\n readonly dlqBaseBackoffMs: number;\n readonly dlqMaxBackoffMs: number;\n /**\n * Auto-form a quarantined episode from each processed standard-phase\n * slice (P1-2). Defaults on at the `standard`+ tiers, off at `free` /\n * `cheap` / `custom`. The episode summary is one budgeted LLM call;\n * when the budget is exhausted (or no episodic tier is wired) the\n * phase degrades to fact-only behaviour.\n */\n readonly formEpisodes: boolean;\n /**\n * Ask the episode-summarization call for an LLM importance score\n * (1-10, normalized to `[0, 1]`) so episodic triple-signal retrieval\n * (recency × relevance × importance) runs on all three signals\n * (P1-2). Importance is always a *soft* signal - it never gates\n * retention. Defaults track {@link formEpisodes}.\n */\n readonly importanceScoring: boolean;\n /**\n * Auto-promotion policy (MCON-2). When `true`, the standard phase admits an\n * injection-clean **extraction** fact as `active` instead of quarantined, so\n * routine distillation surfaces in default recall without a manual\n * `memory review --promote`. Injection-flagged facts always stay quarantined\n * - the security gate is preserved - and episodes / insights / induced\n * procedures are unaffected (they remain quarantined-until-validated).\n * Defaults **off** at every tier: it trades the fail-safe default for\n * convenience and is an explicit operator opt-in.\n */\n readonly autoPromoteExtraction: boolean;\n /**\n * Run the deep-phase reflection pass (P1-1): when accumulated episode\n * importance crosses {@link importanceThreshold}, synthesize\n * higher-order, cited insights over recent memories (Generative\n * Agents). Insights land quarantined + `provenance: 'reflection'` and\n * are ranked below the facts they cite. Defaults **on at the `full`\n * tier only** (off at `free` / `cheap` / `standard` / `custom`) - it\n * is the most LLM-intensive phase. A no-op without an episodic tier\n * or an insight-capable storage adapter.\n */\n readonly reflection: boolean;\n /**\n * Sum of recent episode importance (each in `[0, 1]`) at or above\n * which {@link reflection} fires. Below it the pass makes no LLM\n * call. Defaults to `3`.\n */\n readonly importanceThreshold: number;\n /** Upper bound on salient questions reflection asks per pass. Defaults to `3`. */\n readonly reflectionMaxQuestions: number;\n /**\n * Contextual retrieval for facts written by the standard phase (P1-3).\n * `'late-chunk'` (default at every tier) relies on the offline\n * situating-context prefix the shared {@link SemanticMemory} computes\n * for every write - no extra LLM call. `'llm'` is the opt-in\n * enrichment: the standard phase spends one budgeted cheap-model call\n * per additive write to author a 1-2 sentence situating prefix, then\n * passes it as the write's index text. `'off'` indexes the bare text.\n * The `'llm'` mode is **consolidator-only** by construction - the hot\n * write path never has a provider for contextualization.\n */\n readonly contextualRetrieval: ContextualRetrievalMode;\n /**\n * Maintain the learned-context digest block (D3): after the deep\n * phase, one budgeted LLM call rewrites the reserved\n * `learned_context` working block from the previous digest + recent\n * episodes / active insights / active procedures, so the system\n * prompt carries a compact standing summary. Defaults **off at every\n * tier** (Wave-D trial) - a no-op without a working tier handle.\n */\n readonly learnedContext: boolean;\n /** Character bound enforced on the learned-context digest. Default `1200`. */\n readonly learnedContextMaxChars: number;\n}\n\n/**\n * Per-phase last-run snapshot surfaced inside\n * {@link ConsolidatorStatus.lastRuns}. Each entry carries the\n * timestamp of the most recent **completed** invocation for that\n * phase (`undefined` when the phase has never run).\n *\n * @stable\n */\nexport interface ConsolidatorLastRuns {\n readonly light?: string;\n readonly standard?: string;\n readonly deep?: string;\n}\n\n/**\n * Budget snapshot block of {@link ConsolidatorStatus}. Surfaces\n * both the absolute usage and the remaining envelope so consumers\n * (CLI, server health endpoint) can render the operator dashboard\n * without doing the math themselves.\n *\n * @stable\n */\nexport interface ConsolidatorBudgetSnapshot {\n readonly tokensUsedToday: number;\n readonly costUsedToday: number;\n readonly tokensRemaining: number;\n readonly costRemaining: number;\n readonly resetAt: string;\n}\n\n/**\n * Status snapshot returned by {@link Consolidator.status}.\n *\n * Public shape: `{ tier, queueDepth, dlqSize, lastRuns,\n * budgetRemaining, deferredRuns }` - extended with a few additional\n * fields the server health endpoint and the\n * `graphorin consolidator status` CLI consume.\n *\n * `queueDepth` is an alias for {@link pendingConflicts} (the size\n * of the deep-phase queue); both fields are populated for backwards\n * compatibility.\n *\n * @stable\n */\nexport interface ConsolidatorStatus {\n readonly tier: ConsolidatorTier;\n readonly triggers: ReadonlyArray<ConsolidatorTriggerSpec>;\n readonly phases: ReadonlyArray<ConsolidatorPhase>;\n readonly running: boolean;\n readonly paused: boolean;\n /** Most recent completed run timestamp (any phase). */\n readonly lastRunAt?: string;\n /** Phase of the most recent completed run. */\n readonly lastPhase?: ConsolidatorPhase;\n /** Per-phase last-completed timestamps surfaced for CLI / dashboard. */\n readonly lastRuns: ConsolidatorLastRuns;\n /** Spec alias for {@link pendingConflicts}. */\n readonly queueDepth: number;\n readonly pendingConflicts: number;\n readonly dlqSize: number;\n readonly deferredRuns: number;\n readonly emptyExtractions: number;\n readonly budget: ConsolidatorBudgetSnapshot;\n /** Spec alias - surfaces remaining-budget figures at the top level. */\n readonly budgetRemaining: {\n readonly tokens: number;\n readonly costUsd: number;\n };\n}\n\n/**\n * Outcome surfaced by every phase invocation. Recorded into\n * `consolidator_runs` and emitted on the AISpan.\n *\n * @stable\n */\nexport interface PhaseOutcome {\n readonly phase: ConsolidatorPhase;\n readonly status: 'completed' | 'failed' | 'deferred' | 'partial';\n readonly factsCreated: number;\n readonly factsUpdated: number;\n readonly conflictsResolved: number;\n /** Episodes auto-formed from the processed slice (P1-2). */\n readonly episodesFormed: number;\n /** Insights synthesized by the deep-phase reflection pass (P1-1). */\n readonly insightsCreated: number;\n /** True when the learned-context digest block was rewritten (D3). */\n readonly learnedContextUpdated?: boolean;\n readonly noiseFilteredCount: number;\n readonly emptyExtractions: number;\n readonly llmTokensUsed: number;\n readonly llmCostUsd: number | null;\n readonly errorMessage: string | null;\n}\n\n/**\n * Listener callback subscribed via\n * {@link Consolidator.onPhaseFinished}. Useful for tests + observers.\n *\n * @stable\n */\nexport type PhaseListener = (\n outcome: PhaseOutcome & {\n readonly scope: SessionScope;\n readonly trigger: ConsolidatorTriggerReason;\n },\n) => void;\n\n/**\n * Options accepted by {@link createConsolidator}.\n *\n * @stable\n */\nexport interface CreateConsolidatorOptions {\n /**\n * Storage adapter - supplies the consolidator state, runs, DLQ,\n * and per-tier helpers. The default `@graphorin/store-sqlite`\n * adapter exposes everything by construction.\n */\n readonly store: MemoryStoreAdapter;\n /**\n * The {@link SemanticMemory} tier instance from the parent\n * `createMemory(...)` facade. The standard phase routes every\n * extracted fact through `semantic.remember(...)` so the conflict\n * pipeline (Phase 10b) handles dedup / supersede.\n */\n readonly semantic: SemanticMemory;\n /**\n * The {@link EpisodicMemory} tier instance from the parent\n * `createMemory(...)` facade. When supplied (and `formEpisodes` is\n * on) the standard phase auto-forms a quarantined episode per\n * processed slice (P1-2). Omitted ⇒ episode formation is skipped.\n */\n readonly episodic?: EpisodicMemory;\n /**\n * The {@link WorkingMemory} tier instance from the parent\n * `createMemory(...)` facade (D3). Required for the learned-context\n * pass - without it the pass is a silent no-op even when\n * `learnedContext` is enabled.\n */\n readonly working?: WorkingMemory;\n /**\n * Provider used by the standard + deep phases. Required when the\n * tier enables either phase; ignored when the active phases\n * collapse to `['light']`.\n */\n readonly provider?: Provider | null;\n readonly tracer?: Tracer;\n /** Override the wall clock - used by tests. */\n readonly now?: () => number;\n /** Random source for stable run ids - used by tests. */\n readonly randomId?: () => string;\n readonly triggers?: ReadonlyArray<ConsolidatorTriggerSpec>;\n readonly tier?: ConsolidatorTier;\n readonly phases?: ReadonlyArray<ConsolidatorPhase>;\n readonly ceilings?: Partial<ConsolidatorCeilings>;\n readonly onExceed?: OnBudgetExceed;\n /**\n * USD pricer for phase LLM usage (memory-consolidation-02). Wire it\n * to `@graphorin/pricing`'s `calculateCost` (or any per-token rate)\n * so the `maxCostPerDay` ceiling can actually accumulate spend -\n * without it every phase prices its calls at $0 and the USD ceiling\n * never trips at any tier.\n *\n * ```ts\n * priceUsage: ({ promptTokens, completionTokens }) =>\n * (promptTokens * 3 + completionTokens * 15) / 1_000_000\n * ```\n */\n readonly priceUsage?: (usage: { promptTokens: number; completionTokens: number }) => number;\n /**\n * Provider routed to the standard phase (extraction / episode /\n * reconcile / situating-context calls) when set (MCON-7). Falls back\n * to `provider`. Pair with `cheapModel` for the telemetry label.\n */\n readonly cheapProvider?: Provider | null;\n /**\n * Provider routed to the deep phase (conflict judge) and the\n * reflection pass when set (MCON-7). Falls back to `provider`.\n */\n readonly deepProvider?: Provider | null;\n readonly cheapModel?: string | null;\n readonly deepModel?: string | null;\n readonly budgetResetSemantics?: 'utc' | 'local' | 'sliding-24h';\n readonly noiseFilters?: ReadonlyArray<'default' | 'minimal' | 'none'>;\n readonly lockWaitMs?: number;\n readonly decayTauDays?: number;\n readonly decayArchiveThreshold?: number;\n /** Override the {@link ConsolidatorConfig.decayCapacity} default (X-1). */\n readonly decayCapacity?: number | null;\n /** Override the {@link ConsolidatorConfig.salienceWeights} default (X-1). */\n readonly salienceWeights?: SalienceWeights;\n readonly maxStandardBatchSize?: number;\n readonly maxDeepConflictsPerRun?: number;\n readonly dlqMaxRetries?: number;\n readonly dlqBaseBackoffMs?: number;\n readonly dlqMaxBackoffMs?: number;\n /** Override the per-tier {@link ConsolidatorConfig.formEpisodes} default (P1-2). */\n readonly formEpisodes?: boolean;\n /** Override the per-tier {@link ConsolidatorConfig.importanceScoring} default (P1-2). */\n readonly importanceScoring?: boolean;\n /**\n * Opt in to auto-promotion of injection-clean extraction facts (MCON-2).\n * Defaults `false`. See {@link ConsolidatorConfig.autoPromoteExtraction}.\n */\n readonly autoPromoteExtraction?: boolean;\n /** Override the per-tier {@link ConsolidatorConfig.reflection} default (P1-1). */\n readonly reflection?: boolean;\n /** Override the {@link ConsolidatorConfig.importanceThreshold} default (P1-1). */\n readonly importanceThreshold?: number;\n /** Override the {@link ConsolidatorConfig.reflectionMaxQuestions} default (P1-1). */\n readonly reflectionMaxQuestions?: number;\n /** Override the per-tier {@link ConsolidatorConfig.contextualRetrieval} default (P1-3). */\n readonly contextualRetrieval?: ContextualRetrievalMode;\n /** Override the per-tier {@link ConsolidatorConfig.learnedContext} default (D3). */\n readonly learnedContext?: boolean;\n /** Override the {@link ConsolidatorConfig.learnedContextMaxChars} default (D3). */\n readonly learnedContextMaxChars?: number;\n /** Default scope used by event triggers + the manual `fireNow` path. */\n readonly defaultScope?: SessionScope;\n}\n\n/**\n * Tier preset table. The defaults follow ADR-038 §4 - `'free'`\n * disables every LLM phase and pins zero ceilings, the upper tiers\n * widen the budget envelope progressively.\n *\n * @stable\n */\nexport const CONSOLIDATOR_TIER_DEFAULTS: Readonly<\n Record<\n ConsolidatorTier,\n {\n readonly ceilings: ConsolidatorCeilings;\n readonly phases: ReadonlyArray<ConsolidatorPhase>;\n readonly cheapModel: string | null;\n readonly deepModel: string | null;\n readonly onExceed: OnBudgetExceed;\n readonly formEpisodes: boolean;\n readonly importanceScoring: boolean;\n readonly reflection: boolean;\n readonly importanceThreshold: number;\n readonly reflectionMaxQuestions: number;\n readonly contextualRetrieval: ContextualRetrievalMode;\n readonly learnedContext: boolean;\n readonly learnedContextMaxChars: number;\n }\n >\n> = Object.freeze({\n free: {\n ceilings: {\n maxTokensPerDay: 0,\n maxCostPerDay: 0,\n maxConcurrentRuns: 1,\n maxRunDurationMs: 5 * 60 * 1000,\n cooldownMs: 60_000,\n },\n phases: ['light'],\n cheapModel: null,\n deepModel: null,\n onExceed: 'pause',\n formEpisodes: false,\n importanceScoring: false,\n reflection: false,\n importanceThreshold: 3,\n reflectionMaxQuestions: 3,\n contextualRetrieval: 'late-chunk',\n learnedContext: false,\n learnedContextMaxChars: 1200,\n },\n cheap: {\n ceilings: {\n maxTokensPerDay: 50_000,\n maxCostPerDay: 0.2,\n maxConcurrentRuns: 1,\n maxRunDurationMs: 5 * 60 * 1000,\n cooldownMs: 60_000,\n },\n phases: ['light', 'standard'],\n cheapModel: null,\n deepModel: null,\n onExceed: 'pause',\n formEpisodes: false,\n importanceScoring: false,\n reflection: false,\n importanceThreshold: 3,\n reflectionMaxQuestions: 3,\n contextualRetrieval: 'late-chunk',\n learnedContext: false,\n learnedContextMaxChars: 1200,\n },\n standard: {\n ceilings: {\n maxTokensPerDay: 200_000,\n maxCostPerDay: 1.0,\n maxConcurrentRuns: 1,\n maxRunDurationMs: 10 * 60 * 1000,\n cooldownMs: 30_000,\n },\n phases: ['light', 'standard', 'deep'],\n cheapModel: null,\n deepModel: null,\n onExceed: 'log',\n formEpisodes: true,\n importanceScoring: true,\n reflection: false,\n importanceThreshold: 3,\n reflectionMaxQuestions: 3,\n contextualRetrieval: 'late-chunk',\n learnedContext: false,\n learnedContextMaxChars: 1200,\n },\n full: {\n ceilings: {\n maxTokensPerDay: 1_000_000,\n maxCostPerDay: 5.0,\n maxConcurrentRuns: 2,\n maxRunDurationMs: 15 * 60 * 1000,\n cooldownMs: 10_000,\n },\n phases: ['light', 'standard', 'deep'],\n cheapModel: null,\n deepModel: null,\n onExceed: 'log',\n formEpisodes: true,\n importanceScoring: true,\n reflection: true,\n importanceThreshold: 3,\n reflectionMaxQuestions: 3,\n contextualRetrieval: 'late-chunk',\n learnedContext: false,\n learnedContextMaxChars: 1200,\n },\n custom: {\n ceilings: {\n maxTokensPerDay: 0,\n maxCostPerDay: 0,\n maxConcurrentRuns: 1,\n maxRunDurationMs: 5 * 60 * 1000,\n cooldownMs: 60_000,\n },\n phases: ['light'],\n cheapModel: null,\n deepModel: null,\n onExceed: 'pause',\n formEpisodes: false,\n importanceScoring: false,\n reflection: false,\n importanceThreshold: 3,\n reflectionMaxQuestions: 3,\n contextualRetrieval: 'late-chunk',\n learnedContext: false,\n learnedContextMaxChars: 1200,\n },\n});\n"],"mappings":";;;;;;;;AAucA,MAAaA,6BAmBT,OAAO,OAAO;CAChB,MAAM;EACJ,UAAU;GACR,iBAAiB;GACjB,eAAe;GACf,mBAAmB;GACnB,kBAAkB,MAAS;GAC3B,YAAY;GACb;EACD,QAAQ,CAAC,QAAQ;EACjB,YAAY;EACZ,WAAW;EACX,UAAU;EACV,cAAc;EACd,mBAAmB;EACnB,YAAY;EACZ,qBAAqB;EACrB,wBAAwB;EACxB,qBAAqB;EACrB,gBAAgB;EAChB,wBAAwB;EACzB;CACD,OAAO;EACL,UAAU;GACR,iBAAiB;GACjB,eAAe;GACf,mBAAmB;GACnB,kBAAkB,MAAS;GAC3B,YAAY;GACb;EACD,QAAQ,CAAC,SAAS,WAAW;EAC7B,YAAY;EACZ,WAAW;EACX,UAAU;EACV,cAAc;EACd,mBAAmB;EACnB,YAAY;EACZ,qBAAqB;EACrB,wBAAwB;EACxB,qBAAqB;EACrB,gBAAgB;EAChB,wBAAwB;EACzB;CACD,UAAU;EACR,UAAU;GACR,iBAAiB;GACjB,eAAe;GACf,mBAAmB;GACnB,kBAAkB,MAAU;GAC5B,YAAY;GACb;EACD,QAAQ;GAAC;GAAS;GAAY;GAAO;EACrC,YAAY;EACZ,WAAW;EACX,UAAU;EACV,cAAc;EACd,mBAAmB;EACnB,YAAY;EACZ,qBAAqB;EACrB,wBAAwB;EACxB,qBAAqB;EACrB,gBAAgB;EAChB,wBAAwB;EACzB;CACD,MAAM;EACJ,UAAU;GACR,iBAAiB;GACjB,eAAe;GACf,mBAAmB;GACnB,kBAAkB,MAAU;GAC5B,YAAY;GACb;EACD,QAAQ;GAAC;GAAS;GAAY;GAAO;EACrC,YAAY;EACZ,WAAW;EACX,UAAU;EACV,cAAc;EACd,mBAAmB;EACnB,YAAY;EACZ,qBAAqB;EACrB,wBAAwB;EACxB,qBAAqB;EACrB,gBAAgB;EAChB,wBAAwB;EACzB;CACD,QAAQ;EACN,UAAU;GACR,iBAAiB;GACjB,eAAe;GACf,mBAAmB;GACnB,kBAAkB,MAAS;GAC3B,YAAY;GACb;EACD,QAAQ,CAAC,QAAQ;EACjB,YAAY;EACZ,WAAW;EACX,UAAU;EACV,cAAc;EACd,mBAAmB;EACnB,YAAY;EACZ,qBAAqB;EACrB,wBAAwB;EACxB,qBAAqB;EACrB,gBAAgB;EAChB,wBAAwB;EACzB;CACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.js","names":["CONSOLIDATOR_TIER_DEFAULTS: Readonly<\n Record<\n ConsolidatorTier,\n {\n readonly ceilings: ConsolidatorCeilings;\n readonly phases: ReadonlyArray<ConsolidatorPhase>;\n readonly cheapModel: string | null;\n readonly deepModel: string | null;\n readonly onExceed: OnBudgetExceed;\n /** W-081: input transcript budget for one standard-phase slice. */\n readonly maxTranscriptChars: number;\n readonly formEpisodes: boolean;\n readonly importanceScoring: boolean;\n readonly reflection: boolean;\n readonly importanceThreshold: number;\n readonly reflectionMaxQuestions: number;\n readonly reflectionMaxQuarantinedInsights: number;\n readonly contextualRetrieval: ContextualRetrievalMode;\n readonly learnedContext: boolean;\n readonly learnedContextMaxChars: number;\n }\n >\n>"],"sources":["../../src/consolidator/types.ts"],"sourcesContent":["/**\n * Public types for the Phase 10c consolidator runtime.\n *\n * The consolidator is the background pipeline that turns raw\n * conversation turns into long-lived facts and episodes. The runtime\n * has three phases (`light` / `standard` / `deep`), an explicit cost\n * envelope, an idempotent cursor, a wait-then-defer lock, and a\n * dead-letter queue for retry-able failures. The binding architecture\n * references are DEC-133 / DEC-134 / ADR-038.\n *\n * @packageDocumentation\n */\n\nimport type { Provider, SessionScope, Tracer } from '@graphorin/core';\nimport type { ContextualRetrievalMode } from '../internal/contextualize.js';\nimport type { MemoryStoreAdapter } from '../internal/storage-adapter.js';\nimport type { EpisodicMemory } from '../tiers/episodic-memory.js';\nimport type { SemanticMemory } from '../tiers/semantic-memory.js';\nimport type { WorkingMemory } from '../tiers/working-memory.js';\nimport type { SalienceWeights } from './decay.js';\n\n/**\n * Trigger discriminator. The `'turn:N'` and `'idle:Xm'` variants are\n * the production defaults per DEC-133. `'cron:EXPR'`, `'event:NAME'`\n * and `'budget:N'` are opt-in.\n *\n * @stable\n */\nexport type ConsolidatorTriggerSpec =\n | `turn:${number}`\n | `idle:${string}`\n | `cron:${string}`\n | `event:${string}`\n | `budget:${number}`;\n\n/**\n * Tier preset that selects a consolidator behaviour bundle. The\n * `'free'` preset is the default per DEC-144 / ADR-038 - no LLM call\n * fires until the operator opts in.\n *\n * @stable\n */\nexport type ConsolidatorTier = 'free' | 'cheap' | 'standard' | 'full' | 'custom';\n\n/**\n * Triggering reason surfaced through `Consolidator.trigger(...)`.\n *\n * @stable\n */\nexport interface ConsolidatorTriggerReason {\n readonly kind: 'turn' | 'idle' | 'cron' | 'event' | 'budget' | 'manual';\n readonly value?: string | number;\n}\n\n/**\n * Daily cost ceilings, tracked per budget window. How a breach is\n * handled depends on {@link OnBudgetExceed}: `'pause'` / `'throw'`\n * enforce, `'log'` (the shipped standard/full presets) only WARNs and\n * keeps running. The USD leg accumulates only when a `priceUsage`\n * pricer is configured (memory-consolidation-02) - without one every\n * call prices at $0 and `maxCostPerDay` can never trip. The default\n * ceiling shape per tier is captured in\n * {@link CONSOLIDATOR_TIER_DEFAULTS}.\n *\n * @stable\n */\nexport interface ConsolidatorCeilings {\n readonly maxTokensPerDay: number;\n readonly maxCostPerDay: number;\n /**\n * ADVISORY (MCON-8): the per-scope lock serializes runs, so effective\n * concurrency is always 1 per scope regardless of this value. The\n * field is retained for forward compatibility; it enforces nothing\n * today.\n */\n readonly maxConcurrentRuns: number;\n readonly maxRunDurationMs: number;\n /**\n * Minimum quiet period between non-manual runs per scope (MCON-8).\n * After each run the runtime persists `nextEligibleAt = now +\n * cooldownMs`; trigger-driven runs (`turn` / `idle` / `cron` /\n * `event` / `budget`) inside that window defer with reason\n * `'cooldown'`. Manual `fireNow(...)` and DLQ replays bypass it.\n */\n readonly cooldownMs: number;\n}\n\n/**\n * Behaviour applied by the budget enforcer when a ceiling is hit\n * mid-run. `'pause'` is the conservative default - the consolidator\n * skips subsequent runs until the next budget reset; `'log'` keeps\n * running with a WARN; `'throw'` raises a typed\n * {@link BudgetExceededError}.\n *\n * @stable\n */\nexport type OnBudgetExceed = 'pause' | 'log' | 'throw';\n\n/**\n * Phase identifier used by the runtime + audit rows.\n *\n * @stable\n */\nexport type ConsolidatorPhase = 'light' | 'standard' | 'deep';\n\n/**\n * Locked-down configuration accepted by `createConsolidator(...)`.\n *\n * @stable\n */\nexport interface ConsolidatorConfig {\n readonly triggers: ReadonlyArray<ConsolidatorTriggerSpec>;\n readonly tier: ConsolidatorTier;\n readonly phases: ReadonlyArray<ConsolidatorPhase>;\n readonly ceilings: ConsolidatorCeilings;\n readonly onExceed: OnBudgetExceed;\n /**\n * Advisory label for the standard phase's model - recorded on spans /\n * run telemetry only (MCON-7). Routing happens via\n * `CreateConsolidatorOptions.cheapProvider`; this string disables\n * nothing.\n */\n readonly cheapModel: string | null;\n /**\n * Advisory label for the deep phase's model - telemetry only\n * (MCON-7). Routing happens via\n * `CreateConsolidatorOptions.deepProvider`.\n */\n readonly deepModel: string | null;\n readonly budgetResetSemantics: 'utc' | 'local' | 'sliding-24h';\n readonly noiseFilters: ReadonlyArray<'default' | 'minimal' | 'none'>;\n readonly lockWaitMs: number;\n readonly decayTauDays: number;\n readonly decayArchiveThreshold: number;\n /**\n * Capacity-bounded eviction target for the light phase (X-1). When set,\n * each light pass archives the lowest-salience live facts in the LRU\n * decay window down to this many - **cost / staleness control, not an\n * accuracy lever**. `null` (the default at every tier) leaves storage\n * unbounded, so behaviour is identical to pre-X-1. Archiving is a soft,\n * recoverable move; nothing is hard-deleted.\n */\n readonly decayCapacity: number | null;\n /**\n * Weights for the multi-signal salience score (X-1) that orders both\n * threshold archiving and capacity eviction. Defaults to\n * `DEFAULT_SALIENCE_WEIGHTS`; with neutral importance, active\n * status, and first-party provenance salience equals plain retention.\n */\n readonly salienceWeights: SalienceWeights;\n readonly maxStandardBatchSize: number;\n /**\n * Input-side transcript budget for the standard phase, in characters\n * (W-081; chars/4 is the package's deterministic token proxy, so the\n * 60k default approximates 15k tokens). `maxStandardBatchSize` bounds\n * only the MESSAGE COUNT of a slice; without a size budget a batch of\n * near-cap messages can render to hundreds of kilobytes, overflow a\n * cheap-tier model context on every retry and wedge the cursor\n * permanently. A transcript over this budget is half-split BEFORE the\n * provider call (same convergent recursion as the\n * `finishReason: 'length'` output handling); a single message that\n * alone exceeds the budget is tail-truncated and recorded on the\n * phase span. Conservative for CJK scripts by design.\n */\n readonly maxTranscriptChars: number;\n readonly maxDeepConflictsPerRun: number;\n readonly dlqMaxRetries: number;\n readonly dlqBaseBackoffMs: number;\n readonly dlqMaxBackoffMs: number;\n /**\n * Auto-form a quarantined episode from each processed standard-phase\n * slice (P1-2). Defaults on at the `standard`+ tiers, off at `free` /\n * `cheap` / `custom`. The episode summary is one budgeted LLM call;\n * when the budget is exhausted (or no episodic tier is wired) the\n * phase degrades to fact-only behaviour.\n */\n readonly formEpisodes: boolean;\n /**\n * Ask the episode-summarization call for an LLM importance score\n * (1-10, normalized to `[0, 1]`) so episodic triple-signal retrieval\n * (recency × relevance × importance) runs on all three signals\n * (P1-2). Importance is always a *soft* signal - it never gates\n * retention. Defaults track {@link formEpisodes}.\n */\n readonly importanceScoring: boolean;\n /**\n * Auto-promotion policy (MCON-2). When `true`, the standard phase admits an\n * injection-clean **extraction** fact as `active` instead of quarantined, so\n * routine distillation surfaces in default recall without a manual\n * `memory review --promote`. Injection-flagged facts always stay quarantined\n * - the security gate is preserved - and episodes / insights / induced\n * procedures are unaffected (they remain quarantined-until-validated).\n * Defaults **off** at every tier: it trades the fail-safe default for\n * convenience and is an explicit operator opt-in.\n */\n readonly autoPromoteExtraction: boolean;\n /**\n * Run the deep-phase reflection pass (P1-1): when accumulated episode\n * importance crosses {@link importanceThreshold}, synthesize\n * higher-order, cited insights over recent memories (Generative\n * Agents). Insights land quarantined + `provenance: 'reflection'` and\n * are ranked below the facts they cite. Defaults **on at the `full`\n * tier only** (off at `free` / `cheap` / `standard` / `custom`) - it\n * is the most LLM-intensive phase. A no-op without an episodic tier\n * or an insight-capable storage adapter.\n */\n readonly reflection: boolean;\n /**\n * Sum of recent episode importance (each in `[0, 1]`) at or above\n * which {@link reflection} fires. Below it the pass makes no LLM\n * call. Defaults to `3`.\n */\n readonly importanceThreshold: number;\n /** Upper bound on salient questions reflection asks per pass. Defaults to `3`. */\n readonly reflectionMaxQuestions: number;\n /**\n * W-082: cap on the unreviewed (quarantined) insight queue. Quarantined\n * insights are exempt from reflection pass-decay - their decay clock\n * starts at validation - and this bound keeps the review queue from\n * growing without limit: beyond it the oldest quarantined insights are\n * pruned. Defaults to `100`.\n */\n readonly reflectionMaxQuarantinedInsights: number;\n /**\n * Contextual retrieval for facts written by the standard phase (P1-3).\n * `'late-chunk'` (default at every tier) relies on the offline\n * situating-context prefix the shared {@link SemanticMemory} computes\n * for every write - no extra LLM call. `'llm'` is the opt-in\n * enrichment: the standard phase spends one budgeted cheap-model call\n * per additive write to author a 1-2 sentence situating prefix, then\n * passes it as the write's index text. `'off'` indexes the bare text.\n * The `'llm'` mode is **consolidator-only** by construction - the hot\n * write path never has a provider for contextualization.\n */\n readonly contextualRetrieval: ContextualRetrievalMode;\n /**\n * Maintain the learned-context digest block (D3): after the deep\n * phase, one budgeted LLM call rewrites the reserved\n * `learned_context` working block from the previous digest + recent\n * episodes / active insights / active procedures, so the system\n * prompt carries a compact standing summary. Defaults **off at every\n * tier** (Wave-D trial) - a no-op without a working tier handle.\n */\n readonly learnedContext: boolean;\n /** Character bound enforced on the learned-context digest. Default `1200`. */\n readonly learnedContextMaxChars: number;\n}\n\n/**\n * Per-phase last-run snapshot surfaced inside\n * {@link ConsolidatorStatus.lastRuns}. Each entry carries the\n * timestamp of the most recent **completed** invocation for that\n * phase (`undefined` when the phase has never run).\n *\n * @stable\n */\nexport interface ConsolidatorLastRuns {\n readonly light?: string;\n readonly standard?: string;\n readonly deep?: string;\n}\n\n/**\n * Budget snapshot block of {@link ConsolidatorStatus}. Surfaces\n * both the absolute usage and the remaining envelope so consumers\n * (CLI, server health endpoint) can render the operator dashboard\n * without doing the math themselves.\n *\n * @stable\n */\nexport interface ConsolidatorBudgetSnapshot {\n readonly tokensUsedToday: number;\n readonly costUsedToday: number;\n readonly tokensRemaining: number;\n readonly costRemaining: number;\n readonly resetAt: string;\n}\n\n/**\n * Status snapshot returned by {@link Consolidator.status}.\n *\n * Public shape: `{ tier, queueDepth, dlqSize, lastRuns,\n * budgetRemaining, deferredRuns }` - extended with a few additional\n * fields the server health endpoint and the\n * `graphorin consolidator status` CLI consume.\n *\n * `queueDepth` is an alias for {@link pendingConflicts} (the size\n * of the deep-phase queue); both fields are populated for backwards\n * compatibility.\n *\n * @stable\n */\nexport interface ConsolidatorStatus {\n readonly tier: ConsolidatorTier;\n readonly triggers: ReadonlyArray<ConsolidatorTriggerSpec>;\n readonly phases: ReadonlyArray<ConsolidatorPhase>;\n readonly running: boolean;\n readonly paused: boolean;\n /** Most recent completed run timestamp (any phase). */\n readonly lastRunAt?: string;\n /** Phase of the most recent completed run. */\n readonly lastPhase?: ConsolidatorPhase;\n /** Per-phase last-completed timestamps surfaced for CLI / dashboard. */\n readonly lastRuns: ConsolidatorLastRuns;\n /** Spec alias for {@link pendingConflicts}. */\n readonly queueDepth: number;\n readonly pendingConflicts: number;\n readonly dlqSize: number;\n readonly deferredRuns: number;\n readonly emptyExtractions: number;\n readonly budget: ConsolidatorBudgetSnapshot;\n /** Spec alias - surfaces remaining-budget figures at the top level. */\n readonly budgetRemaining: {\n readonly tokens: number;\n readonly costUsd: number;\n };\n}\n\n/**\n * Outcome surfaced by every phase invocation. Recorded into\n * `consolidator_runs` and emitted on the AISpan.\n *\n * @stable\n */\nexport interface PhaseOutcome {\n readonly phase: ConsolidatorPhase;\n readonly status: 'completed' | 'failed' | 'deferred' | 'partial';\n readonly factsCreated: number;\n readonly factsUpdated: number;\n readonly conflictsResolved: number;\n /** Episodes auto-formed from the processed slice (P1-2). */\n readonly episodesFormed: number;\n /** Insights synthesized by the deep-phase reflection pass (P1-1). */\n readonly insightsCreated: number;\n /** True when the learned-context digest block was rewritten (D3). */\n readonly learnedContextUpdated?: boolean;\n readonly noiseFilteredCount: number;\n readonly emptyExtractions: number;\n readonly llmTokensUsed: number;\n readonly llmCostUsd: number | null;\n readonly errorMessage: string | null;\n}\n\n/**\n * Listener callback subscribed via\n * {@link Consolidator.onPhaseFinished}. Useful for tests + observers.\n *\n * @stable\n */\nexport type PhaseListener = (\n outcome: PhaseOutcome & {\n readonly scope: SessionScope;\n readonly trigger: ConsolidatorTriggerReason;\n },\n) => void;\n\n/**\n * Options accepted by {@link createConsolidator}.\n *\n * @stable\n */\nexport interface CreateConsolidatorOptions {\n /**\n * Storage adapter - supplies the consolidator state, runs, DLQ,\n * and per-tier helpers. The default `@graphorin/store-sqlite`\n * adapter exposes everything by construction.\n */\n readonly store: MemoryStoreAdapter;\n /**\n * The {@link SemanticMemory} tier instance from the parent\n * `createMemory(...)` facade. The standard phase routes every\n * extracted fact through `semantic.remember(...)` so the conflict\n * pipeline (Phase 10b) handles dedup / supersede.\n */\n readonly semantic: SemanticMemory;\n /**\n * The {@link EpisodicMemory} tier instance from the parent\n * `createMemory(...)` facade. When supplied (and `formEpisodes` is\n * on) the standard phase auto-forms a quarantined episode per\n * processed slice (P1-2). Omitted ⇒ episode formation is skipped.\n */\n readonly episodic?: EpisodicMemory;\n /**\n * The {@link WorkingMemory} tier instance from the parent\n * `createMemory(...)` facade (D3). Required for the learned-context\n * pass - without it the pass is a silent no-op even when\n * `learnedContext` is enabled.\n */\n readonly working?: WorkingMemory;\n /**\n * Provider used by the standard + deep phases. Required when the\n * tier enables either phase; ignored when the active phases\n * collapse to `['light']`.\n */\n readonly provider?: Provider | null;\n readonly tracer?: Tracer;\n /** Override the wall clock - used by tests. */\n readonly now?: () => number;\n /** Random source for stable run ids - used by tests. */\n readonly randomId?: () => string;\n readonly triggers?: ReadonlyArray<ConsolidatorTriggerSpec>;\n readonly tier?: ConsolidatorTier;\n readonly phases?: ReadonlyArray<ConsolidatorPhase>;\n readonly ceilings?: Partial<ConsolidatorCeilings>;\n readonly onExceed?: OnBudgetExceed;\n /**\n * USD pricer for phase LLM usage (memory-consolidation-02). Wire it\n * to `@graphorin/pricing`'s `calculateCost` (or any per-token rate)\n * so the `maxCostPerDay` ceiling can actually accumulate spend -\n * without it every phase prices its calls at $0 and the USD ceiling\n * never trips at any tier.\n *\n * ```ts\n * priceUsage: ({ promptTokens, completionTokens }) =>\n * (promptTokens * 3 + completionTokens * 15) / 1_000_000\n * ```\n */\n readonly priceUsage?: (usage: { promptTokens: number; completionTokens: number }) => number;\n /**\n * Provider routed to the standard phase (extraction / episode /\n * reconcile / situating-context calls) when set (MCON-7). Falls back\n * to `provider`. Pair with `cheapModel` for the telemetry label.\n */\n readonly cheapProvider?: Provider | null;\n /**\n * Provider routed to the deep phase (conflict judge) and the\n * reflection pass when set (MCON-7). Falls back to `provider`.\n */\n readonly deepProvider?: Provider | null;\n readonly cheapModel?: string | null;\n readonly deepModel?: string | null;\n readonly budgetResetSemantics?: 'utc' | 'local' | 'sliding-24h';\n readonly noiseFilters?: ReadonlyArray<'default' | 'minimal' | 'none'>;\n readonly lockWaitMs?: number;\n readonly decayTauDays?: number;\n readonly decayArchiveThreshold?: number;\n /** Override the {@link ConsolidatorConfig.decayCapacity} default (X-1). */\n readonly decayCapacity?: number | null;\n /** Override the {@link ConsolidatorConfig.salienceWeights} default (X-1). */\n readonly salienceWeights?: SalienceWeights;\n readonly maxStandardBatchSize?: number;\n /** Override the per-tier {@link ConsolidatorConfig.maxTranscriptChars} default (W-081). */\n readonly maxTranscriptChars?: number;\n readonly maxDeepConflictsPerRun?: number;\n readonly dlqMaxRetries?: number;\n readonly dlqBaseBackoffMs?: number;\n readonly dlqMaxBackoffMs?: number;\n /** Override the per-tier {@link ConsolidatorConfig.formEpisodes} default (P1-2). */\n readonly formEpisodes?: boolean;\n /** Override the per-tier {@link ConsolidatorConfig.importanceScoring} default (P1-2). */\n readonly importanceScoring?: boolean;\n /**\n * Opt in to auto-promotion of injection-clean extraction facts (MCON-2).\n * Defaults `false`. See {@link ConsolidatorConfig.autoPromoteExtraction}.\n */\n readonly autoPromoteExtraction?: boolean;\n /** Override the per-tier {@link ConsolidatorConfig.reflection} default (P1-1). */\n readonly reflection?: boolean;\n /** Override the {@link ConsolidatorConfig.importanceThreshold} default (P1-1). */\n readonly importanceThreshold?: number;\n /** Override the {@link ConsolidatorConfig.reflectionMaxQuestions} default (P1-1). */\n readonly reflectionMaxQuestions?: number;\n /** Override the {@link ConsolidatorConfig.reflectionMaxQuarantinedInsights} default (W-082). */\n readonly reflectionMaxQuarantinedInsights?: number;\n /** Override the per-tier {@link ConsolidatorConfig.contextualRetrieval} default (P1-3). */\n readonly contextualRetrieval?: ContextualRetrievalMode;\n /** Override the per-tier {@link ConsolidatorConfig.learnedContext} default (D3). */\n readonly learnedContext?: boolean;\n /** Override the {@link ConsolidatorConfig.learnedContextMaxChars} default (D3). */\n readonly learnedContextMaxChars?: number;\n /** Default scope used by event triggers + the manual `fireNow` path. */\n readonly defaultScope?: SessionScope;\n}\n\n/**\n * Tier preset table. The defaults follow ADR-038 §4 - `'free'`\n * disables every LLM phase and pins zero ceilings, the upper tiers\n * widen the budget envelope progressively.\n *\n * @stable\n */\nexport const CONSOLIDATOR_TIER_DEFAULTS: Readonly<\n Record<\n ConsolidatorTier,\n {\n readonly ceilings: ConsolidatorCeilings;\n readonly phases: ReadonlyArray<ConsolidatorPhase>;\n readonly cheapModel: string | null;\n readonly deepModel: string | null;\n readonly onExceed: OnBudgetExceed;\n /** W-081: input transcript budget for one standard-phase slice. */\n readonly maxTranscriptChars: number;\n readonly formEpisodes: boolean;\n readonly importanceScoring: boolean;\n readonly reflection: boolean;\n readonly importanceThreshold: number;\n readonly reflectionMaxQuestions: number;\n readonly reflectionMaxQuarantinedInsights: number;\n readonly contextualRetrieval: ContextualRetrievalMode;\n readonly learnedContext: boolean;\n readonly learnedContextMaxChars: number;\n }\n >\n> = Object.freeze({\n free: {\n ceilings: {\n maxTokensPerDay: 0,\n maxCostPerDay: 0,\n maxConcurrentRuns: 1,\n maxRunDurationMs: 5 * 60 * 1000,\n cooldownMs: 60_000,\n },\n phases: ['light'],\n cheapModel: null,\n deepModel: null,\n onExceed: 'pause',\n maxTranscriptChars: 60_000,\n formEpisodes: false,\n importanceScoring: false,\n reflection: false,\n importanceThreshold: 3,\n reflectionMaxQuestions: 3,\n reflectionMaxQuarantinedInsights: 100,\n contextualRetrieval: 'late-chunk',\n learnedContext: false,\n learnedContextMaxChars: 1200,\n },\n cheap: {\n ceilings: {\n maxTokensPerDay: 50_000,\n maxCostPerDay: 0.2,\n maxConcurrentRuns: 1,\n maxRunDurationMs: 5 * 60 * 1000,\n cooldownMs: 60_000,\n },\n phases: ['light', 'standard'],\n cheapModel: null,\n deepModel: null,\n onExceed: 'pause',\n maxTranscriptChars: 60_000,\n formEpisodes: false,\n importanceScoring: false,\n reflection: false,\n importanceThreshold: 3,\n reflectionMaxQuestions: 3,\n reflectionMaxQuarantinedInsights: 100,\n contextualRetrieval: 'late-chunk',\n learnedContext: false,\n learnedContextMaxChars: 1200,\n },\n standard: {\n ceilings: {\n maxTokensPerDay: 200_000,\n maxCostPerDay: 1.0,\n maxConcurrentRuns: 1,\n maxRunDurationMs: 10 * 60 * 1000,\n cooldownMs: 30_000,\n },\n phases: ['light', 'standard', 'deep'],\n cheapModel: null,\n deepModel: null,\n onExceed: 'log',\n maxTranscriptChars: 60_000,\n formEpisodes: true,\n importanceScoring: true,\n reflection: false,\n importanceThreshold: 3,\n reflectionMaxQuestions: 3,\n reflectionMaxQuarantinedInsights: 100,\n contextualRetrieval: 'late-chunk',\n learnedContext: false,\n learnedContextMaxChars: 1200,\n },\n full: {\n ceilings: {\n maxTokensPerDay: 1_000_000,\n maxCostPerDay: 5.0,\n maxConcurrentRuns: 2,\n maxRunDurationMs: 15 * 60 * 1000,\n cooldownMs: 10_000,\n },\n phases: ['light', 'standard', 'deep'],\n cheapModel: null,\n deepModel: null,\n onExceed: 'log',\n // Full-tier models carry wider contexts - a larger input budget\n // keeps slices coarse (fewer calls) without risking overflow.\n maxTranscriptChars: 120_000,\n formEpisodes: true,\n importanceScoring: true,\n reflection: true,\n importanceThreshold: 3,\n reflectionMaxQuestions: 3,\n reflectionMaxQuarantinedInsights: 100,\n contextualRetrieval: 'late-chunk',\n learnedContext: false,\n learnedContextMaxChars: 1200,\n },\n custom: {\n ceilings: {\n maxTokensPerDay: 0,\n maxCostPerDay: 0,\n maxConcurrentRuns: 1,\n maxRunDurationMs: 5 * 60 * 1000,\n cooldownMs: 60_000,\n },\n phases: ['light'],\n cheapModel: null,\n deepModel: null,\n onExceed: 'pause',\n maxTranscriptChars: 60_000,\n formEpisodes: false,\n importanceScoring: false,\n reflection: false,\n importanceThreshold: 3,\n reflectionMaxQuestions: 3,\n reflectionMaxQuarantinedInsights: 100,\n contextualRetrieval: 'late-chunk',\n learnedContext: false,\n learnedContextMaxChars: 1200,\n },\n});\n"],"mappings":";;;;;;;;AAieA,MAAaA,6BAsBT,OAAO,OAAO;CAChB,MAAM;EACJ,UAAU;GACR,iBAAiB;GACjB,eAAe;GACf,mBAAmB;GACnB,kBAAkB,MAAS;GAC3B,YAAY;GACb;EACD,QAAQ,CAAC,QAAQ;EACjB,YAAY;EACZ,WAAW;EACX,UAAU;EACV,oBAAoB;EACpB,cAAc;EACd,mBAAmB;EACnB,YAAY;EACZ,qBAAqB;EACrB,wBAAwB;EACxB,kCAAkC;EAClC,qBAAqB;EACrB,gBAAgB;EAChB,wBAAwB;EACzB;CACD,OAAO;EACL,UAAU;GACR,iBAAiB;GACjB,eAAe;GACf,mBAAmB;GACnB,kBAAkB,MAAS;GAC3B,YAAY;GACb;EACD,QAAQ,CAAC,SAAS,WAAW;EAC7B,YAAY;EACZ,WAAW;EACX,UAAU;EACV,oBAAoB;EACpB,cAAc;EACd,mBAAmB;EACnB,YAAY;EACZ,qBAAqB;EACrB,wBAAwB;EACxB,kCAAkC;EAClC,qBAAqB;EACrB,gBAAgB;EAChB,wBAAwB;EACzB;CACD,UAAU;EACR,UAAU;GACR,iBAAiB;GACjB,eAAe;GACf,mBAAmB;GACnB,kBAAkB,MAAU;GAC5B,YAAY;GACb;EACD,QAAQ;GAAC;GAAS;GAAY;GAAO;EACrC,YAAY;EACZ,WAAW;EACX,UAAU;EACV,oBAAoB;EACpB,cAAc;EACd,mBAAmB;EACnB,YAAY;EACZ,qBAAqB;EACrB,wBAAwB;EACxB,kCAAkC;EAClC,qBAAqB;EACrB,gBAAgB;EAChB,wBAAwB;EACzB;CACD,MAAM;EACJ,UAAU;GACR,iBAAiB;GACjB,eAAe;GACf,mBAAmB;GACnB,kBAAkB,MAAU;GAC5B,YAAY;GACb;EACD,QAAQ;GAAC;GAAS;GAAY;GAAO;EACrC,YAAY;EACZ,WAAW;EACX,UAAU;EAGV,oBAAoB;EACpB,cAAc;EACd,mBAAmB;EACnB,YAAY;EACZ,qBAAqB;EACrB,wBAAwB;EACxB,kCAAkC;EAClC,qBAAqB;EACrB,gBAAgB;EAChB,wBAAwB;EACzB;CACD,QAAQ;EACN,UAAU;GACR,iBAAiB;GACjB,eAAe;GACf,mBAAmB;GACnB,kBAAkB,MAAS;GAC3B,YAAY;GACb;EACD,QAAQ,CAAC,QAAQ;EACjB,YAAY;EACZ,WAAW;EACX,UAAU;EACV,oBAAoB;EACpB,cAAc;EACd,mBAAmB;EACnB,YAAY;EACZ,qBAAqB;EACrB,wBAAwB;EACxB,kCAAkC;EAClC,qBAAqB;EACrB,gBAAgB;EAChB,wBAAwB;EACzB;CACF,CAAC"}
|
|
@@ -7,7 +7,7 @@ import { Message } from "@graphorin/core";
|
|
|
7
7
|
declare const CLEARED_TOOL_RESULT_MARKER = "[cleared tool result";
|
|
8
8
|
/** Knobs for {@link clearOldToolResults} (mirrors the strategy variant). */
|
|
9
9
|
interface ClearToolResultsOptions {
|
|
10
|
-
/** Most-recent tool results kept verbatim (default
|
|
10
|
+
/** Most-recent tool results kept verbatim (default `DEFAULT_KEEP_TOOL_USES`). */
|
|
11
11
|
readonly keepToolUses?: number;
|
|
12
12
|
/** Only clear if at least this many tokens are reclaimable; else leave untouched (default 0). */
|
|
13
13
|
readonly clearAtLeast?: number;
|
|
@@ -114,5 +114,5 @@ async function clearOldToolResults(messages, options, counter) {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
//#endregion
|
|
117
|
-
export { CLEARED_TOOL_RESULT_MARKER,
|
|
117
|
+
export { CLEARED_TOOL_RESULT_MARKER, clearOldToolResults };
|
|
118
118
|
//# sourceMappingURL=clear-tool-results.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clear-tool-results.js","names":["toolIdx: number[]","replaced: ToolMessage","out: Message[]"],"sources":["../../../src/context-engine/compaction/clear-tool-results.ts"],"sourcesContent":["/**\n * SOTA-1: zero-LLM tool-result clearing - the cheapest pre-compaction tier.\n *\n * Mirrors Anthropic context editing (`clear_tool_uses_20250919`): when the\n * buffer crosses the compaction threshold, replace the oldest tool results with\n * a compact placeholder BEFORE paying for a summarizer LLM call. The most recent\n * `keepToolUses` results stay verbatim, `excludeTools` are never touched, and\n * clearing is skipped entirely when it would reclaim fewer than `clearAtLeast`\n * tokens (not worth the context churn). Pure + deterministic - no provider call.\n *\n * @packageDocumentation\n */\n\nimport type { Message, ToolMessage } from '@graphorin/core';\nimport {\n type ContextTokenCounter,\n countMessageTokens,\n renderMessageText,\n} from '../token-counter.js';\n\n/** Placeholder prefix on a cleared tool result - makes clearing idempotent. */\nexport const CLEARED_TOOL_RESULT_MARKER = '[cleared tool result';\n\n/** Default count of most-recent tool results kept verbatim. */\nexport const DEFAULT_KEEP_TOOL_USES = 3;\n\n/** Knobs for {@link clearOldToolResults} (mirrors the strategy variant). */\nexport interface ClearToolResultsOptions {\n /** Most-recent tool results kept verbatim (default {@link DEFAULT_KEEP_TOOL_USES}). */\n readonly keepToolUses?: number;\n /** Only clear if at least this many tokens are reclaimable; else leave untouched (default 0). */\n readonly clearAtLeast?: number;\n /** Tool names whose results are never cleared. */\n readonly excludeTools?: ReadonlyArray<string>;\n /** Placeholder builder; defaults to a one-line `[cleared tool result …]` marker. */\n readonly placeholder?: (info: {\n readonly toolCallId: string;\n readonly toolName?: string;\n readonly clearedTokens: number;\n }) => string;\n /**\n * A6 / SOTA-2 - recoverable clearing. When provided, the original tool-result\n * text of each cleared message is handed to this callback (wire it to a spill\n * store / the `read_result` handle registry) and the placeholder references the\n * returned handle id + preview, so the model can re-fetch the full result via\n * `read_result` rather than losing it. Invoked only for clears that actually\n * commit (after the `clearAtLeast` floor), so a rejected clearing never spills.\n * Omitted ⇒ the bare `placeholder` (irrecoverable, byte-identical default).\n */\n readonly externalize?: (\n content: string,\n info: {\n readonly toolCallId: string;\n readonly toolName?: string;\n readonly clearedTokens: number;\n },\n ) => Promise<{ readonly handleId: string; readonly preview?: string }>;\n /**\n * C4 (context-engine-11): the tool the externalized-handle placeholder\n * advertises. The memory package cannot know whether the agent\n * registered `read_result` (that depends on spill wiring), so callers\n * whose runtime does NOT expose it pass `null` and the placeholder\n * degrades to a tool-neutral phrasing instead of promising a tool the\n * model cannot call. Default `'read_result'` (the agent built-in).\n */\n readonly readResultToolName?: string | null;\n /**\n * C4 (clear_tool_uses_20250919 parity): additionally blank the PAIRED\n * assistant message's tool-call arguments for every cleared result,\n * reclaiming the input side of verbose calls too. Default `false`.\n */\n readonly clearToolInputs?: boolean;\n}\n\n/** Result of a clearing pass. `clearedIndices` empty ⇒ nothing changed. */\nexport interface ClearToolResultsOutcome {\n readonly messages: ReadonlyArray<Message>;\n readonly clearedIndices: ReadonlyArray<number>;\n readonly reclaimedTokens: number;\n}\n\nfunction defaultPlaceholder(info: { toolName?: string; clearedTokens: number }): string {\n return `${CLEARED_TOOL_RESULT_MARKER} · ${info.toolName ?? 'tool'} · ${info.clearedTokens} tokens elided · re-run the tool if needed]`;\n}\n\n/** A6: a placeholder that points at the externalized handle (recoverable via read_result). */\nfunction handlePlaceholder(info: {\n toolName?: string;\n clearedTokens: number;\n handleId: string;\n preview?: string;\n readToolName?: string | null;\n}): string {\n const preview = info.preview !== undefined && info.preview.length > 0 ? ` · ${info.preview}` : '';\n // C4 (context-engine-11): only advertise a retrieval tool the caller\n // vouches exists; `null` degrades to a tool-neutral phrasing.\n const retrieval =\n info.readToolName === null\n ? `full result externalized to handle: ${info.handleId}`\n : `full result via ${info.readToolName ?? 'read_result'} handle: ${info.handleId}`;\n return `${CLEARED_TOOL_RESULT_MARKER} · ${info.toolName ?? 'tool'} · ${info.clearedTokens} tokens · ${retrieval}${preview}]`;\n}\n\nfunction isAlreadyCleared(content: Message['content']): boolean {\n return typeof content === 'string' && content.startsWith(CLEARED_TOOL_RESULT_MARKER);\n}\n\n/**\n * Replace the oldest clearable tool results with placeholders. Returns the new\n * buffer (same length - content is replaced in place, never removed) plus the\n * cleared indices and reclaimed token count. Idempotent: already-cleared\n * placeholders are skipped on a second pass.\n */\nexport async function clearOldToolResults(\n messages: ReadonlyArray<Message>,\n options: ClearToolResultsOptions,\n counter: ContextTokenCounter,\n): Promise<ClearToolResultsOutcome> {\n const keep = options.keepToolUses ?? DEFAULT_KEEP_TOOL_USES;\n const exclude = new Set(options.excludeTools ?? []);\n const placeholder = options.placeholder ?? defaultPlaceholder;\n\n // toolCallId → toolName from every assistant tool-call in the buffer.\n const toolNameById = new Map<string, string>();\n for (const m of messages) {\n if (m.role === 'assistant' && m.toolCalls) {\n for (const tc of m.toolCalls) toolNameById.set(tc.toolCallId, tc.toolName);\n }\n }\n\n // Indices of all not-yet-cleared tool-result messages, oldest first.\n const toolIdx: number[] = [];\n for (let i = 0; i < messages.length; i++) {\n const m = messages[i];\n if (m?.role === 'tool' && !isAlreadyCleared(m.content)) toolIdx.push(i);\n }\n\n // Keep the most-recent `keep`; older ones are candidates unless excluded.\n const keptCutoff = toolIdx.length - Math.max(0, keep);\n const replacements = new Map<number, ToolMessage>();\n const clearedTokensByIdx = new Map<number, number>();\n let reclaimable = 0;\n for (let j = 0; j < toolIdx.length && j < keptCutoff; j++) {\n const idx = toolIdx[j];\n if (idx === undefined) continue;\n const tm = messages[idx] as ToolMessage;\n const name = toolNameById.get(tm.toolCallId);\n if (name !== undefined && exclude.has(name)) continue;\n const originalTokens = await countMessageTokens([tm], counter);\n const replaced: ToolMessage = {\n role: 'tool',\n toolCallId: tm.toolCallId,\n content: placeholder({\n toolCallId: tm.toolCallId,\n ...(name !== undefined ? { toolName: name } : {}),\n clearedTokens: originalTokens,\n }),\n };\n const placeholderTokens = await countMessageTokens([replaced], counter);\n reclaimable += Math.max(0, originalTokens - placeholderTokens);\n replacements.set(idx, replaced);\n clearedTokensByIdx.set(idx, originalTokens);\n }\n\n const clearAtLeast = options.clearAtLeast ?? 0;\n if (replacements.size === 0 || reclaimable < clearAtLeast) {\n return { messages, clearedIndices: [], reclaimedTokens: 0 };\n }\n\n // A6: only now that the clearing commits, externalize each cleared result to a\n // recoverable handle and rewrite its placeholder to reference it. Skipped\n // clears (above) never reach here, so the spill never fires for nothing.\n const externalize = options.externalize;\n if (externalize !== undefined) {\n for (const idx of replacements.keys()) {\n const tm = messages[idx] as ToolMessage;\n const name = toolNameById.get(tm.toolCallId);\n const clearedTokens = clearedTokensByIdx.get(idx) ?? 0;\n const content = typeof tm.content === 'string' ? tm.content : renderMessageText(tm);\n const handle = await externalize(content, {\n toolCallId: tm.toolCallId,\n ...(name !== undefined ? { toolName: name } : {}),\n clearedTokens,\n });\n replacements.set(idx, {\n role: 'tool',\n toolCallId: tm.toolCallId,\n content: handlePlaceholder({\n ...(name !== undefined ? { toolName: name } : {}),\n clearedTokens,\n handleId: handle.handleId,\n ...(handle.preview !== undefined ? { preview: handle.preview } : {}),\n ...(options.readResultToolName !== undefined\n ? { readToolName: options.readResultToolName }\n : {}),\n }),\n });\n }\n }\n\n let out: Message[] = messages.map((m, i) => replacements.get(i) ?? m);\n\n // C4 (clear_tool_uses parity): blank the paired assistant tool-call\n // ARGUMENTS for every cleared result. The call's name and id survive\n // (the transcript stays well-formed); only the argument payload is\n // replaced with a marker object.\n if (options.clearToolInputs === true && replacements.size > 0) {\n const clearedCallIds = new Set<string>();\n for (const idx of replacements.keys()) {\n const tm = messages[idx] as ToolMessage;\n clearedCallIds.add(tm.toolCallId);\n }\n out = out.map((m) => {\n if (m.role !== 'assistant' || m.toolCalls === undefined) return m;\n if (!m.toolCalls.some((tc) => clearedCallIds.has(tc.toolCallId))) return m;\n return {\n ...m,\n toolCalls: m.toolCalls.map((tc) =>\n clearedCallIds.has(tc.toolCallId)\n ? { ...tc, args: { cleared: '[tool input elided by context clearing]' } }\n : tc,\n ),\n };\n });\n }\n\n return {\n messages: out,\n clearedIndices: [...replacements.keys()],\n reclaimedTokens: reclaimable,\n };\n}\n"],"mappings":";;;;AAqBA,MAAa,6BAA6B;;AAG1C,MAAa,yBAAyB;AAyDtC,SAAS,mBAAmB,MAA4D;AACtF,QAAO,GAAG,2BAA2B,KAAK,KAAK,YAAY,OAAO,KAAK,KAAK,cAAc;;;AAI5F,SAAS,kBAAkB,MAMhB;CACT,MAAM,UAAU,KAAK,YAAY,UAAa,KAAK,QAAQ,SAAS,IAAI,MAAM,KAAK,YAAY;CAG/F,MAAM,YACJ,KAAK,iBAAiB,OAClB,uCAAuC,KAAK,aAC5C,mBAAmB,KAAK,gBAAgB,cAAc,WAAW,KAAK;AAC5E,QAAO,GAAG,2BAA2B,KAAK,KAAK,YAAY,OAAO,KAAK,KAAK,cAAc,YAAY,YAAY,QAAQ;;AAG5H,SAAS,iBAAiB,SAAsC;AAC9D,QAAO,OAAO,YAAY,YAAY,QAAQ,WAAW,2BAA2B;;;;;;;;AAStF,eAAsB,oBACpB,UACA,SACA,SACkC;CAClC,MAAM,OAAO,QAAQ,gBAAgB;CACrC,MAAM,UAAU,IAAI,IAAI,QAAQ,gBAAgB,EAAE,CAAC;CACnD,MAAM,cAAc,QAAQ,eAAe;CAG3C,MAAM,+BAAe,IAAI,KAAqB;AAC9C,MAAK,MAAM,KAAK,SACd,KAAI,EAAE,SAAS,eAAe,EAAE,UAC9B,MAAK,MAAM,MAAM,EAAE,UAAW,cAAa,IAAI,GAAG,YAAY,GAAG,SAAS;CAK9E,MAAMA,UAAoB,EAAE;AAC5B,MAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,IAAI,SAAS;AACnB,MAAI,GAAG,SAAS,UAAU,CAAC,iBAAiB,EAAE,QAAQ,CAAE,SAAQ,KAAK,EAAE;;CAIzE,MAAM,aAAa,QAAQ,SAAS,KAAK,IAAI,GAAG,KAAK;CACrD,MAAM,+BAAe,IAAI,KAA0B;CACnD,MAAM,qCAAqB,IAAI,KAAqB;CACpD,IAAI,cAAc;AAClB,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,UAAU,IAAI,YAAY,KAAK;EACzD,MAAM,MAAM,QAAQ;AACpB,MAAI,QAAQ,OAAW;EACvB,MAAM,KAAK,SAAS;EACpB,MAAM,OAAO,aAAa,IAAI,GAAG,WAAW;AAC5C,MAAI,SAAS,UAAa,QAAQ,IAAI,KAAK,CAAE;EAC7C,MAAM,iBAAiB,MAAM,mBAAmB,CAAC,GAAG,EAAE,QAAQ;EAC9D,MAAMC,WAAwB;GAC5B,MAAM;GACN,YAAY,GAAG;GACf,SAAS,YAAY;IACnB,YAAY,GAAG;IACf,GAAI,SAAS,SAAY,EAAE,UAAU,MAAM,GAAG,EAAE;IAChD,eAAe;IAChB,CAAC;GACH;EACD,MAAM,oBAAoB,MAAM,mBAAmB,CAAC,SAAS,EAAE,QAAQ;AACvE,iBAAe,KAAK,IAAI,GAAG,iBAAiB,kBAAkB;AAC9D,eAAa,IAAI,KAAK,SAAS;AAC/B,qBAAmB,IAAI,KAAK,eAAe;;CAG7C,MAAM,eAAe,QAAQ,gBAAgB;AAC7C,KAAI,aAAa,SAAS,KAAK,cAAc,aAC3C,QAAO;EAAE;EAAU,gBAAgB,EAAE;EAAE,iBAAiB;EAAG;CAM7D,MAAM,cAAc,QAAQ;AAC5B,KAAI,gBAAgB,OAClB,MAAK,MAAM,OAAO,aAAa,MAAM,EAAE;EACrC,MAAM,KAAK,SAAS;EACpB,MAAM,OAAO,aAAa,IAAI,GAAG,WAAW;EAC5C,MAAM,gBAAgB,mBAAmB,IAAI,IAAI,IAAI;EAErD,MAAM,SAAS,MAAM,YADL,OAAO,GAAG,YAAY,WAAW,GAAG,UAAU,kBAAkB,GAAG,EACzC;GACxC,YAAY,GAAG;GACf,GAAI,SAAS,SAAY,EAAE,UAAU,MAAM,GAAG,EAAE;GAChD;GACD,CAAC;AACF,eAAa,IAAI,KAAK;GACpB,MAAM;GACN,YAAY,GAAG;GACf,SAAS,kBAAkB;IACzB,GAAI,SAAS,SAAY,EAAE,UAAU,MAAM,GAAG,EAAE;IAChD;IACA,UAAU,OAAO;IACjB,GAAI,OAAO,YAAY,SAAY,EAAE,SAAS,OAAO,SAAS,GAAG,EAAE;IACnE,GAAI,QAAQ,uBAAuB,SAC/B,EAAE,cAAc,QAAQ,oBAAoB,GAC5C,EAAE;IACP,CAAC;GACH,CAAC;;CAIN,IAAIC,MAAiB,SAAS,KAAK,GAAG,MAAM,aAAa,IAAI,EAAE,IAAI,EAAE;AAMrE,KAAI,QAAQ,oBAAoB,QAAQ,aAAa,OAAO,GAAG;EAC7D,MAAM,iCAAiB,IAAI,KAAa;AACxC,OAAK,MAAM,OAAO,aAAa,MAAM,EAAE;GACrC,MAAM,KAAK,SAAS;AACpB,kBAAe,IAAI,GAAG,WAAW;;AAEnC,QAAM,IAAI,KAAK,MAAM;AACnB,OAAI,EAAE,SAAS,eAAe,EAAE,cAAc,OAAW,QAAO;AAChE,OAAI,CAAC,EAAE,UAAU,MAAM,OAAO,eAAe,IAAI,GAAG,WAAW,CAAC,CAAE,QAAO;AACzE,UAAO;IACL,GAAG;IACH,WAAW,EAAE,UAAU,KAAK,OAC1B,eAAe,IAAI,GAAG,WAAW,GAC7B;KAAE,GAAG;KAAI,MAAM,EAAE,SAAS,2CAA2C;KAAE,GACvE,GACL;IACF;IACD;;AAGJ,QAAO;EACL,UAAU;EACV,gBAAgB,CAAC,GAAG,aAAa,MAAM,CAAC;EACxC,iBAAiB;EAClB"}
|
|
1
|
+
{"version":3,"file":"clear-tool-results.js","names":["toolIdx: number[]","replaced: ToolMessage","out: Message[]"],"sources":["../../../src/context-engine/compaction/clear-tool-results.ts"],"sourcesContent":["/**\n * SOTA-1: zero-LLM tool-result clearing - the cheapest pre-compaction tier.\n *\n * Mirrors Anthropic context editing (`clear_tool_uses_20250919`): when the\n * buffer crosses the compaction threshold, replace the oldest tool results with\n * a compact placeholder BEFORE paying for a summarizer LLM call. The most recent\n * `keepToolUses` results stay verbatim, `excludeTools` are never touched, and\n * clearing is skipped entirely when it would reclaim fewer than `clearAtLeast`\n * tokens (not worth the context churn). Pure + deterministic - no provider call.\n *\n * @packageDocumentation\n */\n\nimport type { Message, ToolMessage } from '@graphorin/core';\nimport {\n type ContextTokenCounter,\n countMessageTokens,\n renderMessageText,\n} from '../token-counter.js';\n\n/** Placeholder prefix on a cleared tool result - makes clearing idempotent. */\nexport const CLEARED_TOOL_RESULT_MARKER = '[cleared tool result';\n\n/** Default count of most-recent tool results kept verbatim. */\nexport const DEFAULT_KEEP_TOOL_USES = 3;\n\n/** Knobs for {@link clearOldToolResults} (mirrors the strategy variant). */\nexport interface ClearToolResultsOptions {\n /** Most-recent tool results kept verbatim (default `DEFAULT_KEEP_TOOL_USES`). */\n readonly keepToolUses?: number;\n /** Only clear if at least this many tokens are reclaimable; else leave untouched (default 0). */\n readonly clearAtLeast?: number;\n /** Tool names whose results are never cleared. */\n readonly excludeTools?: ReadonlyArray<string>;\n /** Placeholder builder; defaults to a one-line `[cleared tool result …]` marker. */\n readonly placeholder?: (info: {\n readonly toolCallId: string;\n readonly toolName?: string;\n readonly clearedTokens: number;\n }) => string;\n /**\n * A6 / SOTA-2 - recoverable clearing. When provided, the original tool-result\n * text of each cleared message is handed to this callback (wire it to a spill\n * store / the `read_result` handle registry) and the placeholder references the\n * returned handle id + preview, so the model can re-fetch the full result via\n * `read_result` rather than losing it. Invoked only for clears that actually\n * commit (after the `clearAtLeast` floor), so a rejected clearing never spills.\n * Omitted ⇒ the bare `placeholder` (irrecoverable, byte-identical default).\n */\n readonly externalize?: (\n content: string,\n info: {\n readonly toolCallId: string;\n readonly toolName?: string;\n readonly clearedTokens: number;\n },\n ) => Promise<{ readonly handleId: string; readonly preview?: string }>;\n /**\n * C4 (context-engine-11): the tool the externalized-handle placeholder\n * advertises. The memory package cannot know whether the agent\n * registered `read_result` (that depends on spill wiring), so callers\n * whose runtime does NOT expose it pass `null` and the placeholder\n * degrades to a tool-neutral phrasing instead of promising a tool the\n * model cannot call. Default `'read_result'` (the agent built-in).\n */\n readonly readResultToolName?: string | null;\n /**\n * C4 (clear_tool_uses_20250919 parity): additionally blank the PAIRED\n * assistant message's tool-call arguments for every cleared result,\n * reclaiming the input side of verbose calls too. Default `false`.\n */\n readonly clearToolInputs?: boolean;\n}\n\n/** Result of a clearing pass. `clearedIndices` empty ⇒ nothing changed. */\nexport interface ClearToolResultsOutcome {\n readonly messages: ReadonlyArray<Message>;\n readonly clearedIndices: ReadonlyArray<number>;\n readonly reclaimedTokens: number;\n}\n\nfunction defaultPlaceholder(info: { toolName?: string; clearedTokens: number }): string {\n return `${CLEARED_TOOL_RESULT_MARKER} · ${info.toolName ?? 'tool'} · ${info.clearedTokens} tokens elided · re-run the tool if needed]`;\n}\n\n/** A6: a placeholder that points at the externalized handle (recoverable via read_result). */\nfunction handlePlaceholder(info: {\n toolName?: string;\n clearedTokens: number;\n handleId: string;\n preview?: string;\n readToolName?: string | null;\n}): string {\n const preview = info.preview !== undefined && info.preview.length > 0 ? ` · ${info.preview}` : '';\n // C4 (context-engine-11): only advertise a retrieval tool the caller\n // vouches exists; `null` degrades to a tool-neutral phrasing.\n const retrieval =\n info.readToolName === null\n ? `full result externalized to handle: ${info.handleId}`\n : `full result via ${info.readToolName ?? 'read_result'} handle: ${info.handleId}`;\n return `${CLEARED_TOOL_RESULT_MARKER} · ${info.toolName ?? 'tool'} · ${info.clearedTokens} tokens · ${retrieval}${preview}]`;\n}\n\nfunction isAlreadyCleared(content: Message['content']): boolean {\n return typeof content === 'string' && content.startsWith(CLEARED_TOOL_RESULT_MARKER);\n}\n\n/**\n * Replace the oldest clearable tool results with placeholders. Returns the new\n * buffer (same length - content is replaced in place, never removed) plus the\n * cleared indices and reclaimed token count. Idempotent: already-cleared\n * placeholders are skipped on a second pass.\n */\nexport async function clearOldToolResults(\n messages: ReadonlyArray<Message>,\n options: ClearToolResultsOptions,\n counter: ContextTokenCounter,\n): Promise<ClearToolResultsOutcome> {\n const keep = options.keepToolUses ?? DEFAULT_KEEP_TOOL_USES;\n const exclude = new Set(options.excludeTools ?? []);\n const placeholder = options.placeholder ?? defaultPlaceholder;\n\n // toolCallId → toolName from every assistant tool-call in the buffer.\n const toolNameById = new Map<string, string>();\n for (const m of messages) {\n if (m.role === 'assistant' && m.toolCalls) {\n for (const tc of m.toolCalls) toolNameById.set(tc.toolCallId, tc.toolName);\n }\n }\n\n // Indices of all not-yet-cleared tool-result messages, oldest first.\n const toolIdx: number[] = [];\n for (let i = 0; i < messages.length; i++) {\n const m = messages[i];\n if (m?.role === 'tool' && !isAlreadyCleared(m.content)) toolIdx.push(i);\n }\n\n // Keep the most-recent `keep`; older ones are candidates unless excluded.\n const keptCutoff = toolIdx.length - Math.max(0, keep);\n const replacements = new Map<number, ToolMessage>();\n const clearedTokensByIdx = new Map<number, number>();\n let reclaimable = 0;\n for (let j = 0; j < toolIdx.length && j < keptCutoff; j++) {\n const idx = toolIdx[j];\n if (idx === undefined) continue;\n const tm = messages[idx] as ToolMessage;\n const name = toolNameById.get(tm.toolCallId);\n if (name !== undefined && exclude.has(name)) continue;\n const originalTokens = await countMessageTokens([tm], counter);\n const replaced: ToolMessage = {\n role: 'tool',\n toolCallId: tm.toolCallId,\n content: placeholder({\n toolCallId: tm.toolCallId,\n ...(name !== undefined ? { toolName: name } : {}),\n clearedTokens: originalTokens,\n }),\n };\n const placeholderTokens = await countMessageTokens([replaced], counter);\n reclaimable += Math.max(0, originalTokens - placeholderTokens);\n replacements.set(idx, replaced);\n clearedTokensByIdx.set(idx, originalTokens);\n }\n\n const clearAtLeast = options.clearAtLeast ?? 0;\n if (replacements.size === 0 || reclaimable < clearAtLeast) {\n return { messages, clearedIndices: [], reclaimedTokens: 0 };\n }\n\n // A6: only now that the clearing commits, externalize each cleared result to a\n // recoverable handle and rewrite its placeholder to reference it. Skipped\n // clears (above) never reach here, so the spill never fires for nothing.\n const externalize = options.externalize;\n if (externalize !== undefined) {\n for (const idx of replacements.keys()) {\n const tm = messages[idx] as ToolMessage;\n const name = toolNameById.get(tm.toolCallId);\n const clearedTokens = clearedTokensByIdx.get(idx) ?? 0;\n const content = typeof tm.content === 'string' ? tm.content : renderMessageText(tm);\n const handle = await externalize(content, {\n toolCallId: tm.toolCallId,\n ...(name !== undefined ? { toolName: name } : {}),\n clearedTokens,\n });\n replacements.set(idx, {\n role: 'tool',\n toolCallId: tm.toolCallId,\n content: handlePlaceholder({\n ...(name !== undefined ? { toolName: name } : {}),\n clearedTokens,\n handleId: handle.handleId,\n ...(handle.preview !== undefined ? { preview: handle.preview } : {}),\n ...(options.readResultToolName !== undefined\n ? { readToolName: options.readResultToolName }\n : {}),\n }),\n });\n }\n }\n\n let out: Message[] = messages.map((m, i) => replacements.get(i) ?? m);\n\n // C4 (clear_tool_uses parity): blank the paired assistant tool-call\n // ARGUMENTS for every cleared result. The call's name and id survive\n // (the transcript stays well-formed); only the argument payload is\n // replaced with a marker object.\n if (options.clearToolInputs === true && replacements.size > 0) {\n const clearedCallIds = new Set<string>();\n for (const idx of replacements.keys()) {\n const tm = messages[idx] as ToolMessage;\n clearedCallIds.add(tm.toolCallId);\n }\n out = out.map((m) => {\n if (m.role !== 'assistant' || m.toolCalls === undefined) return m;\n if (!m.toolCalls.some((tc) => clearedCallIds.has(tc.toolCallId))) return m;\n return {\n ...m,\n toolCalls: m.toolCalls.map((tc) =>\n clearedCallIds.has(tc.toolCallId)\n ? { ...tc, args: { cleared: '[tool input elided by context clearing]' } }\n : tc,\n ),\n };\n });\n }\n\n return {\n messages: out,\n clearedIndices: [...replacements.keys()],\n reclaimedTokens: reclaimable,\n };\n}\n"],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ import { ContextTokenCounter } from "../token-counter.js";
|
|
|
3
3
|
import { CompactionResult, CompactionSource, CompactionStrategy, CompactionSummarizer } from "./types.js";
|
|
4
4
|
import * as _graphorin_core2 from "@graphorin/core";
|
|
5
5
|
import { Message } from "@graphorin/core";
|
|
6
|
+
import "@graphorin/observability/redaction";
|
|
6
7
|
|
|
7
8
|
//#region src/context-engine/compaction/compactor.d.ts
|
|
8
9
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compactor.d.ts","names":[],"sources":["../../../src/context-engine/compaction/compactor.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"compactor.d.ts","names":[],"sources":["../../../src/context-engine/compaction/compactor.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;cAqDa,6BAAA;;;;;;;;;;UA+DI,sBAAA;qBACI,cAAc;mBAChB;qBACE;uBACE;uBACA;0BACG;;;;;kBAAmB,gBAAA,CAKD;2BAAY,gBAAA,CACH;;oBAEjC;;;;;;;;;;iBAWE,iBAAA,QAAyB,yBAAyB,QAAQ"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { UNTRUSTED_CONTENT_OPEN_PREFIX, wrapUntrusted } from "../../internal/envelope.js";
|
|
1
2
|
import { HEURISTIC_TOKEN_COUNTER, countMessageTokens, renderMessageText } from "../token-counter.js";
|
|
2
3
|
import { clearOldToolResults } from "./clear-tool-results.js";
|
|
3
4
|
import { SUMMARY_TEMPLATE_NAME, SUMMARY_TEMPLATE_VERSION, buildSummarizerPrompt, renderFinalSummary } from "./templates/summary-9-section.js";
|
|
@@ -12,16 +13,32 @@ import { scanImperativePatterns } from "@graphorin/observability/redaction";
|
|
|
12
13
|
*/
|
|
13
14
|
const DEFAULT_PRESERVE_RECENT_TURNS = 6;
|
|
14
15
|
/** Opening marker of the inbound-untrusted envelope (tools sanitize layer). */
|
|
15
|
-
const UNTRUSTED_MARKER =
|
|
16
|
+
const UNTRUSTED_MARKER = UNTRUSTED_CONTENT_OPEN_PREFIX;
|
|
16
17
|
/**
|
|
17
18
|
* Wall-clock budget for the CE-15 injection scan of the summarizer
|
|
18
19
|
* output. The scanner's 5ms default exists for the per-tool-result
|
|
19
|
-
* hot path;
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* hot path; a compaction already paid for an LLM call, the summary
|
|
21
|
+
* body is a single bounded text, and the pattern catalogue is fixed,
|
|
22
|
+
* so the scan here runs UNBUDGETED - a wall-clock budget only turned
|
|
23
|
+
* scheduler noise into a `null` verdict (a 50ms budget was observed
|
|
24
|
+
* expiring on a degraded CI runner, silently failing the check open).
|
|
23
25
|
*/
|
|
24
|
-
const COMPACTION_SCAN_BUDGET_MS =
|
|
26
|
+
const COMPACTION_SCAN_BUDGET_MS = Number.POSITIVE_INFINITY;
|
|
27
|
+
/**
|
|
28
|
+
* CE-15 trust decision for a freshly produced summary. Exported for
|
|
29
|
+
* unit tests because the `scan === null` branch (scanner budget
|
|
30
|
+
* exceeded) MUST fail closed: the scanner's contract is "null = the
|
|
31
|
+
* caller applies its own best-effort degradation", and treating null
|
|
32
|
+
* as "no hits" would commit a poisoned summary as trusted exactly on
|
|
33
|
+
* the contended hosts where the budget expires.
|
|
34
|
+
*
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
function resolveSummaryTrust(windowUntrusted, scan) {
|
|
38
|
+
if (windowUntrusted) return "untrusted-derived";
|
|
39
|
+
if (scan === null) return "untrusted-derived";
|
|
40
|
+
return scan.hits.length > 0 ? "untrusted-derived" : "trusted";
|
|
41
|
+
}
|
|
25
42
|
/** CE-15: does the compacted window carry inbound-untrusted envelopes? */
|
|
26
43
|
function windowContainsUntrusted(messages) {
|
|
27
44
|
return messages.some((message) => renderMessageText(message).includes(UNTRUSTED_MARKER));
|
|
@@ -31,9 +48,16 @@ function windowContainsUntrusted(messages) {
|
|
|
31
48
|
* envelope. Envelope marker sequences inside the body are neutralized
|
|
32
49
|
* first so summarizer output influenced by injected text cannot break
|
|
33
50
|
* out of the envelope and masquerade as authoritative system text.
|
|
51
|
+
* Delegates to the shared `internal/envelope.ts` helper (W-083) so the
|
|
52
|
+
* whole memory package neutralizes markers with one scheme; the
|
|
53
|
+
* output is byte-identical to the historical inline implementation on
|
|
54
|
+
* literal-marker inputs.
|
|
34
55
|
*/
|
|
35
56
|
function wrapSummaryAsDerived(body) {
|
|
36
|
-
return
|
|
57
|
+
return wrapUntrusted(body, {
|
|
58
|
+
trust: "derived",
|
|
59
|
+
tool: "compaction-summarizer"
|
|
60
|
+
});
|
|
37
61
|
}
|
|
38
62
|
/**
|
|
39
63
|
* Perform a compaction call. Returns the result envelope containing
|
|
@@ -119,7 +143,7 @@ async function executeCompaction(input) {
|
|
|
119
143
|
preserveRecentTurns
|
|
120
144
|
};
|
|
121
145
|
const summaryScan = scanImperativePatterns(summarized.text, void 0, COMPACTION_SCAN_BUDGET_MS);
|
|
122
|
-
const summaryTrust = windowContainsUntrusted(olderMessages)
|
|
146
|
+
const summaryTrust = resolveSummaryTrust(windowContainsUntrusted(olderMessages), summaryScan);
|
|
123
147
|
const finalSummary = renderFinalSummary({
|
|
124
148
|
template,
|
|
125
149
|
summaryFromLlm: summaryTrust === "untrusted-derived" ? wrapSummaryAsDerived(summarized.text) : summarized.text,
|