@diyor28/context 1.0.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/README.md +270 -0
- package/dist/__tests__/attachment-selector.test.d.ts +11 -0
- package/dist/__tests__/attachment-selector.test.d.ts.map +1 -0
- package/dist/__tests__/attachment-selector.test.js +449 -0
- package/dist/__tests__/attachment-selector.test.js.map +1 -0
- package/dist/__tests__/cache-breakpoints.test.d.ts +11 -0
- package/dist/__tests__/cache-breakpoints.test.d.ts.map +1 -0
- package/dist/__tests__/cache-breakpoints.test.js +398 -0
- package/dist/__tests__/cache-breakpoints.test.js.map +1 -0
- package/dist/__tests__/codecs.test.d.ts +7 -0
- package/dist/__tests__/codecs.test.d.ts.map +1 -0
- package/dist/__tests__/codecs.test.js +331 -0
- package/dist/__tests__/codecs.test.js.map +1 -0
- package/dist/__tests__/compactor.test.d.ts +11 -0
- package/dist/__tests__/compactor.test.d.ts.map +1 -0
- package/dist/__tests__/compactor.test.js +519 -0
- package/dist/__tests__/compactor.test.js.map +1 -0
- package/dist/__tests__/context-graph.test.d.ts +7 -0
- package/dist/__tests__/context-graph.test.d.ts.map +1 -0
- package/dist/__tests__/context-graph.test.js +262 -0
- package/dist/__tests__/context-graph.test.js.map +1 -0
- package/dist/__tests__/hash.test.d.ts +7 -0
- package/dist/__tests__/hash.test.d.ts.map +1 -0
- package/dist/__tests__/hash.test.js +228 -0
- package/dist/__tests__/hash.test.js.map +1 -0
- package/dist/__tests__/integration.test.d.ts +15 -0
- package/dist/__tests__/integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration.test.js +728 -0
- package/dist/__tests__/integration.test.js.map +1 -0
- package/dist/__tests__/kind-order.test.d.ts +7 -0
- package/dist/__tests__/kind-order.test.d.ts.map +1 -0
- package/dist/__tests__/kind-order.test.js +243 -0
- package/dist/__tests__/kind-order.test.js.map +1 -0
- package/dist/__tests__/phase2-integration.test.d.ts +5 -0
- package/dist/__tests__/phase2-integration.test.d.ts.map +1 -0
- package/dist/__tests__/phase2-integration.test.js +222 -0
- package/dist/__tests__/phase2-integration.test.js.map +1 -0
- package/dist/__tests__/queries.test.d.ts +7 -0
- package/dist/__tests__/queries.test.d.ts.map +1 -0
- package/dist/__tests__/queries.test.js +254 -0
- package/dist/__tests__/queries.test.js.map +1 -0
- package/dist/__tests__/token-estimator.test.d.ts +7 -0
- package/dist/__tests__/token-estimator.test.d.ts.map +1 -0
- package/dist/__tests__/token-estimator.test.js +267 -0
- package/dist/__tests__/token-estimator.test.js.map +1 -0
- package/dist/adapters/anthropic-estimator.d.ts +38 -0
- package/dist/adapters/anthropic-estimator.d.ts.map +1 -0
- package/dist/adapters/anthropic-estimator.js +108 -0
- package/dist/adapters/anthropic-estimator.js.map +1 -0
- package/dist/adapters/attachment-resolver.d.ts +96 -0
- package/dist/adapters/attachment-resolver.d.ts.map +1 -0
- package/dist/adapters/attachment-resolver.js +176 -0
- package/dist/adapters/attachment-resolver.js.map +1 -0
- package/dist/adapters/attachment-selector.d.ts +59 -0
- package/dist/adapters/attachment-selector.d.ts.map +1 -0
- package/dist/adapters/attachment-selector.js +163 -0
- package/dist/adapters/attachment-selector.js.map +1 -0
- package/dist/adapters/gemini-estimator.d.ts +27 -0
- package/dist/adapters/gemini-estimator.d.ts.map +1 -0
- package/dist/adapters/gemini-estimator.js +80 -0
- package/dist/adapters/gemini-estimator.js.map +1 -0
- package/dist/adapters/index.d.ts +12 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +28 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/memory-store.d.ts +139 -0
- package/dist/adapters/memory-store.d.ts.map +1 -0
- package/dist/adapters/memory-store.js +187 -0
- package/dist/adapters/memory-store.js.map +1 -0
- package/dist/adapters/openai-estimator.d.ts +35 -0
- package/dist/adapters/openai-estimator.d.ts.map +1 -0
- package/dist/adapters/openai-estimator.js +89 -0
- package/dist/adapters/openai-estimator.js.map +1 -0
- package/dist/adapters/summarizer.d.ts +121 -0
- package/dist/adapters/summarizer.d.ts.map +1 -0
- package/dist/adapters/summarizer.js +121 -0
- package/dist/adapters/summarizer.js.map +1 -0
- package/dist/adapters/token-estimator.d.ts +63 -0
- package/dist/adapters/token-estimator.d.ts.map +1 -0
- package/dist/adapters/token-estimator.js +37 -0
- package/dist/adapters/token-estimator.js.map +1 -0
- package/dist/builder/context-builder.d.ts +186 -0
- package/dist/builder/context-builder.d.ts.map +1 -0
- package/dist/builder/context-builder.js +305 -0
- package/dist/builder/context-builder.js.map +1 -0
- package/dist/builder/context-fork.d.ts +166 -0
- package/dist/builder/context-fork.d.ts.map +1 -0
- package/dist/builder/context-fork.js +282 -0
- package/dist/builder/context-fork.js.map +1 -0
- package/dist/builder/index.d.ts +6 -0
- package/dist/builder/index.d.ts.map +1 -0
- package/dist/builder/index.js +22 -0
- package/dist/builder/index.js.map +1 -0
- package/dist/codecs/base.d.ts +18 -0
- package/dist/codecs/base.d.ts.map +1 -0
- package/dist/codecs/base.js +39 -0
- package/dist/codecs/base.js.map +1 -0
- package/dist/codecs/conversation-history.codec.d.ts +81 -0
- package/dist/codecs/conversation-history.codec.d.ts.map +1 -0
- package/dist/codecs/conversation-history.codec.js +89 -0
- package/dist/codecs/conversation-history.codec.js.map +1 -0
- package/dist/codecs/index.d.ts +31 -0
- package/dist/codecs/index.d.ts.map +1 -0
- package/dist/codecs/index.js +71 -0
- package/dist/codecs/index.js.map +1 -0
- package/dist/codecs/redacted-stub.codec.d.ts +32 -0
- package/dist/codecs/redacted-stub.codec.d.ts.map +1 -0
- package/dist/codecs/redacted-stub.codec.js +64 -0
- package/dist/codecs/redacted-stub.codec.js.map +1 -0
- package/dist/codecs/structured-reference.codec.d.ts +40 -0
- package/dist/codecs/structured-reference.codec.d.ts.map +1 -0
- package/dist/codecs/structured-reference.codec.js +81 -0
- package/dist/codecs/structured-reference.codec.js.map +1 -0
- package/dist/codecs/system-rules.codec.d.ts +32 -0
- package/dist/codecs/system-rules.codec.d.ts.map +1 -0
- package/dist/codecs/system-rules.codec.js +62 -0
- package/dist/codecs/system-rules.codec.js.map +1 -0
- package/dist/codecs/tool-output.codec.d.ts +66 -0
- package/dist/codecs/tool-output.codec.d.ts.map +1 -0
- package/dist/codecs/tool-output.codec.js +95 -0
- package/dist/codecs/tool-output.codec.js.map +1 -0
- package/dist/codecs/tool-schema.codec.d.ts +36 -0
- package/dist/codecs/tool-schema.codec.d.ts.map +1 -0
- package/dist/codecs/tool-schema.codec.js +74 -0
- package/dist/codecs/tool-schema.codec.js.map +1 -0
- package/dist/codecs/unsafe-text.codec.d.ts +28 -0
- package/dist/codecs/unsafe-text.codec.d.ts.map +1 -0
- package/dist/codecs/unsafe-text.codec.js +63 -0
- package/dist/codecs/unsafe-text.codec.js.map +1 -0
- package/dist/graph/context-graph.d.ts +121 -0
- package/dist/graph/context-graph.d.ts.map +1 -0
- package/dist/graph/context-graph.js +166 -0
- package/dist/graph/context-graph.js.map +1 -0
- package/dist/graph/index.d.ts +8 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +24 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/kind-order.d.ts +60 -0
- package/dist/graph/kind-order.d.ts.map +1 -0
- package/dist/graph/kind-order.js +113 -0
- package/dist/graph/kind-order.js.map +1 -0
- package/dist/graph/queries.d.ts +68 -0
- package/dist/graph/queries.d.ts.map +1 -0
- package/dist/graph/queries.js +240 -0
- package/dist/graph/queries.js.map +1 -0
- package/dist/graph/views.d.ts +90 -0
- package/dist/graph/views.d.ts.map +1 -0
- package/dist/graph/views.js +173 -0
- package/dist/graph/views.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/pipeline/compactor.d.ts +128 -0
- package/dist/pipeline/compactor.d.ts.map +1 -0
- package/dist/pipeline/compactor.js +346 -0
- package/dist/pipeline/compactor.js.map +1 -0
- package/dist/pipeline/index.d.ts +6 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +22 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/summarizer.d.ts +18 -0
- package/dist/pipeline/summarizer.d.ts.map +1 -0
- package/dist/pipeline/summarizer.js +68 -0
- package/dist/pipeline/summarizer.js.map +1 -0
- package/dist/policies/default-policy.d.ts +29 -0
- package/dist/policies/default-policy.d.ts.map +1 -0
- package/dist/policies/default-policy.js +58 -0
- package/dist/policies/default-policy.js.map +1 -0
- package/dist/policies/index.d.ts +5 -0
- package/dist/policies/index.d.ts.map +1 -0
- package/dist/policies/index.js +21 -0
- package/dist/policies/index.js.map +1 -0
- package/dist/providers/anthropic-compiler.d.ts +58 -0
- package/dist/providers/anthropic-compiler.d.ts.map +1 -0
- package/dist/providers/anthropic-compiler.js +182 -0
- package/dist/providers/anthropic-compiler.js.map +1 -0
- package/dist/providers/capabilities.d.ts +54 -0
- package/dist/providers/capabilities.d.ts.map +1 -0
- package/dist/providers/capabilities.js +87 -0
- package/dist/providers/capabilities.js.map +1 -0
- package/dist/providers/gemini-compiler.d.ts +51 -0
- package/dist/providers/gemini-compiler.d.ts.map +1 -0
- package/dist/providers/gemini-compiler.js +206 -0
- package/dist/providers/gemini-compiler.js.map +1 -0
- package/dist/providers/index.d.ts +8 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +24 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai-compiler.d.ts +46 -0
- package/dist/providers/openai-compiler.d.ts.map +1 -0
- package/dist/providers/openai-compiler.js +149 -0
- package/dist/providers/openai-compiler.js.map +1 -0
- package/dist/types/attachment.d.ts +62 -0
- package/dist/types/attachment.d.ts.map +1 -0
- package/dist/types/attachment.js +6 -0
- package/dist/types/attachment.js.map +1 -0
- package/dist/types/block.d.ts +61 -0
- package/dist/types/block.d.ts.map +1 -0
- package/dist/types/block.js +8 -0
- package/dist/types/block.js.map +1 -0
- package/dist/types/codec.d.ts +58 -0
- package/dist/types/codec.d.ts.map +1 -0
- package/dist/types/codec.js +6 -0
- package/dist/types/codec.js.map +1 -0
- package/dist/types/compiled.d.ts +91 -0
- package/dist/types/compiled.d.ts.map +1 -0
- package/dist/types/compiled.js +6 -0
- package/dist/types/compiled.js.map +1 -0
- package/dist/types/hash.d.ts +24 -0
- package/dist/types/hash.d.ts.map +1 -0
- package/dist/types/hash.js +49 -0
- package/dist/types/hash.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +26 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/policy.d.ts +128 -0
- package/dist/types/policy.d.ts.map +1 -0
- package/dist/types/policy.js +55 -0
- package/dist/types/policy.js.map +1 -0
- package/package.json +55 -0
- package/postcss.config.js +4 -0
- package/src/__tests__/attachment-selector.test.ts +559 -0
- package/src/__tests__/cache-breakpoints.test.ts +566 -0
- package/src/__tests__/codecs.test.ts +417 -0
- package/src/__tests__/compactor.test.ts +608 -0
- package/src/__tests__/context-graph.test.ts +383 -0
- package/src/__tests__/hash.test.ts +274 -0
- package/src/__tests__/integration.test.ts +866 -0
- package/src/__tests__/kind-order.test.ts +312 -0
- package/src/__tests__/phase2-integration.test.ts +253 -0
- package/src/__tests__/queries.test.ts +387 -0
- package/src/__tests__/token-estimator.test.ts +326 -0
- package/src/adapters/anthropic-estimator.ts +125 -0
- package/src/adapters/attachment-resolver.ts +295 -0
- package/src/adapters/attachment-selector.ts +218 -0
- package/src/adapters/gemini-estimator.ts +93 -0
- package/src/adapters/index.ts +12 -0
- package/src/adapters/memory-store.ts +299 -0
- package/src/adapters/openai-estimator.ts +105 -0
- package/src/adapters/summarizer.ts +250 -0
- package/src/adapters/token-estimator.ts +74 -0
- package/src/builder/context-builder.ts +467 -0
- package/src/builder/context-fork.ts +471 -0
- package/src/builder/index.ts +6 -0
- package/src/codecs/base.ts +36 -0
- package/src/codecs/conversation-history.codec.ts +108 -0
- package/src/codecs/index.ts +57 -0
- package/src/codecs/redacted-stub.codec.ts +76 -0
- package/src/codecs/structured-reference.codec.ts +96 -0
- package/src/codecs/system-rules.codec.ts +74 -0
- package/src/codecs/tool-output.codec.ts +109 -0
- package/src/codecs/tool-schema.codec.ts +87 -0
- package/src/codecs/unsafe-text.codec.ts +74 -0
- package/src/graph/context-graph.ts +205 -0
- package/src/graph/index.ts +8 -0
- package/src/graph/kind-order.ts +125 -0
- package/src/graph/queries.ts +306 -0
- package/src/graph/views.ts +255 -0
- package/src/index.ts +31 -0
- package/src/pipeline/compactor.ts +563 -0
- package/src/pipeline/index.ts +6 -0
- package/src/pipeline/summarizer.ts +76 -0
- package/src/policies/default-policy.ts +69 -0
- package/src/policies/index.ts +5 -0
- package/src/providers/anthropic-compiler.ts +294 -0
- package/src/providers/capabilities.ts +144 -0
- package/src/providers/gemini-compiler.ts +272 -0
- package/src/providers/index.ts +8 -0
- package/src/providers/openai-compiler.ts +191 -0
- package/src/types/attachment.ts +86 -0
- package/src/types/block.ts +84 -0
- package/src/types/codec.ts +68 -0
- package/src/types/compiled.ts +109 -0
- package/src/types/hash.ts +58 -0
- package/src/types/index.ts +10 -0
- package/src/types/policy.ts +194 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +21 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContextGraph: Core data structure for managing context blocks and relationships.
|
|
3
|
+
*
|
|
4
|
+
* Holds blocks, tracks derivation/reference edges, supports querying and view creation.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ContextBlock, BlockRef } from '../types/block.js';
|
|
8
|
+
import type { BlockQuery } from './queries.js';
|
|
9
|
+
import type { ContextView, ViewOptions } from './views.js';
|
|
10
|
+
import { createContextView } from './views.js';
|
|
11
|
+
import { matchesQuery } from './queries.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Edge types for block relationships.
|
|
15
|
+
*/
|
|
16
|
+
export interface ContextGraphEdges {
|
|
17
|
+
/** Derivation edges: blockHash -> parent BlockRefs (provenance tracking) */
|
|
18
|
+
derivedFrom: Map<string, BlockRef[]>;
|
|
19
|
+
|
|
20
|
+
/** Reference edges: blockHash -> referenced blockHashes (lightweight citations) */
|
|
21
|
+
references: Map<string, string[]>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* ContextGraph: Mutable graph of context blocks with relationship tracking.
|
|
26
|
+
*
|
|
27
|
+
* Responsibilities:
|
|
28
|
+
* - Store blocks by content-addressed hash
|
|
29
|
+
* - Track derivation and reference relationships
|
|
30
|
+
* - Support query-based block selection
|
|
31
|
+
* - Create deterministic views with stable ordering
|
|
32
|
+
*/
|
|
33
|
+
export class ContextGraph {
|
|
34
|
+
/** All blocks indexed by blockHash */
|
|
35
|
+
private readonly blocks: Map<string, ContextBlock<unknown>>;
|
|
36
|
+
|
|
37
|
+
/** Block relationship edges */
|
|
38
|
+
private readonly edges: ContextGraphEdges;
|
|
39
|
+
|
|
40
|
+
constructor() {
|
|
41
|
+
this.blocks = new Map();
|
|
42
|
+
this.edges = {
|
|
43
|
+
derivedFrom: new Map(),
|
|
44
|
+
references: new Map(),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Add a block to the graph.
|
|
50
|
+
* Idempotent: if block already exists (same hash), this is a no-op.
|
|
51
|
+
*
|
|
52
|
+
* @param block - Block to add
|
|
53
|
+
* @param derivedFrom - Optional parent blocks for provenance
|
|
54
|
+
* @param references - Optional referenced block hashes
|
|
55
|
+
*/
|
|
56
|
+
addBlock<TPayload>(
|
|
57
|
+
block: ContextBlock<TPayload>,
|
|
58
|
+
derivedFrom?: BlockRef[],
|
|
59
|
+
references?: string[]
|
|
60
|
+
): void {
|
|
61
|
+
const { blockHash } = block;
|
|
62
|
+
|
|
63
|
+
// Idempotent: skip if already exists
|
|
64
|
+
if (this.blocks.has(blockHash)) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Add block
|
|
69
|
+
this.blocks.set(blockHash, block as ContextBlock<unknown>);
|
|
70
|
+
|
|
71
|
+
// Add derivation edges
|
|
72
|
+
if (derivedFrom && derivedFrom.length > 0) {
|
|
73
|
+
this.edges.derivedFrom.set(blockHash, derivedFrom);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Add reference edges
|
|
77
|
+
if (references && references.length > 0) {
|
|
78
|
+
this.edges.references.set(blockHash, references);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Remove a block from the graph.
|
|
84
|
+
* Also removes associated edges.
|
|
85
|
+
*
|
|
86
|
+
* @param blockHash - Block hash to remove
|
|
87
|
+
* @returns True if block was removed, false if not found
|
|
88
|
+
*/
|
|
89
|
+
removeBlock(blockHash: string): boolean {
|
|
90
|
+
const existed = this.blocks.delete(blockHash);
|
|
91
|
+
|
|
92
|
+
// Clean up edges
|
|
93
|
+
this.edges.derivedFrom.delete(blockHash);
|
|
94
|
+
this.edges.references.delete(blockHash);
|
|
95
|
+
|
|
96
|
+
return existed;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get a block by hash.
|
|
101
|
+
*
|
|
102
|
+
* @param blockHash - Block hash
|
|
103
|
+
* @returns Block if found, undefined otherwise
|
|
104
|
+
*/
|
|
105
|
+
getBlock<TPayload = unknown>(blockHash: string): ContextBlock<TPayload> | undefined {
|
|
106
|
+
return this.blocks.get(blockHash) as ContextBlock<TPayload> | undefined;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Check if a block exists in the graph.
|
|
111
|
+
*
|
|
112
|
+
* @param blockHash - Block hash
|
|
113
|
+
* @returns True if block exists
|
|
114
|
+
*/
|
|
115
|
+
hasBlock(blockHash: string): boolean {
|
|
116
|
+
return this.blocks.has(blockHash);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get all blocks in the graph (unordered).
|
|
121
|
+
*
|
|
122
|
+
* @returns Array of all blocks
|
|
123
|
+
*/
|
|
124
|
+
getAllBlocks(): ContextBlock<unknown>[] {
|
|
125
|
+
return Array.from(this.blocks.values());
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get the number of blocks in the graph.
|
|
130
|
+
*
|
|
131
|
+
* @returns Block count
|
|
132
|
+
*/
|
|
133
|
+
getBlockCount(): number {
|
|
134
|
+
return this.blocks.size;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get parent blocks (provenance) for a given block.
|
|
139
|
+
*
|
|
140
|
+
* @param blockHash - Block hash
|
|
141
|
+
* @returns Array of parent BlockRefs, or empty array if none
|
|
142
|
+
*/
|
|
143
|
+
getDerivedFrom(blockHash: string): BlockRef[] {
|
|
144
|
+
return this.edges.derivedFrom.get(blockHash) ?? [];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get referenced block hashes for a given block.
|
|
149
|
+
*
|
|
150
|
+
* @param blockHash - Block hash
|
|
151
|
+
* @returns Array of referenced hashes, or empty array if none
|
|
152
|
+
*/
|
|
153
|
+
getReferences(blockHash: string): string[] {
|
|
154
|
+
return this.edges.references.get(blockHash) ?? [];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Select blocks matching a query.
|
|
159
|
+
* Returns blocks in arbitrary order (use createView for deterministic ordering).
|
|
160
|
+
*
|
|
161
|
+
* @param query - Block query
|
|
162
|
+
* @returns Matching blocks
|
|
163
|
+
*/
|
|
164
|
+
select(query: BlockQuery): ContextBlock<unknown>[] {
|
|
165
|
+
const allBlocks = this.getAllBlocks();
|
|
166
|
+
return allBlocks.filter((block) => matchesQuery(block, query, this));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Create a deterministic view of the graph.
|
|
171
|
+
* View is immutable snapshot with stable ordering (KIND_ORDER + lexicographic).
|
|
172
|
+
*
|
|
173
|
+
* @param options - View options (query, token budget, etc.)
|
|
174
|
+
* @returns ContextView with ordered blocks
|
|
175
|
+
*/
|
|
176
|
+
async createView(options: ViewOptions): Promise<ContextView> {
|
|
177
|
+
return createContextView(this, options);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Clear all blocks and edges.
|
|
182
|
+
*/
|
|
183
|
+
clear(): void {
|
|
184
|
+
this.blocks.clear();
|
|
185
|
+
this.edges.derivedFrom.clear();
|
|
186
|
+
this.edges.references.clear();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get graph statistics.
|
|
191
|
+
*
|
|
192
|
+
* @returns Graph stats
|
|
193
|
+
*/
|
|
194
|
+
getStats(): {
|
|
195
|
+
blockCount: number;
|
|
196
|
+
derivationEdgeCount: number;
|
|
197
|
+
referenceEdgeCount: number;
|
|
198
|
+
} {
|
|
199
|
+
return {
|
|
200
|
+
blockCount: this.blocks.size,
|
|
201
|
+
derivationEdgeCount: this.edges.derivedFrom.size,
|
|
202
|
+
referenceEdgeCount: this.edges.references.size,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KIND_ORDER: Deterministic block ordering for context compilation.
|
|
3
|
+
*
|
|
4
|
+
* This is the single source of truth for block ordering.
|
|
5
|
+
* All context compilation MUST respect this order.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { BlockKind, ContextBlock } from '../types/block.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Immutable block kind ordering (pinned → reference → memory → state → tool_output → history → turn).
|
|
12
|
+
*
|
|
13
|
+
* NEVER modify this array. It is the contract for deterministic compilation.
|
|
14
|
+
*/
|
|
15
|
+
export const KIND_ORDER: readonly BlockKind[] = Object.freeze([
|
|
16
|
+
'pinned', // System rules, always first
|
|
17
|
+
'reference', // Tool schemas, external docs
|
|
18
|
+
'memory', // Long-term memory, RAG results
|
|
19
|
+
'state', // Current workflow/session state
|
|
20
|
+
'tool_output', // Tool execution results
|
|
21
|
+
'history', // Conversation history
|
|
22
|
+
'turn', // Current turn (user message)
|
|
23
|
+
] as const);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get kind index for ordering comparison.
|
|
27
|
+
* Returns -1 if kind is not in KIND_ORDER.
|
|
28
|
+
*
|
|
29
|
+
* @param kind - Block kind
|
|
30
|
+
* @returns Index in KIND_ORDER, or -1 if not found
|
|
31
|
+
*/
|
|
32
|
+
export function getKindIndex(kind: BlockKind): number {
|
|
33
|
+
return KIND_ORDER.indexOf(kind);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Compare two block kinds for ordering.
|
|
38
|
+
* Returns negative if a < b, positive if a > b, zero if equal.
|
|
39
|
+
*
|
|
40
|
+
* @param a - First block kind
|
|
41
|
+
* @param b - Second block kind
|
|
42
|
+
* @returns Comparison result
|
|
43
|
+
*/
|
|
44
|
+
export function compareKinds(a: BlockKind, b: BlockKind): number {
|
|
45
|
+
const indexA = getKindIndex(a);
|
|
46
|
+
const indexB = getKindIndex(b);
|
|
47
|
+
|
|
48
|
+
// Throw if either kind is not in KIND_ORDER
|
|
49
|
+
if (indexA === -1) {
|
|
50
|
+
throw new Error(`Invalid block kind: ${a}`);
|
|
51
|
+
}
|
|
52
|
+
if (indexB === -1) {
|
|
53
|
+
throw new Error(`Invalid block kind: ${b}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return indexA - indexB;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Sort blocks by KIND_ORDER (stable sort, preserves relative order within same kind).
|
|
61
|
+
*
|
|
62
|
+
* @param blocks - Blocks to sort
|
|
63
|
+
* @returns Sorted blocks (new array)
|
|
64
|
+
*/
|
|
65
|
+
export function sortBlocksByKind<TPayload = unknown>(
|
|
66
|
+
blocks: ContextBlock<TPayload>[]
|
|
67
|
+
): ContextBlock<TPayload>[] {
|
|
68
|
+
return [...blocks].sort((a, b) => compareKinds(a.meta.kind, b.meta.kind));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Validate that blocks are sorted by KIND_ORDER.
|
|
73
|
+
* Throws if blocks are not sorted correctly.
|
|
74
|
+
*
|
|
75
|
+
* @param blocks - Blocks to validate
|
|
76
|
+
*/
|
|
77
|
+
export function validateBlockOrder<TPayload = unknown>(
|
|
78
|
+
blocks: ContextBlock<TPayload>[]
|
|
79
|
+
): void {
|
|
80
|
+
for (let i = 1; i < blocks.length; i++) {
|
|
81
|
+
const prev = blocks[i - 1];
|
|
82
|
+
const curr = blocks[i];
|
|
83
|
+
|
|
84
|
+
const comparison = compareKinds(prev.meta.kind, curr.meta.kind);
|
|
85
|
+
if (comparison > 0) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
`Blocks not sorted by KIND_ORDER: ${prev.meta.kind} (index ${i - 1}) ` +
|
|
88
|
+
`comes before ${curr.meta.kind} (index ${i})`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Group blocks by kind (in KIND_ORDER).
|
|
96
|
+
* Returns a map of kind -> blocks.
|
|
97
|
+
*
|
|
98
|
+
* @param blocks - Blocks to group
|
|
99
|
+
* @returns Map of kind to blocks
|
|
100
|
+
*/
|
|
101
|
+
export function groupBlocksByKind<TPayload = unknown>(
|
|
102
|
+
blocks: ContextBlock<TPayload>[]
|
|
103
|
+
): Map<BlockKind, ContextBlock<TPayload>[]> {
|
|
104
|
+
const groups = new Map<BlockKind, ContextBlock<TPayload>[]>();
|
|
105
|
+
|
|
106
|
+
for (const block of blocks) {
|
|
107
|
+
const kind = block.meta.kind;
|
|
108
|
+
if (!groups.has(kind)) {
|
|
109
|
+
groups.set(kind, []);
|
|
110
|
+
}
|
|
111
|
+
groups.get(kind)!.push(block);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return groups;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Check if a kind is valid (exists in KIND_ORDER).
|
|
119
|
+
*
|
|
120
|
+
* @param kind - Block kind to check
|
|
121
|
+
* @returns True if valid
|
|
122
|
+
*/
|
|
123
|
+
export function isValidKind(kind: string): kind is BlockKind {
|
|
124
|
+
return getKindIndex(kind as BlockKind) !== -1;
|
|
125
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BlockQuery: Filtering and selection of context blocks.
|
|
3
|
+
*
|
|
4
|
+
* Supports filtering by kind, tags, sensitivity, stability, provenance, and token budget.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { BlockKind, SensitivityLevel, ContextBlock } from '../types/block.js';
|
|
8
|
+
import type { ContextGraph } from './context-graph.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Block query for filtering blocks in a ContextGraph.
|
|
12
|
+
*/
|
|
13
|
+
export interface BlockQuery {
|
|
14
|
+
/** Filter by block kinds (OR logic: match any) */
|
|
15
|
+
kinds?: BlockKind[];
|
|
16
|
+
|
|
17
|
+
/** Filter by tags (AND logic: block must have all tags) */
|
|
18
|
+
tags?: string[];
|
|
19
|
+
|
|
20
|
+
/** Filter by minimum sensitivity level */
|
|
21
|
+
minSensitivity?: SensitivityLevel;
|
|
22
|
+
|
|
23
|
+
/** Filter by maximum sensitivity level */
|
|
24
|
+
maxSensitivity?: SensitivityLevel;
|
|
25
|
+
|
|
26
|
+
/** Filter by source identifier */
|
|
27
|
+
source?: string;
|
|
28
|
+
|
|
29
|
+
/** Filter by minimum creation timestamp (Unix seconds) */
|
|
30
|
+
minCreatedAt?: number;
|
|
31
|
+
|
|
32
|
+
/** Filter by maximum creation timestamp (Unix seconds) */
|
|
33
|
+
maxCreatedAt?: number;
|
|
34
|
+
|
|
35
|
+
/** Filter by provenance: only blocks derived from given hashes */
|
|
36
|
+
derivedFromAny?: string[];
|
|
37
|
+
|
|
38
|
+
/** Filter by provenance: only blocks NOT derived from given hashes */
|
|
39
|
+
notDerivedFromAny?: string[];
|
|
40
|
+
|
|
41
|
+
/** Filter by references: only blocks referencing any of given hashes */
|
|
42
|
+
referencesAny?: string[];
|
|
43
|
+
|
|
44
|
+
/** Exclude blocks with given hashes */
|
|
45
|
+
excludeHashes?: string[];
|
|
46
|
+
|
|
47
|
+
/** Maximum token budget (requires token estimation - applied in views) */
|
|
48
|
+
maxTokens?: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Sensitivity level ordering (public < internal < restricted).
|
|
53
|
+
*/
|
|
54
|
+
const SENSITIVITY_ORDER: Record<SensitivityLevel, number> = {
|
|
55
|
+
public: 0,
|
|
56
|
+
internal: 1,
|
|
57
|
+
restricted: 2,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Compare two sensitivity levels.
|
|
62
|
+
*
|
|
63
|
+
* @param a - First sensitivity level
|
|
64
|
+
* @param b - Second sensitivity level
|
|
65
|
+
* @returns Negative if a < b, positive if a > b, zero if equal
|
|
66
|
+
*/
|
|
67
|
+
export function compareSensitivity(a: SensitivityLevel, b: SensitivityLevel): number {
|
|
68
|
+
return SENSITIVITY_ORDER[a] - SENSITIVITY_ORDER[b];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if a block matches a query.
|
|
73
|
+
* Does NOT apply token budget (that's done in view creation).
|
|
74
|
+
*
|
|
75
|
+
* @param block - Block to check
|
|
76
|
+
* @param query - Query to match
|
|
77
|
+
* @param graph - Context graph (for provenance/reference lookups)
|
|
78
|
+
* @returns True if block matches query
|
|
79
|
+
*/
|
|
80
|
+
export function matchesQuery(
|
|
81
|
+
block: ContextBlock<unknown>,
|
|
82
|
+
query: BlockQuery,
|
|
83
|
+
graph: ContextGraph
|
|
84
|
+
): boolean {
|
|
85
|
+
// Filter by kinds (OR logic)
|
|
86
|
+
if (query.kinds && query.kinds.length > 0) {
|
|
87
|
+
if (!query.kinds.includes(block.meta.kind)) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Filter by tags (AND logic: block must have all query tags)
|
|
93
|
+
if (query.tags && query.tags.length > 0) {
|
|
94
|
+
const blockTags = new Set(block.meta.tags ?? []);
|
|
95
|
+
for (const tag of query.tags) {
|
|
96
|
+
if (!blockTags.has(tag)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Filter by minimum sensitivity
|
|
103
|
+
if (query.minSensitivity !== undefined) {
|
|
104
|
+
if (compareSensitivity(block.meta.sensitivity, query.minSensitivity) < 0) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Filter by maximum sensitivity
|
|
110
|
+
if (query.maxSensitivity !== undefined) {
|
|
111
|
+
if (compareSensitivity(block.meta.sensitivity, query.maxSensitivity) > 0) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Filter by source
|
|
117
|
+
if (query.source !== undefined) {
|
|
118
|
+
if (block.meta.source !== query.source) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Filter by minimum creation timestamp
|
|
124
|
+
if (query.minCreatedAt !== undefined) {
|
|
125
|
+
if (block.meta.createdAt < query.minCreatedAt) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Filter by maximum creation timestamp
|
|
131
|
+
if (query.maxCreatedAt !== undefined) {
|
|
132
|
+
if (block.meta.createdAt > query.maxCreatedAt) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Filter by provenance: derivedFromAny
|
|
138
|
+
if (query.derivedFromAny && query.derivedFromAny.length > 0) {
|
|
139
|
+
const parents = graph.getDerivedFrom(block.blockHash);
|
|
140
|
+
const parentHashes = new Set(parents.map((p) => p.blockHash));
|
|
141
|
+
|
|
142
|
+
const hasMatch = query.derivedFromAny.some((hash) => parentHashes.has(hash));
|
|
143
|
+
if (!hasMatch) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Filter by provenance: notDerivedFromAny
|
|
149
|
+
if (query.notDerivedFromAny && query.notDerivedFromAny.length > 0) {
|
|
150
|
+
const parents = graph.getDerivedFrom(block.blockHash);
|
|
151
|
+
const parentHashes = new Set(parents.map((p) => p.blockHash));
|
|
152
|
+
|
|
153
|
+
const hasMatch = query.notDerivedFromAny.some((hash) => parentHashes.has(hash));
|
|
154
|
+
if (hasMatch) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Filter by references: referencesAny
|
|
160
|
+
if (query.referencesAny && query.referencesAny.length > 0) {
|
|
161
|
+
const refs = graph.getReferences(block.blockHash);
|
|
162
|
+
const refSet = new Set(refs);
|
|
163
|
+
|
|
164
|
+
const hasMatch = query.referencesAny.some((hash) => refSet.has(hash));
|
|
165
|
+
if (!hasMatch) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Exclude specific hashes
|
|
171
|
+
if (query.excludeHashes && query.excludeHashes.length > 0) {
|
|
172
|
+
if (query.excludeHashes.includes(block.blockHash)) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// All filters passed
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Create an empty query (matches all blocks).
|
|
183
|
+
*
|
|
184
|
+
* @returns Empty query
|
|
185
|
+
*/
|
|
186
|
+
export function emptyQuery(): BlockQuery {
|
|
187
|
+
return {};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Merge multiple queries (AND logic: block must match all queries).
|
|
192
|
+
*
|
|
193
|
+
* @param queries - Queries to merge
|
|
194
|
+
* @returns Merged query
|
|
195
|
+
*/
|
|
196
|
+
export function mergeQueries(...queries: BlockQuery[]): BlockQuery {
|
|
197
|
+
const merged: BlockQuery = {};
|
|
198
|
+
|
|
199
|
+
for (const query of queries) {
|
|
200
|
+
// Merge kinds (intersection)
|
|
201
|
+
if (query.kinds) {
|
|
202
|
+
if (merged.kinds) {
|
|
203
|
+
const kindSet = new Set(merged.kinds);
|
|
204
|
+
merged.kinds = query.kinds.filter((k) => kindSet.has(k));
|
|
205
|
+
} else {
|
|
206
|
+
merged.kinds = [...query.kinds];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Merge tags (union - block must have all)
|
|
211
|
+
if (query.tags) {
|
|
212
|
+
merged.tags = [...(merged.tags ?? []), ...query.tags];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Merge sensitivity (most restrictive)
|
|
216
|
+
if (query.minSensitivity !== undefined) {
|
|
217
|
+
if (merged.minSensitivity === undefined) {
|
|
218
|
+
merged.minSensitivity = query.minSensitivity;
|
|
219
|
+
} else {
|
|
220
|
+
// Take the higher minimum
|
|
221
|
+
if (compareSensitivity(query.minSensitivity, merged.minSensitivity) > 0) {
|
|
222
|
+
merged.minSensitivity = query.minSensitivity;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (query.maxSensitivity !== undefined) {
|
|
228
|
+
if (merged.maxSensitivity === undefined) {
|
|
229
|
+
merged.maxSensitivity = query.maxSensitivity;
|
|
230
|
+
} else {
|
|
231
|
+
// Take the lower maximum
|
|
232
|
+
if (compareSensitivity(query.maxSensitivity, merged.maxSensitivity) < 0) {
|
|
233
|
+
merged.maxSensitivity = query.maxSensitivity;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Merge source (must match - conflicting sources = no results)
|
|
239
|
+
if (query.source !== undefined) {
|
|
240
|
+
if (merged.source !== undefined && merged.source !== query.source) {
|
|
241
|
+
// Conflict: return impossible query
|
|
242
|
+
merged.kinds = [];
|
|
243
|
+
} else {
|
|
244
|
+
merged.source = query.source;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Merge timestamps (most restrictive)
|
|
249
|
+
if (query.minCreatedAt !== undefined) {
|
|
250
|
+
if (merged.minCreatedAt === undefined) {
|
|
251
|
+
merged.minCreatedAt = query.minCreatedAt;
|
|
252
|
+
} else {
|
|
253
|
+
merged.minCreatedAt = Math.max(merged.minCreatedAt, query.minCreatedAt);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (query.maxCreatedAt !== undefined) {
|
|
258
|
+
if (merged.maxCreatedAt === undefined) {
|
|
259
|
+
merged.maxCreatedAt = query.maxCreatedAt;
|
|
260
|
+
} else {
|
|
261
|
+
merged.maxCreatedAt = Math.min(merged.maxCreatedAt, query.maxCreatedAt);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Merge provenance (union)
|
|
266
|
+
if (query.derivedFromAny) {
|
|
267
|
+
merged.derivedFromAny = [
|
|
268
|
+
...(merged.derivedFromAny ?? []),
|
|
269
|
+
...query.derivedFromAny,
|
|
270
|
+
];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (query.notDerivedFromAny) {
|
|
274
|
+
merged.notDerivedFromAny = [
|
|
275
|
+
...(merged.notDerivedFromAny ?? []),
|
|
276
|
+
...query.notDerivedFromAny,
|
|
277
|
+
];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (query.referencesAny) {
|
|
281
|
+
merged.referencesAny = [
|
|
282
|
+
...(merged.referencesAny ?? []),
|
|
283
|
+
...query.referencesAny,
|
|
284
|
+
];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Merge excludeHashes (union)
|
|
288
|
+
if (query.excludeHashes) {
|
|
289
|
+
merged.excludeHashes = [
|
|
290
|
+
...(merged.excludeHashes ?? []),
|
|
291
|
+
...query.excludeHashes,
|
|
292
|
+
];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Merge maxTokens (minimum)
|
|
296
|
+
if (query.maxTokens !== undefined) {
|
|
297
|
+
if (merged.maxTokens === undefined) {
|
|
298
|
+
merged.maxTokens = query.maxTokens;
|
|
299
|
+
} else {
|
|
300
|
+
merged.maxTokens = Math.min(merged.maxTokens, query.maxTokens);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return merged;
|
|
306
|
+
}
|