@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,471 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContextFork: Create sub-agent contexts with sensitivity enforcement.
|
|
3
|
+
*
|
|
4
|
+
* Enables forking contexts for sub-agents with automatic sensitivity redaction,
|
|
5
|
+
* budget overrides, and schema-enforced output validation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createHash } from 'crypto';
|
|
9
|
+
import { z, type ZodSchema } from 'zod';
|
|
10
|
+
import type { ContextGraph } from '../graph/context-graph.js';
|
|
11
|
+
import type { ContextView } from '../graph/views.js';
|
|
12
|
+
import type { ContextBlock, SensitivityLevel, BlockRef } from '../types/block.js';
|
|
13
|
+
import type { ModelRef } from '../types/policy.js';
|
|
14
|
+
import { RedactedStubCodec, type RedactedStubPayload } from '../codecs/redacted-stub.codec.js';
|
|
15
|
+
import { computeBlockHash } from '../types/hash.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Sub-agent task definition with schema-enforced output.
|
|
19
|
+
*/
|
|
20
|
+
export interface SubAgentTask<TOutput = unknown> {
|
|
21
|
+
/** Task instruction for the sub-agent */
|
|
22
|
+
instruction: string;
|
|
23
|
+
|
|
24
|
+
/** Expected output schema (Zod schema, required) */
|
|
25
|
+
expectedOutputSchema: ZodSchema<TOutput>;
|
|
26
|
+
|
|
27
|
+
/** Maximum output tokens (optional) */
|
|
28
|
+
maxOutputTokens?: number;
|
|
29
|
+
|
|
30
|
+
/** Forbidden fields that must not appear in output */
|
|
31
|
+
forbiddenFields?: string[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Fork options for creating sub-agent contexts.
|
|
36
|
+
*/
|
|
37
|
+
export interface ContextForkOptions {
|
|
38
|
+
/** Unique agent identifier */
|
|
39
|
+
agentId: string;
|
|
40
|
+
|
|
41
|
+
/** Human-readable fork name */
|
|
42
|
+
name: string;
|
|
43
|
+
|
|
44
|
+
/** Model to use for this fork */
|
|
45
|
+
model: ModelRef;
|
|
46
|
+
|
|
47
|
+
/** Inherit query from parent context */
|
|
48
|
+
inheritQuery?: boolean;
|
|
49
|
+
|
|
50
|
+
/** Include conversation history in fork */
|
|
51
|
+
includeHistory?: boolean;
|
|
52
|
+
|
|
53
|
+
/** Include state blocks in fork */
|
|
54
|
+
includeState?: boolean;
|
|
55
|
+
|
|
56
|
+
/** Maximum sensitivity level to include (default: 'public') */
|
|
57
|
+
maxSensitivity?: SensitivityLevel;
|
|
58
|
+
|
|
59
|
+
/** Token budget override (optional) */
|
|
60
|
+
budgetOverride?: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* LLM usage statistics.
|
|
65
|
+
*/
|
|
66
|
+
export interface UsageStats {
|
|
67
|
+
/** Input tokens */
|
|
68
|
+
inputTokens: number;
|
|
69
|
+
|
|
70
|
+
/** Output tokens */
|
|
71
|
+
outputTokens: number;
|
|
72
|
+
|
|
73
|
+
/** Total tokens */
|
|
74
|
+
totalTokens: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Provenance information for fork result.
|
|
79
|
+
*/
|
|
80
|
+
export interface ForkProvenance {
|
|
81
|
+
/** Source view hash */
|
|
82
|
+
sourceViewHash: string;
|
|
83
|
+
|
|
84
|
+
/** Execution hash (deterministic) */
|
|
85
|
+
executionHash: string;
|
|
86
|
+
|
|
87
|
+
/** Fork creation timestamp */
|
|
88
|
+
forkedAt: number;
|
|
89
|
+
|
|
90
|
+
/** Fork completion timestamp */
|
|
91
|
+
completedAt: number;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Fork execution result.
|
|
96
|
+
*/
|
|
97
|
+
export interface ForkResult<TOutput = unknown> {
|
|
98
|
+
/** Agent ID */
|
|
99
|
+
agentId: string;
|
|
100
|
+
|
|
101
|
+
/** Model used */
|
|
102
|
+
model: ModelRef;
|
|
103
|
+
|
|
104
|
+
/** Task summary */
|
|
105
|
+
summary: string;
|
|
106
|
+
|
|
107
|
+
/** Structured output (validated against schema) */
|
|
108
|
+
output: TOutput;
|
|
109
|
+
|
|
110
|
+
/** Generated artifacts (optional) */
|
|
111
|
+
artifacts?: Array<{
|
|
112
|
+
name: string;
|
|
113
|
+
type: string;
|
|
114
|
+
content: string;
|
|
115
|
+
}>;
|
|
116
|
+
|
|
117
|
+
/** Citations to source blocks (block hashes) */
|
|
118
|
+
citations?: string[];
|
|
119
|
+
|
|
120
|
+
/** Usage statistics */
|
|
121
|
+
usage: UsageStats;
|
|
122
|
+
|
|
123
|
+
/** Provenance information */
|
|
124
|
+
provenance: ForkProvenance;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Sensitivity level ordering (for comparison).
|
|
129
|
+
*/
|
|
130
|
+
const SENSITIVITY_ORDER: Record<SensitivityLevel, number> = {
|
|
131
|
+
public: 0,
|
|
132
|
+
internal: 1,
|
|
133
|
+
restricted: 2,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Check if a sensitivity level exceeds the maximum allowed level.
|
|
138
|
+
*
|
|
139
|
+
* @param level - Sensitivity level to check
|
|
140
|
+
* @param maxLevel - Maximum allowed level
|
|
141
|
+
* @returns True if level exceeds maxLevel
|
|
142
|
+
*/
|
|
143
|
+
function exceedsSensitivityLevel(
|
|
144
|
+
level: SensitivityLevel,
|
|
145
|
+
maxLevel: SensitivityLevel
|
|
146
|
+
): boolean {
|
|
147
|
+
return SENSITIVITY_ORDER[level] > SENSITIVITY_ORDER[maxLevel];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Create a redacted stub block for sensitive content.
|
|
152
|
+
*
|
|
153
|
+
* @param originalBlock - Original block to redact
|
|
154
|
+
* @param reason - Redaction reason
|
|
155
|
+
* @returns Redacted stub block
|
|
156
|
+
*/
|
|
157
|
+
function createRedactedStub(
|
|
158
|
+
originalBlock: ContextBlock<unknown>,
|
|
159
|
+
reason: string
|
|
160
|
+
): ContextBlock<RedactedStubPayload> {
|
|
161
|
+
const payload: RedactedStubPayload = {
|
|
162
|
+
originalBlockHash: originalBlock.blockHash,
|
|
163
|
+
reason,
|
|
164
|
+
placeholder: '[REDACTED - Sensitive content removed]',
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const meta = {
|
|
168
|
+
kind: originalBlock.meta.kind,
|
|
169
|
+
sensitivity: 'public' as const,
|
|
170
|
+
codecId: RedactedStubCodec.codecId,
|
|
171
|
+
codecVersion: RedactedStubCodec.version,
|
|
172
|
+
createdAt: Math.floor(Date.now() / 1000),
|
|
173
|
+
source: originalBlock.meta.source,
|
|
174
|
+
tags: originalBlock.meta.tags,
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const blockHash = computeBlockHash(meta, payload, RedactedStubCodec);
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
blockHash,
|
|
181
|
+
meta,
|
|
182
|
+
payload,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Filter view blocks by sensitivity level.
|
|
188
|
+
* Blocks exceeding maxSensitivity are replaced with redacted stubs.
|
|
189
|
+
*
|
|
190
|
+
* @param view - Source context view
|
|
191
|
+
* @param maxSensitivity - Maximum allowed sensitivity level
|
|
192
|
+
* @returns Filtered blocks with redacted stubs
|
|
193
|
+
*/
|
|
194
|
+
export function filterBySensitivity(
|
|
195
|
+
view: ContextView,
|
|
196
|
+
maxSensitivity: SensitivityLevel
|
|
197
|
+
): ContextBlock<unknown>[] {
|
|
198
|
+
return view.blocks.map((block) => {
|
|
199
|
+
if (exceedsSensitivityLevel(block.meta.sensitivity, maxSensitivity)) {
|
|
200
|
+
return createRedactedStub(
|
|
201
|
+
block,
|
|
202
|
+
`Sensitivity level '${block.meta.sensitivity}' exceeds maximum '${maxSensitivity}'`
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
return block;
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Compute deterministic execution hash.
|
|
211
|
+
* Hash includes: model + compiledViewHash + instruction + schemaHash + toolsetVersion
|
|
212
|
+
*
|
|
213
|
+
* @param model - Model reference
|
|
214
|
+
* @param viewHash - Compiled view hash
|
|
215
|
+
* @param instruction - Task instruction
|
|
216
|
+
* @param schemaHash - Output schema hash
|
|
217
|
+
* @param toolsetVersion - Toolset version (optional)
|
|
218
|
+
* @returns Hex-encoded SHA-256 hash
|
|
219
|
+
*/
|
|
220
|
+
export function computeExecutionHash(
|
|
221
|
+
model: ModelRef,
|
|
222
|
+
viewHash: string,
|
|
223
|
+
instruction: string,
|
|
224
|
+
schemaHash: string,
|
|
225
|
+
toolsetVersion?: string
|
|
226
|
+
): string {
|
|
227
|
+
const combined = {
|
|
228
|
+
model: `${model.provider}:${model.model}`,
|
|
229
|
+
viewHash,
|
|
230
|
+
instruction,
|
|
231
|
+
schemaHash,
|
|
232
|
+
toolsetVersion: toolsetVersion ?? 'none',
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
return createHash('sha256')
|
|
236
|
+
.update(JSON.stringify(combined))
|
|
237
|
+
.digest('hex');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Compute schema hash for deterministic execution tracking.
|
|
242
|
+
*
|
|
243
|
+
* @param schema - Zod schema
|
|
244
|
+
* @returns Hex-encoded SHA-256 hash
|
|
245
|
+
*/
|
|
246
|
+
export function computeSchemaHash(schema: ZodSchema): string {
|
|
247
|
+
// Use schema description for hashing (simplified approach)
|
|
248
|
+
// In production, you'd want to serialize the schema structure
|
|
249
|
+
const schemaDesc = schema.description ?? JSON.stringify(schema._def);
|
|
250
|
+
|
|
251
|
+
return createHash('sha256')
|
|
252
|
+
.update(schemaDesc)
|
|
253
|
+
.digest('hex');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* ContextFork: Create sub-agent contexts with sensitivity enforcement.
|
|
258
|
+
*/
|
|
259
|
+
export class ContextFork {
|
|
260
|
+
constructor(
|
|
261
|
+
private readonly graph: ContextGraph,
|
|
262
|
+
private readonly parentView: ContextView
|
|
263
|
+
) {}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Create a fork with sensitivity filtering and budget override.
|
|
267
|
+
*
|
|
268
|
+
* @param options - Fork options
|
|
269
|
+
* @returns Filtered context view for sub-agent
|
|
270
|
+
*/
|
|
271
|
+
async createFork(options: ContextForkOptions): Promise<ContextView> {
|
|
272
|
+
const maxSensitivity = options.maxSensitivity ?? 'public';
|
|
273
|
+
|
|
274
|
+
// Filter blocks by sensitivity
|
|
275
|
+
const filteredBlocks = filterBySensitivity(this.parentView, maxSensitivity);
|
|
276
|
+
|
|
277
|
+
// Apply additional filters based on options
|
|
278
|
+
let finalBlocks = filteredBlocks;
|
|
279
|
+
|
|
280
|
+
if (!options.includeHistory) {
|
|
281
|
+
finalBlocks = finalBlocks.filter((block) => block.meta.kind !== 'history');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (!options.includeState) {
|
|
285
|
+
finalBlocks = finalBlocks.filter((block) => block.meta.kind !== 'state');
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Create new view with filtered blocks
|
|
289
|
+
// Re-compute stable prefix hash
|
|
290
|
+
const stablePrefixHash = createHash('sha256')
|
|
291
|
+
.update(finalBlocks.map((b) => b.blockHash).join('|'))
|
|
292
|
+
.digest('hex');
|
|
293
|
+
|
|
294
|
+
const forkedView: ContextView = {
|
|
295
|
+
blocks: finalBlocks,
|
|
296
|
+
stablePrefixHash,
|
|
297
|
+
createdAt: Math.floor(Date.now() / 1000),
|
|
298
|
+
tokenEstimate: options.budgetOverride
|
|
299
|
+
? { tokens: options.budgetOverride, confidence: 'high', truncated: false }
|
|
300
|
+
: this.parentView.tokenEstimate,
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
return forkedView;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Execute a sub-agent task with schema validation.
|
|
308
|
+
*
|
|
309
|
+
* @param task - Sub-agent task definition
|
|
310
|
+
* @param options - Fork options
|
|
311
|
+
* @param executor - Async executor function (calls LLM)
|
|
312
|
+
* @returns Fork result with validated output
|
|
313
|
+
*/
|
|
314
|
+
async executeFork<TOutput>(
|
|
315
|
+
task: SubAgentTask<TOutput>,
|
|
316
|
+
options: ContextForkOptions,
|
|
317
|
+
executor: (instruction: string, view: ContextView) => Promise<{
|
|
318
|
+
output: unknown;
|
|
319
|
+
summary: string;
|
|
320
|
+
artifacts?: Array<{ name: string; type: string; content: string }>;
|
|
321
|
+
citations?: string[];
|
|
322
|
+
usage: UsageStats;
|
|
323
|
+
}>
|
|
324
|
+
): Promise<ForkResult<TOutput>> {
|
|
325
|
+
const forkedAt = Math.floor(Date.now() / 1000);
|
|
326
|
+
|
|
327
|
+
// Create fork
|
|
328
|
+
const forkedView = await this.createFork(options);
|
|
329
|
+
|
|
330
|
+
// Validate forbidden fields
|
|
331
|
+
if (task.forbiddenFields && task.forbiddenFields.length > 0) {
|
|
332
|
+
// Add validation to instruction
|
|
333
|
+
const forbiddenFieldsNote = `\n\nIMPORTANT: Your output MUST NOT include these fields: ${task.forbiddenFields.join(', ')}`;
|
|
334
|
+
const enhancedInstruction = task.instruction + forbiddenFieldsNote;
|
|
335
|
+
|
|
336
|
+
// Execute with enhanced instruction
|
|
337
|
+
const executionResult = await executor(enhancedInstruction, forkedView);
|
|
338
|
+
|
|
339
|
+
// Validate output against schema
|
|
340
|
+
const validatedOutput = task.expectedOutputSchema.parse(executionResult.output);
|
|
341
|
+
|
|
342
|
+
// Check for forbidden fields in output
|
|
343
|
+
const outputStr = JSON.stringify(validatedOutput);
|
|
344
|
+
for (const field of task.forbiddenFields) {
|
|
345
|
+
if (outputStr.includes(field)) {
|
|
346
|
+
throw new Error(
|
|
347
|
+
`Fork execution failed: Output contains forbidden field '${field}'`
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const completedAt = Math.floor(Date.now() / 1000);
|
|
353
|
+
|
|
354
|
+
// Compute execution hash
|
|
355
|
+
const schemaHash = computeSchemaHash(task.expectedOutputSchema);
|
|
356
|
+
const executionHash = computeExecutionHash(
|
|
357
|
+
options.model,
|
|
358
|
+
forkedView.stablePrefixHash,
|
|
359
|
+
task.instruction,
|
|
360
|
+
schemaHash
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
return {
|
|
364
|
+
agentId: options.agentId,
|
|
365
|
+
model: options.model,
|
|
366
|
+
summary: executionResult.summary,
|
|
367
|
+
output: validatedOutput,
|
|
368
|
+
artifacts: executionResult.artifacts,
|
|
369
|
+
citations: executionResult.citations,
|
|
370
|
+
usage: executionResult.usage,
|
|
371
|
+
provenance: {
|
|
372
|
+
sourceViewHash: this.parentView.stablePrefixHash,
|
|
373
|
+
executionHash,
|
|
374
|
+
forkedAt,
|
|
375
|
+
completedAt,
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
} else {
|
|
379
|
+
// Execute without forbidden field validation
|
|
380
|
+
const executionResult = await executor(task.instruction, forkedView);
|
|
381
|
+
|
|
382
|
+
// Validate output against schema
|
|
383
|
+
const validatedOutput = task.expectedOutputSchema.parse(executionResult.output);
|
|
384
|
+
|
|
385
|
+
const completedAt = Math.floor(Date.now() / 1000);
|
|
386
|
+
|
|
387
|
+
// Compute execution hash
|
|
388
|
+
const schemaHash = computeSchemaHash(task.expectedOutputSchema);
|
|
389
|
+
const executionHash = computeExecutionHash(
|
|
390
|
+
options.model,
|
|
391
|
+
forkedView.stablePrefixHash,
|
|
392
|
+
task.instruction,
|
|
393
|
+
schemaHash
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
return {
|
|
397
|
+
agentId: options.agentId,
|
|
398
|
+
model: options.model,
|
|
399
|
+
summary: executionResult.summary,
|
|
400
|
+
output: validatedOutput,
|
|
401
|
+
artifacts: executionResult.artifacts,
|
|
402
|
+
citations: executionResult.citations,
|
|
403
|
+
usage: executionResult.usage,
|
|
404
|
+
provenance: {
|
|
405
|
+
sourceViewHash: this.parentView.stablePrefixHash,
|
|
406
|
+
executionHash,
|
|
407
|
+
forkedAt,
|
|
408
|
+
completedAt,
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Ingest fork result back into parent graph.
|
|
416
|
+
* Adds result as a memory block with provenance tracking.
|
|
417
|
+
*
|
|
418
|
+
* @param result - Fork result to ingest
|
|
419
|
+
* @param asMemory - Add as memory block (default: true)
|
|
420
|
+
* @returns Block hash of ingested result
|
|
421
|
+
*/
|
|
422
|
+
ingestForkResult<TOutput>(
|
|
423
|
+
result: ForkResult<TOutput>,
|
|
424
|
+
asMemory = true
|
|
425
|
+
): string {
|
|
426
|
+
// Create a structured reference block for the fork result
|
|
427
|
+
// This would use a codec designed for fork results
|
|
428
|
+
// For now, we'll use a simplified approach
|
|
429
|
+
|
|
430
|
+
const payload = {
|
|
431
|
+
agentId: result.agentId,
|
|
432
|
+
model: result.model,
|
|
433
|
+
summary: result.summary,
|
|
434
|
+
output: result.output,
|
|
435
|
+
artifacts: result.artifacts,
|
|
436
|
+
citations: result.citations,
|
|
437
|
+
usage: result.usage,
|
|
438
|
+
provenance: result.provenance,
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
const meta = {
|
|
442
|
+
kind: asMemory ? ('memory' as const) : ('reference' as const),
|
|
443
|
+
sensitivity: 'public' as const,
|
|
444
|
+
codecId: 'fork-result',
|
|
445
|
+
codecVersion: '1.0.0',
|
|
446
|
+
createdAt: Math.floor(Date.now() / 1000),
|
|
447
|
+
source: result.agentId,
|
|
448
|
+
tags: ['fork-result', result.model.provider],
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
// Compute block hash (without codec for now)
|
|
452
|
+
const blockHash = createHash('sha256')
|
|
453
|
+
.update(JSON.stringify({ meta, payload }))
|
|
454
|
+
.digest('hex');
|
|
455
|
+
|
|
456
|
+
const block: ContextBlock<unknown> = {
|
|
457
|
+
blockHash,
|
|
458
|
+
meta,
|
|
459
|
+
payload,
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
// Add to graph with provenance
|
|
463
|
+
const derivedFrom: BlockRef[] = result.citations
|
|
464
|
+
? result.citations.map((hash) => ({ blockHash: hash }))
|
|
465
|
+
: [];
|
|
466
|
+
|
|
467
|
+
this.graph.addBlock(block, derivedFrom);
|
|
468
|
+
|
|
469
|
+
return blockHash;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base codec utilities.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createHash } from 'crypto';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Default hash implementation: SHA-256 of JSON.stringify(canonicalized).
|
|
9
|
+
*
|
|
10
|
+
* @param canonicalized - Canonicalized payload
|
|
11
|
+
* @returns Hex-encoded SHA-256 hash
|
|
12
|
+
*/
|
|
13
|
+
export function defaultHash(canonicalized: unknown): string {
|
|
14
|
+
return createHash('sha256')
|
|
15
|
+
.update(JSON.stringify(canonicalized))
|
|
16
|
+
.digest('hex');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Sort object keys for deterministic serialization.
|
|
21
|
+
*
|
|
22
|
+
* @param obj - Object to sort
|
|
23
|
+
* @returns Object with sorted keys
|
|
24
|
+
*/
|
|
25
|
+
export function sortObjectKeys(obj: Record<string, unknown>): Record<string, unknown> {
|
|
26
|
+
const sorted: Record<string, unknown> = {};
|
|
27
|
+
for (const key of Object.keys(obj).sort()) {
|
|
28
|
+
const value = obj[key];
|
|
29
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
30
|
+
sorted[key] = sortObjectKeys(value as Record<string, unknown>);
|
|
31
|
+
} else {
|
|
32
|
+
sorted[key] = value;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return sorted;
|
|
36
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation history codec (kind: 'history').
|
|
3
|
+
*
|
|
4
|
+
* Previous turns in conversation.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import type { BlockCodec, RenderedContent } from '../types/codec.js';
|
|
9
|
+
import type { ContextBlock } from '../types/block.js';
|
|
10
|
+
import { defaultHash, sortObjectKeys } from './base.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Conversation message schema.
|
|
14
|
+
*/
|
|
15
|
+
export const ConversationMessageSchema = z.object({
|
|
16
|
+
/** Message role */
|
|
17
|
+
role: z.enum(['user', 'assistant']),
|
|
18
|
+
|
|
19
|
+
/** Message content (text or structured) */
|
|
20
|
+
content: z.union([
|
|
21
|
+
z.string(),
|
|
22
|
+
z.array(z.record(z.unknown())),
|
|
23
|
+
]),
|
|
24
|
+
|
|
25
|
+
/** Optional message ID */
|
|
26
|
+
messageId: z.string().optional(),
|
|
27
|
+
|
|
28
|
+
/** Optional timestamp */
|
|
29
|
+
timestamp: z.number().optional(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export type ConversationMessage = z.infer<typeof ConversationMessageSchema>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Conversation history payload schema.
|
|
36
|
+
*/
|
|
37
|
+
export const ConversationHistoryPayloadSchema = z.object({
|
|
38
|
+
/** Array of messages */
|
|
39
|
+
messages: z.array(ConversationMessageSchema),
|
|
40
|
+
|
|
41
|
+
/** Optional summary of earlier messages */
|
|
42
|
+
summary: z.string().optional(),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export type ConversationHistoryPayload = z.infer<typeof ConversationHistoryPayloadSchema>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Conversation history codec implementation.
|
|
49
|
+
*/
|
|
50
|
+
export const ConversationHistoryCodec: BlockCodec<ConversationHistoryPayload> = {
|
|
51
|
+
codecId: 'conversation-history',
|
|
52
|
+
version: '1.0.0',
|
|
53
|
+
payloadSchema: ConversationHistoryPayloadSchema,
|
|
54
|
+
|
|
55
|
+
canonicalize(payload: ConversationHistoryPayload): unknown {
|
|
56
|
+
// Canonicalize messages (exclude volatile fields like timestamp)
|
|
57
|
+
const canonicalMessages = payload.messages.map((msg) => ({
|
|
58
|
+
role: msg.role,
|
|
59
|
+
content: msg.content,
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
return sortObjectKeys({
|
|
63
|
+
messages: canonicalMessages,
|
|
64
|
+
summary: payload.summary ?? null,
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
hash(canonicalized: unknown): string {
|
|
69
|
+
return defaultHash(canonicalized);
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
render(block: ContextBlock<ConversationHistoryPayload>): RenderedContent {
|
|
73
|
+
const { messages, summary } = block.payload;
|
|
74
|
+
|
|
75
|
+
// Add summary as first message if present
|
|
76
|
+
const allMessages = summary
|
|
77
|
+
? [{ role: 'user' as const, content: `**Earlier conversation (summary):**\n${summary}` }, ...messages]
|
|
78
|
+
: messages;
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
// Anthropic: messages array
|
|
82
|
+
anthropic: allMessages.map((msg) => ({
|
|
83
|
+
role: msg.role,
|
|
84
|
+
content: typeof msg.content === 'string'
|
|
85
|
+
? msg.content
|
|
86
|
+
: msg.content,
|
|
87
|
+
})),
|
|
88
|
+
|
|
89
|
+
// OpenAI: messages array
|
|
90
|
+
openai: allMessages.map((msg) => ({
|
|
91
|
+
role: msg.role,
|
|
92
|
+
content: msg.content,
|
|
93
|
+
})),
|
|
94
|
+
|
|
95
|
+
// Gemini: convert to user/model role
|
|
96
|
+
gemini: allMessages.map((msg) => ({
|
|
97
|
+
role: msg.role === 'assistant' ? 'model' : 'user',
|
|
98
|
+
parts: typeof msg.content === 'string'
|
|
99
|
+
? [{ text: msg.content }]
|
|
100
|
+
: msg.content,
|
|
101
|
+
})),
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
validate(payload: unknown): ConversationHistoryPayload {
|
|
106
|
+
return ConversationHistoryPayloadSchema.parse(payload);
|
|
107
|
+
},
|
|
108
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in codecs for @foundry/context
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from './base.js';
|
|
6
|
+
export * from './system-rules.codec.js';
|
|
7
|
+
export * from './tool-schema.codec.js';
|
|
8
|
+
export * from './structured-reference.codec.js';
|
|
9
|
+
export * from './conversation-history.codec.js';
|
|
10
|
+
export * from './tool-output.codec.js';
|
|
11
|
+
export * from './redacted-stub.codec.js';
|
|
12
|
+
export * from './unsafe-text.codec.js';
|
|
13
|
+
|
|
14
|
+
import type { BlockCodec } from '../types/codec.js';
|
|
15
|
+
import { SystemRulesCodec } from './system-rules.codec.js';
|
|
16
|
+
import { ToolSchemaCodec } from './tool-schema.codec.js';
|
|
17
|
+
import { StructuredReferenceCodec } from './structured-reference.codec.js';
|
|
18
|
+
import { ConversationHistoryCodec } from './conversation-history.codec.js';
|
|
19
|
+
import { ToolOutputCodec } from './tool-output.codec.js';
|
|
20
|
+
import { RedactedStubCodec } from './redacted-stub.codec.js';
|
|
21
|
+
import { UnsafeTextCodec } from './unsafe-text.codec.js';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Built-in codec registry.
|
|
25
|
+
* Maps codecId -> codec implementation.
|
|
26
|
+
*/
|
|
27
|
+
export const BUILT_IN_CODECS: Record<string, BlockCodec> = {
|
|
28
|
+
'system-rules': SystemRulesCodec,
|
|
29
|
+
'tool-schema': ToolSchemaCodec,
|
|
30
|
+
'structured-reference': StructuredReferenceCodec,
|
|
31
|
+
'conversation-history': ConversationHistoryCodec,
|
|
32
|
+
'tool-output': ToolOutputCodec,
|
|
33
|
+
'redacted-stub': RedactedStubCodec,
|
|
34
|
+
'unsafe-text': UnsafeTextCodec,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get codec by ID.
|
|
39
|
+
*
|
|
40
|
+
* @param codecId - Codec identifier
|
|
41
|
+
* @returns Codec implementation, or undefined if not found
|
|
42
|
+
*/
|
|
43
|
+
export function getCodec(codecId: string): BlockCodec | undefined {
|
|
44
|
+
return BUILT_IN_CODECS[codecId];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Register custom codec.
|
|
49
|
+
*
|
|
50
|
+
* @param codec - Custom codec implementation
|
|
51
|
+
*/
|
|
52
|
+
export function registerCodec(codec: BlockCodec): void {
|
|
53
|
+
if (BUILT_IN_CODECS[codec.codecId]) {
|
|
54
|
+
throw new Error(`Codec already registered: ${codec.codecId}`);
|
|
55
|
+
}
|
|
56
|
+
BUILT_IN_CODECS[codec.codecId] = codec;
|
|
57
|
+
}
|