@graphorin/memory 0.6.1 → 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 +71 -0
- package/README.md +1 -1
- 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 +16 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -5
- 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 +1 -1
- package/dist/package.js.map +1 -1
- 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
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge helpers - wire a {@link Consolidator} into the
|
|
3
|
+
* `@graphorin/triggers` Scheduler so trigger declarations fire on
|
|
4
|
+
* the documented schedule (DEC-150 - "same code path lib + server").
|
|
5
|
+
*
|
|
6
|
+
* The bridge is structural: any object that exposes the same shape
|
|
7
|
+
* as `@graphorin/triggers`'s `Scheduler` (a `register(declaration)`
|
|
8
|
+
* method) works. The structural import keeps `@graphorin/memory`
|
|
9
|
+
* dependency-free at the type level - the workspace consumer
|
|
10
|
+
* supplies the actual scheduler at runtime.
|
|
11
|
+
*
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { SessionScope } from '@graphorin/core';
|
|
16
|
+
import { type ParsedTrigger, parseTriggerSpec, reasonFromTrigger } from './triggers.js';
|
|
17
|
+
import type { ConsolidatorTriggerReason, ConsolidatorTriggerSpec, PhaseOutcome } from './types.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Catch-up policy applied when a trigger missed one or more fires
|
|
21
|
+
* while the scheduler was offline. Mirrors
|
|
22
|
+
* `@graphorin/triggers`'s `CatchupPolicy` - duplicated here so the
|
|
23
|
+
* memory package stays import-free at the type level.
|
|
24
|
+
*
|
|
25
|
+
* @stable
|
|
26
|
+
*/
|
|
27
|
+
export type ConsolidatorCatchupPolicy = 'none' | 'last' | 'all';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Subset of the `Consolidator` runtime surface the bridge needs.
|
|
31
|
+
* Defined structurally - mirroring {@link SchedulerLike} - so this
|
|
32
|
+
* module never imports `./runtime.js` (which imports the bridge) and
|
|
33
|
+
* the consolidator module graph stays acyclic (issue #22). The full
|
|
34
|
+
* `Consolidator` is assignable as-is.
|
|
35
|
+
*
|
|
36
|
+
* @stable
|
|
37
|
+
*/
|
|
38
|
+
export interface ConsolidatorLike {
|
|
39
|
+
trigger(reason: ConsolidatorTriggerReason, scope: SessionScope): Promise<PhaseOutcome | null>;
|
|
40
|
+
config(): { readonly triggers: ReadonlyArray<ConsolidatorTriggerSpec> };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Subset of the `@graphorin/triggers` Scheduler surface the bridge
|
|
45
|
+
* needs. Defined structurally so consumers can inject mocks in
|
|
46
|
+
* tests without taking the package dependency.
|
|
47
|
+
*
|
|
48
|
+
* @stable
|
|
49
|
+
*/
|
|
50
|
+
export interface SchedulerLike {
|
|
51
|
+
register(declaration: TriggerDeclarationLike): Promise<unknown>;
|
|
52
|
+
unregister?(id: string): Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Subset of `@graphorin/triggers`'s `TriggerDeclaration` accepted
|
|
57
|
+
* by {@link SchedulerLike.register}. Field names mirror the
|
|
58
|
+
* upstream shape exactly so the structural type is interchangeable
|
|
59
|
+
* with the real export.
|
|
60
|
+
*
|
|
61
|
+
* @stable
|
|
62
|
+
*/
|
|
63
|
+
export interface TriggerDeclarationLike {
|
|
64
|
+
readonly id: string;
|
|
65
|
+
readonly kind: 'cron' | 'interval' | 'idle' | 'event';
|
|
66
|
+
readonly spec: string;
|
|
67
|
+
readonly callback: (payload?: unknown) => void | Promise<void>;
|
|
68
|
+
readonly options: {
|
|
69
|
+
readonly catchupPolicy?: ConsolidatorCatchupPolicy;
|
|
70
|
+
readonly maxCatchupRuns?: number;
|
|
71
|
+
readonly catchupWindowMs?: number;
|
|
72
|
+
readonly tags?: ReadonlyArray<string>;
|
|
73
|
+
readonly acknowledgeLibMode?: boolean;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Options accepted by {@link registerConsolidatorTriggers}. */
|
|
78
|
+
export interface RegisterTriggersOptions {
|
|
79
|
+
/**
|
|
80
|
+
* Scope passed to `consolidator.trigger(...)` for every fire. The
|
|
81
|
+
* consolidator only operates on a single user/session at a time
|
|
82
|
+
* (DEC-005 single-user-per-process); multi-tenant deployments
|
|
83
|
+
* register one scheduler-trigger pair per scope.
|
|
84
|
+
*/
|
|
85
|
+
readonly scope: SessionScope;
|
|
86
|
+
/**
|
|
87
|
+
* Catch-up policy applied to every registered trigger. Defaults
|
|
88
|
+
* to `'none'` per DEC-150 - safest for personal-assistant
|
|
89
|
+
* scenarios.
|
|
90
|
+
*/
|
|
91
|
+
readonly catchupPolicy?: ConsolidatorCatchupPolicy;
|
|
92
|
+
/** Suppress the per-process library-mode WARN. Defaults to `true`. */
|
|
93
|
+
readonly acknowledgeLibMode?: boolean;
|
|
94
|
+
/** Optional id prefix - useful when multiple scopes share a scheduler. */
|
|
95
|
+
readonly idPrefix?: string;
|
|
96
|
+
/** Optional tags forwarded to the scheduler. */
|
|
97
|
+
readonly tags?: ReadonlyArray<string>;
|
|
98
|
+
/**
|
|
99
|
+
* Filter - only register the supplied subset of triggers. By
|
|
100
|
+
* default every parseable trigger declared on the consolidator is
|
|
101
|
+
* registered (turn / event triggers are skipped because the
|
|
102
|
+
* Scheduler has no way to fire them on its own).
|
|
103
|
+
*/
|
|
104
|
+
readonly include?: ReadonlyArray<ConsolidatorTriggerSpec>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Outcome of {@link registerConsolidatorTriggers}.
|
|
109
|
+
*
|
|
110
|
+
* @stable
|
|
111
|
+
*/
|
|
112
|
+
export interface RegisterTriggersResult {
|
|
113
|
+
readonly registered: ReadonlyArray<{
|
|
114
|
+
readonly id: string;
|
|
115
|
+
readonly kind: ParsedTrigger['kind'];
|
|
116
|
+
readonly raw: string;
|
|
117
|
+
}>;
|
|
118
|
+
readonly skipped: ReadonlyArray<{
|
|
119
|
+
readonly raw: string;
|
|
120
|
+
readonly reason: 'unsupported-by-scheduler' | 'filtered-out';
|
|
121
|
+
}>;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Register every cron / idle / interval trigger declared on the
|
|
126
|
+
* supplied consolidator with the scheduler. Each trigger fires
|
|
127
|
+
* `consolidator.trigger(reason, scope)` so the lib-mode + server
|
|
128
|
+
* paths converge on the same handler.
|
|
129
|
+
*
|
|
130
|
+
* Turn triggers (`turn:N`) and event triggers (`event:NAME`) are
|
|
131
|
+
* skipped - the scheduler cannot count user turns autonomously, and
|
|
132
|
+
* event triggers fire from the consumer's emit path. The runtime
|
|
133
|
+
* caller is responsible for those (e.g. invoking
|
|
134
|
+
* `consolidator.trigger(...)` from the agent loop).
|
|
135
|
+
*
|
|
136
|
+
* @stable
|
|
137
|
+
*/
|
|
138
|
+
export async function registerConsolidatorTriggers(
|
|
139
|
+
consolidator: ConsolidatorLike,
|
|
140
|
+
scheduler: SchedulerLike,
|
|
141
|
+
options: RegisterTriggersOptions,
|
|
142
|
+
): Promise<RegisterTriggersResult> {
|
|
143
|
+
const config = consolidator.config();
|
|
144
|
+
const include = options.include ?? config.triggers;
|
|
145
|
+
const acknowledgeLibMode = options.acknowledgeLibMode ?? true;
|
|
146
|
+
const catchupPolicy: ConsolidatorCatchupPolicy = options.catchupPolicy ?? 'none';
|
|
147
|
+
const registered: Array<{ id: string; kind: ParsedTrigger['kind']; raw: string }> = [];
|
|
148
|
+
const skipped: Array<{ raw: string; reason: 'unsupported-by-scheduler' | 'filtered-out' }> = [];
|
|
149
|
+
const seenSet = new Set(include);
|
|
150
|
+
for (const spec of config.triggers) {
|
|
151
|
+
if (!seenSet.has(spec)) {
|
|
152
|
+
skipped.push({ raw: spec, reason: 'filtered-out' });
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const parsed = parseTriggerSpec(spec);
|
|
156
|
+
if (parsed.kind === 'turn' || parsed.kind === 'event' || parsed.kind === 'budget') {
|
|
157
|
+
skipped.push({ raw: parsed.raw, reason: 'unsupported-by-scheduler' });
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const id = `${options.idPrefix ?? 'consolidator'}:${parsed.raw}`;
|
|
161
|
+
const kind: 'cron' | 'idle' | 'interval' = parsed.kind === 'cron' ? 'cron' : 'idle';
|
|
162
|
+
const declaration: TriggerDeclarationLike = {
|
|
163
|
+
id,
|
|
164
|
+
kind,
|
|
165
|
+
spec: parsed.kind === 'cron' ? parsed.expression : String(parsed.idleMs),
|
|
166
|
+
async callback() {
|
|
167
|
+
await consolidator.trigger(reasonFromTrigger(parsed), options.scope);
|
|
168
|
+
},
|
|
169
|
+
options: {
|
|
170
|
+
catchupPolicy,
|
|
171
|
+
acknowledgeLibMode,
|
|
172
|
+
...(options.tags !== undefined ? { tags: options.tags } : {}),
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
await scheduler.register(declaration);
|
|
176
|
+
registered.push({ id, kind: parsed.kind, raw: parsed.raw });
|
|
177
|
+
}
|
|
178
|
+
return Object.freeze({
|
|
179
|
+
registered: Object.freeze(registered),
|
|
180
|
+
skipped: Object.freeze(skipped),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trigger spec parser. Translates the human-friendly
|
|
3
|
+
* `'turn:N'` / `'idle:Xm'` / `'cron:EXPR'` / `'event:NAME'` /
|
|
4
|
+
* `'budget:N'` declarations into the runtime structure used by the
|
|
5
|
+
* consolidator.
|
|
6
|
+
*
|
|
7
|
+
* The parser is exhaustive: every shape recognised here must be
|
|
8
|
+
* matched in {@link parseTriggerSpec}; unrecognised shapes throw a
|
|
9
|
+
* `TypeError` so misconfigured triggers fail at registration time
|
|
10
|
+
* rather than silently never firing.
|
|
11
|
+
*
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { ConsolidatorTriggerReason, ConsolidatorTriggerSpec } from './types.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parsed trigger declaration. The `kind` discriminator drives the
|
|
19
|
+
* runtime dispatch; the `value` carries the spec-specific argument
|
|
20
|
+
* already converted to a `number` (turns / millis / threshold) or a
|
|
21
|
+
* string (cron expr / event name).
|
|
22
|
+
*
|
|
23
|
+
* @stable
|
|
24
|
+
*/
|
|
25
|
+
export type ParsedTrigger =
|
|
26
|
+
| { readonly kind: 'turn'; readonly count: number; readonly raw: string }
|
|
27
|
+
| { readonly kind: 'idle'; readonly idleMs: number; readonly raw: string }
|
|
28
|
+
| { readonly kind: 'cron'; readonly expression: string; readonly raw: string }
|
|
29
|
+
| { readonly kind: 'event'; readonly name: string; readonly raw: string }
|
|
30
|
+
| { readonly kind: 'budget'; readonly threshold: number; readonly raw: string };
|
|
31
|
+
|
|
32
|
+
const IDLE_PATTERN = /^(\d+(?:\.\d+)?)(ms|s|m|h)?$/u;
|
|
33
|
+
|
|
34
|
+
/** Parse a {@link ConsolidatorTriggerSpec} into a {@link ParsedTrigger}. */
|
|
35
|
+
export function parseTriggerSpec(spec: ConsolidatorTriggerSpec): ParsedTrigger {
|
|
36
|
+
if (typeof spec !== 'string' || spec.length === 0) {
|
|
37
|
+
throw new TypeError(
|
|
38
|
+
`[graphorin/memory] consolidator trigger spec must be a non-empty string, got ${typeof spec}`,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
const colon = spec.indexOf(':');
|
|
42
|
+
if (colon < 0) {
|
|
43
|
+
throw new TypeError(
|
|
44
|
+
`[graphorin/memory] invalid trigger spec '${spec}' - expected 'kind:value'`,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const kind = spec.slice(0, colon);
|
|
48
|
+
const value = spec.slice(colon + 1);
|
|
49
|
+
|
|
50
|
+
switch (kind) {
|
|
51
|
+
case 'turn': {
|
|
52
|
+
const count = Number.parseInt(value, 10);
|
|
53
|
+
if (!Number.isFinite(count) || count <= 0) {
|
|
54
|
+
throw new TypeError(
|
|
55
|
+
`[graphorin/memory] invalid turn trigger '${spec}' - expected positive integer`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return { kind: 'turn', count, raw: spec };
|
|
59
|
+
}
|
|
60
|
+
case 'idle': {
|
|
61
|
+
const match = IDLE_PATTERN.exec(value);
|
|
62
|
+
if (match === null) {
|
|
63
|
+
throw new TypeError(
|
|
64
|
+
`[graphorin/memory] invalid idle trigger '${spec}' - expected '5m' / '30s' / '1h'`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
const num = Number.parseFloat(match[1] ?? 'NaN');
|
|
68
|
+
const unit = match[2] ?? 'ms';
|
|
69
|
+
const idleMs = toMs(num, unit);
|
|
70
|
+
if (!Number.isFinite(idleMs) || idleMs <= 0) {
|
|
71
|
+
throw new TypeError(
|
|
72
|
+
`[graphorin/memory] invalid idle trigger '${spec}' - non-positive duration`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return { kind: 'idle', idleMs, raw: spec };
|
|
76
|
+
}
|
|
77
|
+
case 'cron': {
|
|
78
|
+
if (value.trim().length === 0) {
|
|
79
|
+
throw new TypeError(`[graphorin/memory] invalid cron trigger '${spec}' - empty expression`);
|
|
80
|
+
}
|
|
81
|
+
return { kind: 'cron', expression: value, raw: spec };
|
|
82
|
+
}
|
|
83
|
+
case 'event': {
|
|
84
|
+
if (value.length === 0) {
|
|
85
|
+
throw new TypeError(
|
|
86
|
+
`[graphorin/memory] invalid event trigger '${spec}' - empty event name`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return { kind: 'event', name: value, raw: spec };
|
|
90
|
+
}
|
|
91
|
+
case 'budget': {
|
|
92
|
+
const threshold = Number.parseFloat(value);
|
|
93
|
+
if (!Number.isFinite(threshold) || threshold <= 0 || threshold >= 1) {
|
|
94
|
+
throw new TypeError(
|
|
95
|
+
`[graphorin/memory] invalid budget trigger '${spec}' - expected fraction between 0 and 1`,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
return { kind: 'budget', threshold, raw: spec };
|
|
99
|
+
}
|
|
100
|
+
default:
|
|
101
|
+
throw new TypeError(`[graphorin/memory] unknown consolidator trigger kind '${kind}'`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Build a {@link ConsolidatorTriggerReason} from a parsed trigger. */
|
|
106
|
+
export function reasonFromTrigger(trigger: ParsedTrigger): ConsolidatorTriggerReason {
|
|
107
|
+
switch (trigger.kind) {
|
|
108
|
+
case 'turn':
|
|
109
|
+
return { kind: 'turn', value: trigger.count };
|
|
110
|
+
case 'idle':
|
|
111
|
+
return { kind: 'idle', value: trigger.raw };
|
|
112
|
+
case 'cron':
|
|
113
|
+
return { kind: 'cron', value: trigger.expression };
|
|
114
|
+
case 'event':
|
|
115
|
+
return { kind: 'event', value: trigger.name };
|
|
116
|
+
case 'budget':
|
|
117
|
+
return { kind: 'budget', value: trigger.threshold };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function toMs(value: number, unit: string): number {
|
|
122
|
+
switch (unit) {
|
|
123
|
+
case 'ms':
|
|
124
|
+
return value;
|
|
125
|
+
case 's':
|
|
126
|
+
return value * 1000;
|
|
127
|
+
case 'm':
|
|
128
|
+
return value * 60 * 1000;
|
|
129
|
+
case 'h':
|
|
130
|
+
return value * 60 * 60 * 1000;
|
|
131
|
+
default:
|
|
132
|
+
return Number.NaN;
|
|
133
|
+
}
|
|
134
|
+
}
|