@weave_protocol/domere 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/PLANNING.md +231 -0
- package/README.md +50 -0
- package/dist/anchoring/ethereum.d.ts +135 -0
- package/dist/anchoring/ethereum.d.ts.map +1 -0
- package/dist/anchoring/ethereum.js +474 -0
- package/dist/anchoring/ethereum.js.map +1 -0
- package/dist/anchoring/index.d.ts +93 -0
- package/dist/anchoring/index.d.ts.map +1 -0
- package/dist/anchoring/index.js +184 -0
- package/dist/anchoring/index.js.map +1 -0
- package/dist/anchoring/merkle.d.ts +91 -0
- package/dist/anchoring/merkle.d.ts.map +1 -0
- package/dist/anchoring/merkle.js +203 -0
- package/dist/anchoring/merkle.js.map +1 -0
- package/dist/anchoring/solana.d.ts +85 -0
- package/dist/anchoring/solana.d.ts.map +1 -0
- package/dist/anchoring/solana.js +301 -0
- package/dist/anchoring/solana.js.map +1 -0
- package/dist/constants.d.ts +130 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +536 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/language/code-analyzer.d.ts +80 -0
- package/dist/language/code-analyzer.d.ts.map +1 -0
- package/dist/language/code-analyzer.js +489 -0
- package/dist/language/code-analyzer.js.map +1 -0
- package/dist/language/detector.d.ts +53 -0
- package/dist/language/detector.d.ts.map +1 -0
- package/dist/language/detector.js +248 -0
- package/dist/language/detector.js.map +1 -0
- package/dist/language/index.d.ts +61 -0
- package/dist/language/index.d.ts.map +1 -0
- package/dist/language/index.js +109 -0
- package/dist/language/index.js.map +1 -0
- package/dist/language/nl-analyzer.d.ts +59 -0
- package/dist/language/nl-analyzer.d.ts.map +1 -0
- package/dist/language/nl-analyzer.js +350 -0
- package/dist/language/nl-analyzer.js.map +1 -0
- package/dist/language/semantic.d.ts +48 -0
- package/dist/language/semantic.d.ts.map +1 -0
- package/dist/language/semantic.js +329 -0
- package/dist/language/semantic.js.map +1 -0
- package/dist/storage/index.d.ts +6 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +6 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/memory.d.ts +48 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +211 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/thread/drift.d.ts +43 -0
- package/dist/thread/drift.d.ts.map +1 -0
- package/dist/thread/drift.js +248 -0
- package/dist/thread/drift.js.map +1 -0
- package/dist/thread/index.d.ts +9 -0
- package/dist/thread/index.d.ts.map +1 -0
- package/dist/thread/index.js +9 -0
- package/dist/thread/index.js.map +1 -0
- package/dist/thread/intent.d.ts +68 -0
- package/dist/thread/intent.d.ts.map +1 -0
- package/dist/thread/intent.js +333 -0
- package/dist/thread/intent.js.map +1 -0
- package/dist/thread/manager.d.ts +85 -0
- package/dist/thread/manager.d.ts.map +1 -0
- package/dist/thread/manager.js +305 -0
- package/dist/thread/manager.js.map +1 -0
- package/dist/thread/weave.d.ts +61 -0
- package/dist/thread/weave.d.ts.map +1 -0
- package/dist/thread/weave.js +158 -0
- package/dist/thread/weave.js.map +1 -0
- package/dist/tools/index.d.ts +18 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +102 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types.d.ts +466 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +48 -0
- package/dist/types.js.map +1 -0
- package/package.json +24 -0
- package/src/anchoring/ethereum.ts +568 -0
- package/src/anchoring/index.ts +236 -0
- package/src/anchoring/merkle.ts +256 -0
- package/src/anchoring/solana.ts +370 -0
- package/src/constants.ts +566 -0
- package/src/index.ts +43 -0
- package/src/language/code-analyzer.ts +564 -0
- package/src/language/detector.ts +297 -0
- package/src/language/index.ts +129 -0
- package/src/language/nl-analyzer.ts +411 -0
- package/src/language/semantic.ts +385 -0
- package/src/storage/index.ts +6 -0
- package/src/storage/memory.ts +271 -0
- package/src/thread/drift.ts +319 -0
- package/src/thread/index.ts +9 -0
- package/src/thread/intent.ts +409 -0
- package/src/thread/manager.ts +414 -0
- package/src/thread/weave.ts +205 -0
- package/src/tools/index.ts +107 -0
- package/src/types.ts +736 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dōmere - The Judge Protocol
|
|
3
|
+
* Thread Manager
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as crypto from 'crypto';
|
|
7
|
+
import type {
|
|
8
|
+
Thread,
|
|
9
|
+
ThreadOrigin,
|
|
10
|
+
ThreadIntent,
|
|
11
|
+
ThreadHop,
|
|
12
|
+
ThreadStatus,
|
|
13
|
+
IDomereStorage,
|
|
14
|
+
AgentInfo,
|
|
15
|
+
HopAction,
|
|
16
|
+
DriftAnalysis,
|
|
17
|
+
LanguageAnalysis,
|
|
18
|
+
SecurityScanResult,
|
|
19
|
+
} from '../types.js';
|
|
20
|
+
import { ThreadError } from '../types.js';
|
|
21
|
+
import { LanguageAnalyzerService } from '../language/index.js';
|
|
22
|
+
import { IntentAnalyzer } from './intent.js';
|
|
23
|
+
import { DriftDetector } from './drift.js';
|
|
24
|
+
import { WeaveSignature } from './weave.js';
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Thread Manager
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
export class ThreadManager {
|
|
31
|
+
private storage: IDomereStorage;
|
|
32
|
+
private languageAnalyzer: LanguageAnalyzerService;
|
|
33
|
+
private intentAnalyzer: IntentAnalyzer;
|
|
34
|
+
private driftDetector: DriftDetector;
|
|
35
|
+
private weaveSignature: WeaveSignature;
|
|
36
|
+
|
|
37
|
+
constructor(storage: IDomereStorage) {
|
|
38
|
+
this.storage = storage;
|
|
39
|
+
this.languageAnalyzer = new LanguageAnalyzerService();
|
|
40
|
+
this.intentAnalyzer = new IntentAnalyzer();
|
|
41
|
+
this.driftDetector = new DriftDetector();
|
|
42
|
+
this.weaveSignature = new WeaveSignature();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Create a new thread
|
|
47
|
+
*/
|
|
48
|
+
async createThread(config: {
|
|
49
|
+
origin: Omit<ThreadOrigin, 'timestamp'>;
|
|
50
|
+
intent: string;
|
|
51
|
+
constraints?: string[];
|
|
52
|
+
metadata?: Record<string, unknown>;
|
|
53
|
+
}): Promise<Thread> {
|
|
54
|
+
const now = new Date();
|
|
55
|
+
const threadId = this.generateThreadId();
|
|
56
|
+
|
|
57
|
+
// Analyze the intent
|
|
58
|
+
const intentAnalysis = this.intentAnalyzer.analyze(config.intent);
|
|
59
|
+
const languageAnalysis = this.languageAnalyzer.analyze(config.intent);
|
|
60
|
+
|
|
61
|
+
const intent: ThreadIntent = {
|
|
62
|
+
raw: config.intent,
|
|
63
|
+
hash: this.hashContent(config.intent),
|
|
64
|
+
normalized: intentAnalysis.normalized,
|
|
65
|
+
classification: languageAnalysis.semantic?.intent_classification || 'unknown',
|
|
66
|
+
constraints: config.constraints || [],
|
|
67
|
+
entities: languageAnalysis.semantic?.entities || [],
|
|
68
|
+
actions_implied: languageAnalysis.semantic?.actions_implied || [],
|
|
69
|
+
language_analysis: languageAnalysis,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const origin: ThreadOrigin = {
|
|
73
|
+
...config.origin,
|
|
74
|
+
timestamp: now,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Create initial weave signature
|
|
78
|
+
const initialSignature = this.weaveSignature.createInitial({
|
|
79
|
+
threadId,
|
|
80
|
+
origin,
|
|
81
|
+
intent,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const thread: Thread = {
|
|
85
|
+
id: threadId,
|
|
86
|
+
origin,
|
|
87
|
+
intent,
|
|
88
|
+
hops: [],
|
|
89
|
+
weave_signature: initialSignature,
|
|
90
|
+
status: 'active',
|
|
91
|
+
created_at: now,
|
|
92
|
+
updated_at: now,
|
|
93
|
+
metadata: config.metadata || {},
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
await this.storage.saveThread(thread);
|
|
97
|
+
|
|
98
|
+
return thread;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Add a hop to a thread
|
|
103
|
+
*/
|
|
104
|
+
async addHop(config: {
|
|
105
|
+
thread_id: string;
|
|
106
|
+
agent: AgentInfo;
|
|
107
|
+
received_intent: string;
|
|
108
|
+
actions: HopAction[];
|
|
109
|
+
security_scan?: SecurityScanResult;
|
|
110
|
+
sandbox_result?: { sandbox_id: string; result_id: string; status: string; summary: string };
|
|
111
|
+
}): Promise<ThreadHop> {
|
|
112
|
+
const thread = await this.storage.getThread(config.thread_id);
|
|
113
|
+
if (!thread) {
|
|
114
|
+
throw new ThreadError('Thread not found', { thread_id: config.thread_id });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (thread.status !== 'active') {
|
|
118
|
+
throw new ThreadError('Thread is not active', {
|
|
119
|
+
thread_id: config.thread_id,
|
|
120
|
+
status: thread.status
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const now = new Date();
|
|
125
|
+
const sequence = thread.hops.length + 1;
|
|
126
|
+
const hopId = this.generateHopId(config.thread_id, sequence);
|
|
127
|
+
|
|
128
|
+
// Analyze received intent
|
|
129
|
+
const languageAnalysis = this.languageAnalyzer.analyze(config.received_intent);
|
|
130
|
+
|
|
131
|
+
// Calculate previous intent for drift detection
|
|
132
|
+
const previousIntent = sequence === 1
|
|
133
|
+
? thread.intent.raw
|
|
134
|
+
: thread.hops[thread.hops.length - 1].received_intent;
|
|
135
|
+
|
|
136
|
+
// Detect drift
|
|
137
|
+
const driftAnalysis = this.driftDetector.analyze({
|
|
138
|
+
original_intent: thread.intent.raw,
|
|
139
|
+
previous_intent: previousIntent,
|
|
140
|
+
current_intent: config.received_intent,
|
|
141
|
+
constraints: thread.intent.constraints,
|
|
142
|
+
hop_number: sequence,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Calculate cumulative hash
|
|
146
|
+
const previousHash = sequence === 1
|
|
147
|
+
? thread.weave_signature
|
|
148
|
+
: thread.hops[thread.hops.length - 1].cumulative_hash;
|
|
149
|
+
|
|
150
|
+
const hopData = {
|
|
151
|
+
hopId,
|
|
152
|
+
agent: config.agent,
|
|
153
|
+
received_intent: config.received_intent,
|
|
154
|
+
actions: config.actions,
|
|
155
|
+
timestamp: now,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const hopSignature = this.weaveSignature.signHop(hopData, previousHash);
|
|
159
|
+
const cumulativeHash = this.weaveSignature.computeCumulativeHash(previousHash, hopSignature);
|
|
160
|
+
|
|
161
|
+
const hop: ThreadHop = {
|
|
162
|
+
sequence,
|
|
163
|
+
hop_id: hopId,
|
|
164
|
+
agent: config.agent,
|
|
165
|
+
received_intent: config.received_intent,
|
|
166
|
+
received_intent_hash: this.hashContent(config.received_intent),
|
|
167
|
+
intent_preserved: driftAnalysis.verdict === 'aligned' || driftAnalysis.verdict === 'minor_drift',
|
|
168
|
+
intent_drift: driftAnalysis,
|
|
169
|
+
actions: config.actions,
|
|
170
|
+
language_analysis: languageAnalysis,
|
|
171
|
+
security_scan: config.security_scan,
|
|
172
|
+
sandbox_result: config.sandbox_result ? {
|
|
173
|
+
sandbox_id: config.sandbox_result.sandbox_id,
|
|
174
|
+
result_id: config.sandbox_result.result_id,
|
|
175
|
+
status: config.sandbox_result.status as 'safe' | 'review' | 'blocked',
|
|
176
|
+
summary: config.sandbox_result.summary,
|
|
177
|
+
} : undefined,
|
|
178
|
+
hop_signature: hopSignature,
|
|
179
|
+
cumulative_hash: cumulativeHash,
|
|
180
|
+
started_at: now,
|
|
181
|
+
completed_at: now,
|
|
182
|
+
duration_ms: 0,
|
|
183
|
+
status: 'success',
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// Update thread
|
|
187
|
+
thread.hops.push(hop);
|
|
188
|
+
thread.weave_signature = cumulativeHash;
|
|
189
|
+
thread.updated_at = now;
|
|
190
|
+
|
|
191
|
+
// Check for violations
|
|
192
|
+
if (driftAnalysis.verdict === 'violated') {
|
|
193
|
+
thread.status = 'violated';
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Update merkle root
|
|
197
|
+
thread.merkle_root = this.computeMerkleRoot(thread.hops);
|
|
198
|
+
|
|
199
|
+
await this.storage.updateThread(thread);
|
|
200
|
+
await this.storage.addHop(config.thread_id, hop);
|
|
201
|
+
|
|
202
|
+
return hop;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Close a thread
|
|
207
|
+
*/
|
|
208
|
+
async closeThread(threadId: string, outcome: 'success' | 'failure' | 'abandoned'): Promise<Thread> {
|
|
209
|
+
const thread = await this.storage.getThread(threadId);
|
|
210
|
+
if (!thread) {
|
|
211
|
+
throw new ThreadError('Thread not found', { thread_id: threadId });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const now = new Date();
|
|
215
|
+
|
|
216
|
+
thread.status = outcome === 'success' ? 'complete' :
|
|
217
|
+
outcome === 'failure' ? 'violated' : 'abandoned';
|
|
218
|
+
thread.closed_at = now;
|
|
219
|
+
thread.updated_at = now;
|
|
220
|
+
|
|
221
|
+
// Final merkle root
|
|
222
|
+
thread.merkle_root = this.computeMerkleRoot(thread.hops);
|
|
223
|
+
|
|
224
|
+
await this.storage.updateThread(thread);
|
|
225
|
+
|
|
226
|
+
return thread;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get a thread
|
|
231
|
+
*/
|
|
232
|
+
async getThread(threadId: string): Promise<Thread | null> {
|
|
233
|
+
return this.storage.getThread(threadId);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* List threads
|
|
238
|
+
*/
|
|
239
|
+
async listThreads(filters?: {
|
|
240
|
+
status?: ThreadStatus;
|
|
241
|
+
origin_type?: ThreadOrigin['type'];
|
|
242
|
+
origin_identity?: string;
|
|
243
|
+
since?: Date;
|
|
244
|
+
until?: Date;
|
|
245
|
+
limit?: number;
|
|
246
|
+
}): Promise<Thread[]> {
|
|
247
|
+
return this.storage.listThreads(filters);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Verify thread integrity
|
|
252
|
+
*/
|
|
253
|
+
async verifyThread(threadId: string): Promise<{
|
|
254
|
+
valid: boolean;
|
|
255
|
+
errors: string[];
|
|
256
|
+
verified_hops: number;
|
|
257
|
+
total_hops: number;
|
|
258
|
+
}> {
|
|
259
|
+
const thread = await this.storage.getThread(threadId);
|
|
260
|
+
if (!thread) {
|
|
261
|
+
return { valid: false, errors: ['Thread not found'], verified_hops: 0, total_hops: 0 };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const errors: string[] = [];
|
|
265
|
+
let verifiedHops = 0;
|
|
266
|
+
|
|
267
|
+
// Verify initial signature
|
|
268
|
+
const expectedInitial = this.weaveSignature.createInitial({
|
|
269
|
+
threadId: thread.id,
|
|
270
|
+
origin: thread.origin,
|
|
271
|
+
intent: thread.intent,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
let previousHash = expectedInitial;
|
|
275
|
+
|
|
276
|
+
// Verify each hop
|
|
277
|
+
for (let i = 0; i < thread.hops.length; i++) {
|
|
278
|
+
const hop = thread.hops[i];
|
|
279
|
+
|
|
280
|
+
// Verify sequence
|
|
281
|
+
if (hop.sequence !== i + 1) {
|
|
282
|
+
errors.push(`Hop ${i + 1} has incorrect sequence: ${hop.sequence}`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Verify hop signature
|
|
286
|
+
const hopData = {
|
|
287
|
+
hopId: hop.hop_id,
|
|
288
|
+
agent: hop.agent,
|
|
289
|
+
received_intent: hop.received_intent,
|
|
290
|
+
actions: hop.actions,
|
|
291
|
+
timestamp: hop.started_at,
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const expectedSignature = this.weaveSignature.signHop(hopData, previousHash);
|
|
295
|
+
if (hop.hop_signature !== expectedSignature) {
|
|
296
|
+
errors.push(`Hop ${i + 1} has invalid signature`);
|
|
297
|
+
} else {
|
|
298
|
+
verifiedHops++;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Verify cumulative hash
|
|
302
|
+
const expectedCumulative = this.weaveSignature.computeCumulativeHash(previousHash, hop.hop_signature);
|
|
303
|
+
if (hop.cumulative_hash !== expectedCumulative) {
|
|
304
|
+
errors.push(`Hop ${i + 1} has invalid cumulative hash`);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
previousHash = hop.cumulative_hash;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Verify final weave signature
|
|
311
|
+
if (thread.hops.length > 0) {
|
|
312
|
+
const lastHop = thread.hops[thread.hops.length - 1];
|
|
313
|
+
if (thread.weave_signature !== lastHop.cumulative_hash) {
|
|
314
|
+
errors.push('Thread weave signature does not match last hop');
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Verify merkle root
|
|
319
|
+
const expectedMerkle = this.computeMerkleRoot(thread.hops);
|
|
320
|
+
if (thread.merkle_root && thread.merkle_root !== expectedMerkle) {
|
|
321
|
+
errors.push('Thread merkle root is invalid');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
valid: errors.length === 0,
|
|
326
|
+
errors,
|
|
327
|
+
verified_hops: verifiedHops,
|
|
328
|
+
total_hops: thread.hops.length,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Get thread summary
|
|
334
|
+
*/
|
|
335
|
+
async getThreadSummary(threadId: string): Promise<{
|
|
336
|
+
id: string;
|
|
337
|
+
status: ThreadStatus;
|
|
338
|
+
origin: string;
|
|
339
|
+
intent_summary: string;
|
|
340
|
+
hop_count: number;
|
|
341
|
+
total_drift: number;
|
|
342
|
+
has_violations: boolean;
|
|
343
|
+
duration_ms: number;
|
|
344
|
+
merkle_root?: string;
|
|
345
|
+
} | null> {
|
|
346
|
+
const thread = await this.storage.getThread(threadId);
|
|
347
|
+
if (!thread) return null;
|
|
348
|
+
|
|
349
|
+
const totalDrift = thread.hops.reduce(
|
|
350
|
+
(sum, hop) => sum + (hop.intent_drift?.hop_drift || 0),
|
|
351
|
+
0
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
const hasViolations = thread.hops.some(
|
|
355
|
+
hop => hop.intent_drift?.verdict === 'violated' || hop.status !== 'success'
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
const durationMs = thread.closed_at
|
|
359
|
+
? thread.closed_at.getTime() - thread.created_at.getTime()
|
|
360
|
+
: Date.now() - thread.created_at.getTime();
|
|
361
|
+
|
|
362
|
+
return {
|
|
363
|
+
id: thread.id,
|
|
364
|
+
status: thread.status,
|
|
365
|
+
origin: `${thread.origin.type}:${thread.origin.identity}`,
|
|
366
|
+
intent_summary: thread.intent.raw.slice(0, 100),
|
|
367
|
+
hop_count: thread.hops.length,
|
|
368
|
+
total_drift: totalDrift,
|
|
369
|
+
has_violations: hasViolations,
|
|
370
|
+
duration_ms: durationMs,
|
|
371
|
+
merkle_root: thread.merkle_root,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// ============================================================================
|
|
376
|
+
// Private Methods
|
|
377
|
+
// ============================================================================
|
|
378
|
+
|
|
379
|
+
private generateThreadId(): string {
|
|
380
|
+
return `thr_${crypto.randomBytes(12).toString('hex')}`;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
private generateHopId(threadId: string, sequence: number): string {
|
|
384
|
+
return `hop_${crypto.randomBytes(8).toString('hex')}_${sequence}`;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
private hashContent(content: string): string {
|
|
388
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
private computeMerkleRoot(hops: ThreadHop[]): string {
|
|
392
|
+
if (hops.length === 0) {
|
|
393
|
+
return this.hashContent('empty');
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Get leaf hashes
|
|
397
|
+
let hashes = hops.map(hop => hop.hop_signature);
|
|
398
|
+
|
|
399
|
+
// Build tree
|
|
400
|
+
while (hashes.length > 1) {
|
|
401
|
+
const newLevel: string[] = [];
|
|
402
|
+
for (let i = 0; i < hashes.length; i += 2) {
|
|
403
|
+
if (i + 1 < hashes.length) {
|
|
404
|
+
newLevel.push(this.hashContent(hashes[i] + hashes[i + 1]));
|
|
405
|
+
} else {
|
|
406
|
+
newLevel.push(hashes[i]); // Odd one out
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
hashes = newLevel;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return hashes[0];
|
|
413
|
+
}
|
|
414
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dōmere - The Judge Protocol
|
|
3
|
+
* Weave Signature - Cryptographic thread verification
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as crypto from 'crypto';
|
|
7
|
+
import type { ThreadOrigin, ThreadIntent, AgentInfo, HopAction } from '../types.js';
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Weave Signature
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
export class WeaveSignature {
|
|
14
|
+
private algorithm: string = 'sha256';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create initial thread signature
|
|
18
|
+
*/
|
|
19
|
+
createInitial(data: {
|
|
20
|
+
threadId: string;
|
|
21
|
+
origin: ThreadOrigin;
|
|
22
|
+
intent: ThreadIntent;
|
|
23
|
+
}): string {
|
|
24
|
+
const canonical = this.canonicalize({
|
|
25
|
+
type: 'thread_init',
|
|
26
|
+
thread_id: data.threadId,
|
|
27
|
+
origin: {
|
|
28
|
+
type: data.origin.type,
|
|
29
|
+
identity: data.origin.identity,
|
|
30
|
+
timestamp: data.origin.timestamp.toISOString(),
|
|
31
|
+
},
|
|
32
|
+
intent: {
|
|
33
|
+
hash: data.intent.hash,
|
|
34
|
+
classification: data.intent.classification,
|
|
35
|
+
constraints: data.intent.constraints,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return this.hash(canonical);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Sign a hop
|
|
44
|
+
*/
|
|
45
|
+
signHop(data: {
|
|
46
|
+
hopId: string;
|
|
47
|
+
agent: AgentInfo;
|
|
48
|
+
received_intent: string;
|
|
49
|
+
actions: HopAction[];
|
|
50
|
+
timestamp: Date;
|
|
51
|
+
}, previousHash: string): string {
|
|
52
|
+
const canonical = this.canonicalize({
|
|
53
|
+
type: 'hop',
|
|
54
|
+
hop_id: data.hopId,
|
|
55
|
+
previous_hash: previousHash,
|
|
56
|
+
agent: {
|
|
57
|
+
id: data.agent.id,
|
|
58
|
+
type: data.agent.type,
|
|
59
|
+
},
|
|
60
|
+
received_intent_hash: this.hash(data.received_intent),
|
|
61
|
+
actions_hash: this.hashActions(data.actions),
|
|
62
|
+
timestamp: data.timestamp.toISOString(),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return this.hash(canonical);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Compute cumulative hash
|
|
70
|
+
*/
|
|
71
|
+
computeCumulativeHash(previousHash: string, currentSignature: string): string {
|
|
72
|
+
return this.hash(`${previousHash}:${currentSignature}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Verify a signature chain
|
|
77
|
+
*/
|
|
78
|
+
verifyChain(signatures: string[]): boolean {
|
|
79
|
+
if (signatures.length < 2) return true;
|
|
80
|
+
const unique = new Set(signatures);
|
|
81
|
+
return unique.size === signatures.length;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Generate Merkle root from hop signatures
|
|
86
|
+
*/
|
|
87
|
+
generateMerkleRoot(signatures: string[]): string {
|
|
88
|
+
if (signatures.length === 0) {
|
|
89
|
+
return this.hash('empty');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let hashes = [...signatures];
|
|
93
|
+
|
|
94
|
+
while (hashes.length > 1) {
|
|
95
|
+
const newLevel: string[] = [];
|
|
96
|
+
for (let i = 0; i < hashes.length; i += 2) {
|
|
97
|
+
if (i + 1 < hashes.length) {
|
|
98
|
+
newLevel.push(this.hash(hashes[i] + hashes[i + 1]));
|
|
99
|
+
} else {
|
|
100
|
+
newLevel.push(hashes[i]);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
hashes = newLevel;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return hashes[0];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Generate Merkle proof for a specific leaf
|
|
111
|
+
*/
|
|
112
|
+
generateMerkleProof(signatures: string[], leafIndex: number): string[] {
|
|
113
|
+
if (leafIndex < 0 || leafIndex >= signatures.length) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const proof: string[] = [];
|
|
118
|
+
let hashes = [...signatures];
|
|
119
|
+
let index = leafIndex;
|
|
120
|
+
|
|
121
|
+
while (hashes.length > 1) {
|
|
122
|
+
const newLevel: string[] = [];
|
|
123
|
+
|
|
124
|
+
for (let i = 0; i < hashes.length; i += 2) {
|
|
125
|
+
if (i + 1 < hashes.length) {
|
|
126
|
+
if (i === index || i + 1 === index) {
|
|
127
|
+
const siblingIndex = i === index ? i + 1 : i;
|
|
128
|
+
proof.push(hashes[siblingIndex]);
|
|
129
|
+
}
|
|
130
|
+
newLevel.push(this.hash(hashes[i] + hashes[i + 1]));
|
|
131
|
+
} else {
|
|
132
|
+
newLevel.push(hashes[i]);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
index = Math.floor(index / 2);
|
|
137
|
+
hashes = newLevel;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return proof;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Verify Merkle proof
|
|
145
|
+
*/
|
|
146
|
+
verifyMerkleProof(
|
|
147
|
+
leaf: string,
|
|
148
|
+
proof: string[],
|
|
149
|
+
root: string,
|
|
150
|
+
leafIndex: number
|
|
151
|
+
): boolean {
|
|
152
|
+
let hash = leaf;
|
|
153
|
+
let index = leafIndex;
|
|
154
|
+
|
|
155
|
+
for (const sibling of proof) {
|
|
156
|
+
if (index % 2 === 0) {
|
|
157
|
+
hash = this.hash(hash + sibling);
|
|
158
|
+
} else {
|
|
159
|
+
hash = this.hash(sibling + hash);
|
|
160
|
+
}
|
|
161
|
+
index = Math.floor(index / 2);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return hash === root;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Create a thread summary hash for anchoring
|
|
169
|
+
*/
|
|
170
|
+
createAnchorHash(data: {
|
|
171
|
+
threadId: string;
|
|
172
|
+
merkleRoot: string;
|
|
173
|
+
hopCount: number;
|
|
174
|
+
intentHash: string;
|
|
175
|
+
compliant: boolean;
|
|
176
|
+
closedAt: Date;
|
|
177
|
+
}): string {
|
|
178
|
+
const canonical = this.canonicalize({
|
|
179
|
+
type: 'anchor',
|
|
180
|
+
thread_id: data.threadId,
|
|
181
|
+
merkle_root: data.merkleRoot,
|
|
182
|
+
hop_count: data.hopCount,
|
|
183
|
+
intent_hash: data.intentHash,
|
|
184
|
+
compliant: data.compliant,
|
|
185
|
+
closed_at: data.closedAt.toISOString(),
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
return this.hash(canonical);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private hash(data: string): string {
|
|
192
|
+
return crypto.createHash(this.algorithm).update(data).digest('hex');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private canonicalize(obj: unknown): string {
|
|
196
|
+
return JSON.stringify(obj, Object.keys(obj as object).sort());
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private hashActions(actions: HopAction[]): string {
|
|
200
|
+
const actionStrings = actions.map(a =>
|
|
201
|
+
`${a.type}:${a.target || ''}:${a.description}`
|
|
202
|
+
);
|
|
203
|
+
return this.hash(actionStrings.join('|'));
|
|
204
|
+
}
|
|
205
|
+
}
|