@jungjaehoon/mama-core 1.8.1 → 2.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/dist/agent-graph/alias-write.js +3 -0
- package/dist/agent-graph/entity-resolve.js +3 -7
- package/dist/agent-graph/graph-query.js +5 -4
- package/dist/agent-graph/types.d.ts +30 -0
- package/dist/cases/store.d.ts +0 -4
- package/dist/cases/store.js +0 -66
- package/dist/cases/wiki-page-index.d.ts +0 -13
- package/dist/cases/wiki-page-index.js +0 -153
- package/dist/connectors/event-index.d.ts +1 -22
- package/dist/connectors/event-index.js +0 -157
- package/dist/context-compile/channel-grant.d.ts +46 -0
- package/dist/context-compile/channel-grant.js +66 -0
- package/dist/context-compile/compiler.js +5 -2
- package/dist/context-compile/index.d.ts +1 -0
- package/dist/context-compile/index.js +1 -0
- package/dist/context-compile/packet-store.d.ts +0 -1
- package/dist/context-compile/packet-store.js +0 -16
- package/dist/context-compile/source-readers.d.ts +1 -0
- package/dist/context-compile/source-readers.js +156 -23
- package/dist/context-compile/types.d.ts +17 -0
- package/dist/context-compile/visibility.js +28 -4
- package/dist/db-adapter/node-sqlite-adapter.js +4 -0
- package/dist/db-manager.d.ts +0 -13
- package/dist/db-manager.js +0 -66
- package/dist/decision-formatter.d.ts +0 -11
- package/dist/decision-formatter.js +0 -122
- package/dist/decision-tracker.d.ts +0 -12
- package/dist/decision-tracker.js +0 -50
- package/dist/edges/ref-validation.d.ts +1 -2
- package/dist/edges/ref-validation.js +33 -13
- package/dist/edges/store.d.ts +1 -3
- package/dist/edges/store.js +0 -214
- package/dist/edges/types.d.ts +10 -0
- package/dist/embedding-client.d.ts +0 -17
- package/dist/embedding-client.js +0 -80
- package/dist/embedding-server/mobile/auth.d.ts +1 -10
- package/dist/embedding-server/mobile/auth.js +0 -15
- package/dist/embeddings.d.ts +0 -12
- package/dist/embeddings.js +0 -68
- package/dist/entities/audit-metrics.d.ts +0 -5
- package/dist/entities/audit-metrics.js +0 -4
- package/dist/entities/candidate-generator.d.ts +1 -2
- package/dist/entities/candidate-generator.js +0 -412
- package/dist/entities/entity-list.d.ts +0 -1
- package/dist/entities/entity-list.js +0 -96
- package/dist/entities/entity-orphan-list.d.ts +0 -9
- package/dist/entities/entity-orphan-list.js +0 -88
- package/dist/entities/entity-search.d.ts +0 -1
- package/dist/entities/entity-search.js +0 -147
- package/dist/entities/errors.d.ts +0 -10
- package/dist/entities/errors.js +1 -25
- package/dist/entities/lineage-store.d.ts +0 -23
- package/dist/entities/lineage-store.js +0 -104
- package/dist/entities/policy-store.d.ts +0 -9
- package/dist/entities/policy-store.js +0 -188
- package/dist/entities/provenance-query.d.ts +0 -1
- package/dist/entities/provenance-query.js +0 -164
- package/dist/entities/resolution-engine.d.ts +1 -8
- package/dist/entities/resolution-engine.js +0 -66
- package/dist/entities/rollback-preview.d.ts +0 -4
- package/dist/entities/rollback-preview.js +0 -232
- package/dist/entities/store.d.ts +1 -10
- package/dist/entities/store.js +0 -49
- package/dist/index.d.ts +15 -31
- package/dist/index.js +4 -46
- package/dist/mama-api.d.ts +19 -1
- package/dist/mama-api.js +97 -2
- package/dist/memory/api.d.ts +9 -0
- package/dist/memory/api.js +68 -4
- package/dist/memory/event-store.d.ts +0 -1
- package/dist/memory/event-store.js +0 -15
- package/dist/memory/finding-store.d.ts +0 -1
- package/dist/memory/finding-store.js +0 -12
- package/dist/memory/scope-backfill.d.ts +0 -3
- package/dist/memory/scope-backfill.js +0 -74
- package/dist/memory/scope-store.d.ts +0 -1
- package/dist/memory/scope-store.js +0 -4
- package/dist/memory/truth-store.d.ts +0 -3
- package/dist/memory/truth-store.js +0 -22
- package/dist/memory/types.d.ts +0 -1
- package/dist/memory/types.js +0 -13
- package/dist/memory-store.d.ts +0 -16
- package/dist/memory-store.js +0 -51
- package/dist/ollama-client.d.ts +0 -9
- package/dist/ollama-client.js +0 -125
- package/dist/outcome-tracker.d.ts +0 -14
- package/dist/outcome-tracker.js +0 -44
- package/dist/progress-indicator.d.ts +0 -4
- package/dist/progress-indicator.js +0 -7
- package/dist/provenance/source-ref.d.ts +0 -9
- package/dist/provenance/source-ref.js +0 -145
- package/dist/query-intent.d.ts +0 -4
- package/dist/query-intent.js +0 -30
- package/dist/relevance-scorer.d.ts +0 -9
- package/dist/relevance-scorer.js +0 -81
- package/dist/search/feedback-store.d.ts +0 -1
- package/dist/search/feedback-store.js +0 -4
- package/dist/search/ranker-rescore.d.ts +0 -3
- package/dist/search/ranker-rescore.js +0 -27
- package/dist/search/ranker-trainer.d.ts +0 -8
- package/dist/search/ranker-trainer.js +0 -376
- package/dist/tier-validator.d.ts +0 -12
- package/dist/tier-validator.js +0 -56
- package/package.json +1 -10
- package/dist/cases/case-links.d.ts +0 -116
- package/dist/cases/case-links.js +0 -794
- package/dist/cases/composition-overrides.d.ts +0 -90
- package/dist/cases/composition-overrides.js +0 -724
- package/dist/cases/corrections.d.ts +0 -156
- package/dist/cases/corrections.js +0 -1045
- package/dist/cases/freshness.d.ts +0 -57
- package/dist/cases/freshness.js +0 -283
- package/dist/cases/live-state.d.ts +0 -48
- package/dist/cases/live-state.js +0 -256
- package/dist/cases/membership-explain.d.ts +0 -56
- package/dist/cases/membership-explain.js +0 -140
- package/dist/cases/membership-matcher.d.ts +0 -23
- package/dist/cases/membership-matcher.js +0 -193
- package/dist/cases/merge-split.d.ts +0 -61
- package/dist/cases/merge-split.js +0 -368
- package/dist/cases/role-inference.d.ts +0 -10
- package/dist/cases/role-inference.js +0 -85
- package/dist/cases/sqlite-transaction.d.ts +0 -10
- package/dist/cases/sqlite-transaction.js +0 -62
- package/dist/cases/target-ref.d.ts +0 -25
- package/dist/cases/target-ref.js +0 -22
- package/dist/cases/tombstone-sweeper.d.ts +0 -13
- package/dist/cases/tombstone-sweeper.js +0 -128
- package/dist/entities/entity-impact.d.ts +0 -41
- package/dist/entities/entity-impact.js +0 -140
- package/dist/entities/exact-merge-backfill.d.ts +0 -11
- package/dist/entities/exact-merge-backfill.js +0 -161
- package/dist/entities/lineage-backfill.d.ts +0 -12
- package/dist/entities/lineage-backfill.js +0 -101
- package/dist/memory-inject.d.ts +0 -24
- package/dist/memory-inject.js +0 -116
|
@@ -17,11 +17,9 @@ exports.formatContext = formatContext;
|
|
|
17
17
|
exports.formatLegacyContext = formatLegacyContext;
|
|
18
18
|
exports.ensureTokenBudget = ensureTokenBudget;
|
|
19
19
|
exports.estimateTokens = estimateTokens;
|
|
20
|
-
exports.formatInstantAnswer = formatInstantAnswer;
|
|
21
20
|
exports.extractQuickAnswer = extractQuickAnswer;
|
|
22
21
|
exports.extractCodeExample = extractCodeExample;
|
|
23
22
|
exports.formatTrustContext = formatTrustContext;
|
|
24
|
-
exports.formatTeaser = formatTeaser;
|
|
25
23
|
exports.formatRecall = formatRecall;
|
|
26
24
|
exports.formatList = formatList;
|
|
27
25
|
const relevance_scorer_js_1 = require("./relevance-scorer.js");
|
|
@@ -362,42 +360,6 @@ function getOutcomeEmoji(outcome) {
|
|
|
362
360
|
};
|
|
363
361
|
return outcome ? emojiMap[outcome] || '' : '';
|
|
364
362
|
}
|
|
365
|
-
/**
|
|
366
|
-
* Format context in Claude-friendly Instant Answer format
|
|
367
|
-
*
|
|
368
|
-
* Story 014.7.10: Claude-Friendly Context Formatting
|
|
369
|
-
* AC #1: Instant Answer format with trust components
|
|
370
|
-
*/
|
|
371
|
-
function formatInstantAnswer(decision, options = {}) {
|
|
372
|
-
const { maxTokens = 500 } = options;
|
|
373
|
-
if (!decision) {
|
|
374
|
-
return null;
|
|
375
|
-
}
|
|
376
|
-
// Extract quick answer (first line of decision)
|
|
377
|
-
const quickAnswer = extractQuickAnswer(decision);
|
|
378
|
-
if (!quickAnswer) {
|
|
379
|
-
return null;
|
|
380
|
-
}
|
|
381
|
-
// Extract code example (from reasoning)
|
|
382
|
-
const codeExample = extractCodeExample(decision);
|
|
383
|
-
// Format trust context
|
|
384
|
-
const trustSection = formatTrustContext(typeof decision.trust_context === 'string'
|
|
385
|
-
? parseTrustContext(decision.trust_context)
|
|
386
|
-
: decision.trust_context);
|
|
387
|
-
// Build output
|
|
388
|
-
let output = `⚡ INSTANT ANSWER\n\n${quickAnswer}`;
|
|
389
|
-
if (codeExample) {
|
|
390
|
-
output += `\n\n${codeExample}`;
|
|
391
|
-
}
|
|
392
|
-
if (trustSection) {
|
|
393
|
-
output += `\n\n${trustSection}`;
|
|
394
|
-
}
|
|
395
|
-
// Token budget check
|
|
396
|
-
if (estimateTokens(output) > maxTokens) {
|
|
397
|
-
output = truncateToFit(output, maxTokens);
|
|
398
|
-
}
|
|
399
|
-
return output;
|
|
400
|
-
}
|
|
401
363
|
/**
|
|
402
364
|
* Extract quick answer from decision
|
|
403
365
|
*/
|
|
@@ -500,50 +462,6 @@ function formatTrustContext(trustCtx) {
|
|
|
500
462
|
lines.push('━'.repeat(40));
|
|
501
463
|
return lines.join('\n');
|
|
502
464
|
}
|
|
503
|
-
/**
|
|
504
|
-
* Truncate output to fit token budget
|
|
505
|
-
*/
|
|
506
|
-
function truncateToFit(output, maxTokens) {
|
|
507
|
-
const sections = output.split('\n\n');
|
|
508
|
-
const quickAnswer = sections[0];
|
|
509
|
-
let result = quickAnswer;
|
|
510
|
-
let remainingTokens = maxTokens - estimateTokens(result);
|
|
511
|
-
// Try to add code example
|
|
512
|
-
const codeIndex = sections.findIndex((s) => s.startsWith('```'));
|
|
513
|
-
if (codeIndex > 0) {
|
|
514
|
-
const codeSection = sections[codeIndex];
|
|
515
|
-
const codeTokens = estimateTokens(codeSection);
|
|
516
|
-
if (codeTokens <= remainingTokens) {
|
|
517
|
-
result += '\n\n' + codeSection;
|
|
518
|
-
remainingTokens -= codeTokens;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
// Try to add trust section (trimmed if needed)
|
|
522
|
-
const trustIndex = sections.findIndex((s) => s.startsWith('━'));
|
|
523
|
-
if (trustIndex > 0 && remainingTokens > 50) {
|
|
524
|
-
const trustSection = sections[trustIndex];
|
|
525
|
-
const trustTokens = estimateTokens(trustSection);
|
|
526
|
-
if (trustTokens <= remainingTokens) {
|
|
527
|
-
result += '\n\n' + trustSection;
|
|
528
|
-
}
|
|
529
|
-
else {
|
|
530
|
-
const trustLines = trustSection.split('\n');
|
|
531
|
-
let trimmed = trustLines[0] + '\n' + trustLines[1] + '\n';
|
|
532
|
-
for (let i = 2; i < trustLines.length - 1; i++) {
|
|
533
|
-
const line = trustLines[i] + '\n';
|
|
534
|
-
if (estimateTokens(trimmed + line) <= remainingTokens - 10) {
|
|
535
|
-
trimmed += line;
|
|
536
|
-
}
|
|
537
|
-
else {
|
|
538
|
-
break;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
trimmed += trustLines[trustLines.length - 1];
|
|
542
|
-
result += '\n\n' + trimmed;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
return result;
|
|
546
|
-
}
|
|
547
465
|
/**
|
|
548
466
|
* Format multiple decisions as Google-style search results
|
|
549
467
|
*/
|
|
@@ -578,46 +496,6 @@ function formatTeaserList(decisions, topN = 3) {
|
|
|
578
496
|
}
|
|
579
497
|
return output;
|
|
580
498
|
}
|
|
581
|
-
/**
|
|
582
|
-
* Format decision as curiosity-inducing teaser
|
|
583
|
-
*/
|
|
584
|
-
function formatTeaser(decision) {
|
|
585
|
-
if (!decision) {
|
|
586
|
-
return null;
|
|
587
|
-
}
|
|
588
|
-
const timeAgo = calculateDuration(getDecisionTimestamp(decision));
|
|
589
|
-
// Extract preview (first 60 chars)
|
|
590
|
-
const preview = decision.decision.length > 60 ? decision.decision.substring(0, 60) + '...' : decision.decision;
|
|
591
|
-
// Extract files from trust_context or show generic
|
|
592
|
-
let files = 'Multiple files';
|
|
593
|
-
const trustCtx = typeof decision.trust_context === 'string'
|
|
594
|
-
? parseTrustContext(decision.trust_context)
|
|
595
|
-
: decision.trust_context;
|
|
596
|
-
if (trustCtx?.source?.file) {
|
|
597
|
-
const fileStr = trustCtx.source.file;
|
|
598
|
-
const fileList = fileStr.split(',').map((f) => f.trim());
|
|
599
|
-
if (fileList.length === 1) {
|
|
600
|
-
files = fileList[0];
|
|
601
|
-
}
|
|
602
|
-
else if (fileList.length === 2) {
|
|
603
|
-
files = fileList.join(', ');
|
|
604
|
-
}
|
|
605
|
-
else {
|
|
606
|
-
files = `${fileList[0]}, ${fileList[1]} (+${fileList.length - 2})`;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
const teaser = `
|
|
610
|
-
💡 MAMA has related info
|
|
611
|
-
|
|
612
|
-
📚 Topic: ${decision.topic}
|
|
613
|
-
📖 Preview: "${preview}"
|
|
614
|
-
📁 Files: ${files}
|
|
615
|
-
⏰ Updated: ${timeAgo}
|
|
616
|
-
|
|
617
|
-
🔍 Read more: mama.recall('${decision.topic}')
|
|
618
|
-
`.trim();
|
|
619
|
-
return teaser;
|
|
620
|
-
}
|
|
621
499
|
/**
|
|
622
500
|
* Format mama.recall() results in readable format
|
|
623
501
|
*/
|
|
@@ -163,18 +163,6 @@ export declare function createEdgesFromReasoning(fromId: string, reasoning: stri
|
|
|
163
163
|
created: number;
|
|
164
164
|
failed: number;
|
|
165
165
|
}>;
|
|
166
|
-
/**
|
|
167
|
-
* Get supersedes chain depth for a topic
|
|
168
|
-
*
|
|
169
|
-
* Story 2.2: Calculate how many times a topic has been superseded
|
|
170
|
-
*
|
|
171
|
-
* @param topic - Decision topic
|
|
172
|
-
* @returns Chain depth and decision IDs
|
|
173
|
-
*/
|
|
174
|
-
export declare function getSupersededChainDepth(topic: string): Promise<{
|
|
175
|
-
depth: number;
|
|
176
|
-
chain: string[];
|
|
177
|
-
}>;
|
|
178
166
|
/**
|
|
179
167
|
* Learn Decision Function (Main API)
|
|
180
168
|
*
|
package/dist/decision-tracker.js
CHANGED
|
@@ -25,7 +25,6 @@ exports.calculateCombinedConfidence = calculateCombinedConfidence;
|
|
|
25
25
|
exports.detectRefinement = detectRefinement;
|
|
26
26
|
exports.parseReasoningForRelationships = parseReasoningForRelationships;
|
|
27
27
|
exports.createEdgesFromReasoning = createEdgesFromReasoning;
|
|
28
|
-
exports.getSupersededChainDepth = getSupersededChainDepth;
|
|
29
28
|
exports.learnDecision = learnDecision;
|
|
30
29
|
exports.updateConfidence = updateConfidence;
|
|
31
30
|
const debug_logger_js_1 = require("./debug-logger.js");
|
|
@@ -305,55 +304,6 @@ async function createEdgesFromReasoning(fromId, reasoning) {
|
|
|
305
304
|
}
|
|
306
305
|
return { created, failed };
|
|
307
306
|
}
|
|
308
|
-
/**
|
|
309
|
-
* Get supersedes chain depth for a topic
|
|
310
|
-
*
|
|
311
|
-
* Story 2.2: Calculate how many times a topic has been superseded
|
|
312
|
-
*
|
|
313
|
-
* @param topic - Decision topic
|
|
314
|
-
* @returns Chain depth and decision IDs
|
|
315
|
-
*/
|
|
316
|
-
async function getSupersededChainDepth(topic) {
|
|
317
|
-
const adapter = (0, memory_store_js_1.getAdapter)();
|
|
318
|
-
const chain = [];
|
|
319
|
-
try {
|
|
320
|
-
// Start from the latest decision (superseded_by IS NULL)
|
|
321
|
-
let stmt = adapter.prepare(`
|
|
322
|
-
SELECT id, supersedes FROM decisions
|
|
323
|
-
WHERE topic = ? AND superseded_by IS NULL
|
|
324
|
-
ORDER BY created_at DESC
|
|
325
|
-
LIMIT 1
|
|
326
|
-
`);
|
|
327
|
-
let current = stmt.get(topic);
|
|
328
|
-
if (!current) {
|
|
329
|
-
return { depth: 0, chain: [] };
|
|
330
|
-
}
|
|
331
|
-
chain.push(current.id);
|
|
332
|
-
// Walk back through supersedes chain with cycle detection
|
|
333
|
-
const visited = new Set([current.id]);
|
|
334
|
-
const MAX_CHAIN_DEPTH = 1000; // Safety limit
|
|
335
|
-
while (current && current.supersedes && chain.length < MAX_CHAIN_DEPTH) {
|
|
336
|
-
// Cycle detection: stop if we've seen this ID before
|
|
337
|
-
if (visited.has(current.supersedes)) {
|
|
338
|
-
break;
|
|
339
|
-
}
|
|
340
|
-
stmt = adapter.prepare('SELECT id, supersedes FROM decisions WHERE id = ?');
|
|
341
|
-
current = stmt.get(current.supersedes);
|
|
342
|
-
if (current) {
|
|
343
|
-
visited.add(current.id);
|
|
344
|
-
chain.push(current.id);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
return {
|
|
348
|
-
depth: chain.length - 1, // depth = number of supersedes edges
|
|
349
|
-
chain: chain.reverse(), // oldest to newest
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
catch (error) {
|
|
353
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
354
|
-
throw new Error(`Failed to get supersedes chain: ${message}`);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
307
|
// ════════════════════════════════════════════════════════════════════════════
|
|
358
308
|
// NOTE: Auto-link functions REMOVED in v1.2.0
|
|
359
309
|
//
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { DatabaseAdapter } from '../db-manager.js';
|
|
2
|
-
import type { ListVisibleTwinEdgesOptions, TwinEdgeRecord, TwinRef,
|
|
2
|
+
import type { ListVisibleTwinEdgesOptions, TwinEdgeRecord, TwinRef, TwinVisibility } from './types.js';
|
|
3
3
|
type TwinRefVisibilityAdapter = Pick<DatabaseAdapter, 'prepare'>;
|
|
4
4
|
export declare function assertTwinRefsVisible(adapter: TwinRefVisibilityAdapter, refs: readonly TwinRef[], visibility?: TwinVisibility): void;
|
|
5
|
-
export declare function assertTwinRefsVisibleToScopes(adapter: TwinRefVisibilityAdapter, refs: readonly TwinRef[], scopes?: TwinScopeRef[]): void;
|
|
6
5
|
export declare function listVisibleTwinEdgesForRefs(adapter: TwinRefVisibilityAdapter, refs: readonly TwinRef[], options?: ListVisibleTwinEdgesOptions): TwinEdgeRecord[];
|
|
7
6
|
export {};
|
|
8
7
|
//# sourceMappingURL=ref-validation.d.ts.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.assertTwinRefsVisible = assertTwinRefsVisible;
|
|
4
|
-
exports.assertTwinRefsVisibleToScopes = assertTwinRefsVisibleToScopes;
|
|
5
4
|
exports.listVisibleTwinEdgesForRefs = listVisibleTwinEdgesForRefs;
|
|
6
5
|
const store_js_1 = require("./store.js");
|
|
6
|
+
const channel_grant_js_1 = require("../context-compile/channel-grant.js");
|
|
7
7
|
const TABLE_COLUMN_PRAGMAS = {
|
|
8
8
|
decisions: 'PRAGMA table_info(decisions)',
|
|
9
9
|
case_truth: 'PRAGMA table_info(case_truth)',
|
|
@@ -21,9 +21,6 @@ function hasScopes(scopes) {
|
|
|
21
21
|
function hasProjectRefs(projectRefs) {
|
|
22
22
|
return Array.isArray(projectRefs) && projectRefs.length > 0;
|
|
23
23
|
}
|
|
24
|
-
function visibilityFromScopes(scopes) {
|
|
25
|
-
return { scopes: scopes ? [...scopes] : undefined };
|
|
26
|
-
}
|
|
27
24
|
function asOfMs(visibility) {
|
|
28
25
|
return typeof visibility.asOfMs === 'number' ? visibility.asOfMs : null;
|
|
29
26
|
}
|
|
@@ -177,6 +174,30 @@ function isCaseVisible(adapter, id, visibility) {
|
|
|
177
174
|
}
|
|
178
175
|
return false;
|
|
179
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* The time bound, shared by both branches so a grant cannot skip it.
|
|
179
|
+
*
|
|
180
|
+
* DELIBERATELY STRICTER THAN THE READER, and a review flagged the difference as a
|
|
181
|
+
* divergence between two delegates of one rule. It is not. A row with no usable timestamp
|
|
182
|
+
* is refused here even when no bound is set, while the reader - whose clause is on
|
|
183
|
+
* `COALESCE(event_datetime, source_timestamp_ms)` - leaves such a row alone when there is
|
|
184
|
+
* no bound to apply.
|
|
185
|
+
*
|
|
186
|
+
* The direction is what makes it correct: this gate guards refs reached INDIRECTLY, through
|
|
187
|
+
* a seed the caller named or an edge the graph walked. Refusing what cannot be shown to sit
|
|
188
|
+
* inside any window keeps the indirect path no wider than the direct one, which is the
|
|
189
|
+
* invariant the whole design rests on. Relaxing it to match the reader would have made
|
|
190
|
+
* citation reach further than reading for exactly the rows whose position in time is
|
|
191
|
+
* unknown. `fails closed for raw endpoints with blank timestamps` states the intent.
|
|
192
|
+
*/
|
|
193
|
+
function isRawWithinTime(row, visibility) {
|
|
194
|
+
const rawTs = row.event_datetime ?? row.source_timestamp_ms;
|
|
195
|
+
if (rawTs === null || rawTs === undefined || (typeof rawTs === 'string' && rawTs.trim() === '')) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
const eventTsMs = parseTimestamp(rawTs);
|
|
199
|
+
return eventTsMs !== null && eventTsMs >= 0 && isWithinVisibilityTime(eventTsMs, visibility);
|
|
200
|
+
}
|
|
180
201
|
function isRawVisible(adapter, id, visibility) {
|
|
181
202
|
const row = adapter
|
|
182
203
|
.prepare('SELECT * FROM connector_event_index WHERE event_index_id = ? LIMIT 1')
|
|
@@ -188,6 +209,13 @@ function isRawVisible(adapter, id, visibility) {
|
|
|
188
209
|
!visibility.connectors.includes(String(row.source_connector))) {
|
|
189
210
|
return false;
|
|
190
211
|
}
|
|
212
|
+
// The grant decides, and it decides the same way here as in the reader - one rule, taken
|
|
213
|
+
// from one place. The scope/project/tenant clauses below are the pre-grant rule and are
|
|
214
|
+
// skipped when a grant is present, exactly as the reader skips them, because applying
|
|
215
|
+
// both would make a cited ref satisfy a stricter test than a read one.
|
|
216
|
+
if (visibility.channels) {
|
|
217
|
+
return ((0, channel_grant_js_1.isChannelGranted)(String(row.source_connector), typeof row.channel === 'string' ? row.channel : null, visibility.channels) && isRawWithinTime(row, visibility));
|
|
218
|
+
}
|
|
191
219
|
if (hasProjectRefs(visibility.projectRefs) &&
|
|
192
220
|
!visibility.projectRefs.some((project) => project.id === row.project_id)) {
|
|
193
221
|
return false;
|
|
@@ -197,12 +225,7 @@ function isRawVisible(adapter, id, visibility) {
|
|
|
197
225
|
row.tenant_id !== visibility.tenantId) {
|
|
198
226
|
return false;
|
|
199
227
|
}
|
|
200
|
-
|
|
201
|
-
if (rawTs === null || rawTs === undefined || (typeof rawTs === 'string' && rawTs.trim() === '')) {
|
|
202
|
-
return false;
|
|
203
|
-
}
|
|
204
|
-
const eventTsMs = parseTimestamp(rawTs);
|
|
205
|
-
if (eventTsMs === null || eventTsMs < 0 || !isWithinVisibilityTime(eventTsMs, visibility)) {
|
|
228
|
+
if (!isRawWithinTime(row, visibility)) {
|
|
206
229
|
return false;
|
|
207
230
|
}
|
|
208
231
|
if (!hasScopes(visibility.scopes)) {
|
|
@@ -269,9 +292,6 @@ function assertTwinRefsVisible(adapter, refs, visibility = {}) {
|
|
|
269
292
|
}
|
|
270
293
|
}
|
|
271
294
|
}
|
|
272
|
-
function assertTwinRefsVisibleToScopes(adapter, refs, scopes) {
|
|
273
|
-
assertTwinRefsVisible(adapter, refs, visibilityFromScopes(scopes));
|
|
274
|
-
}
|
|
275
295
|
function listVisibleTwinEdgesForRefs(adapter, refs, options = {}) {
|
|
276
296
|
const edgeCache = new Map();
|
|
277
297
|
const edgeTypes = normalizeEdgeTypes(options.edgeTypes);
|
package/dist/edges/store.d.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import type { DatabaseAdapter } from '../db-manager.js';
|
|
2
|
-
import { type
|
|
2
|
+
import { type TwinEdgeRecord, type TwinRef } from './types.js';
|
|
3
3
|
type TwinEdgeReadAdapter = Pick<DatabaseAdapter, 'prepare'>;
|
|
4
|
-
type TwinEdgeAdapter = Pick<DatabaseAdapter, 'prepare' | 'transaction'>;
|
|
5
4
|
export declare function mapTwinEdgeRow(row: Record<string, unknown>): TwinEdgeRecord;
|
|
6
5
|
export declare function getTwinEdge(adapter: TwinEdgeReadAdapter, edgeId: string): TwinEdgeRecord | null;
|
|
7
6
|
export declare function listTwinEdgesForRefs(adapter: TwinEdgeReadAdapter, refs: readonly TwinRef[]): TwinEdgeRecord[];
|
|
8
|
-
export declare function insertTwinEdge(adapter: TwinEdgeAdapter, input: InsertTwinEdgeInput): TwinEdgeRecord;
|
|
9
7
|
export {};
|
|
10
8
|
//# sourceMappingURL=store.d.ts.map
|
package/dist/edges/store.js
CHANGED
|
@@ -3,31 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.mapTwinEdgeRow = mapTwinEdgeRow;
|
|
4
4
|
exports.getTwinEdge = getTwinEdge;
|
|
5
5
|
exports.listTwinEdgesForRefs = listTwinEdgesForRefs;
|
|
6
|
-
exports.insertTwinEdge = insertTwinEdge;
|
|
7
|
-
const node_crypto_1 = require("node:crypto");
|
|
8
|
-
const canonicalize_js_1 = require("../canonicalize.js");
|
|
9
6
|
const types_js_1 = require("./types.js");
|
|
10
7
|
const EDGE_TYPE_SET = new Set(types_js_1.TWIN_EDGE_TYPES);
|
|
11
8
|
const REF_KIND_SET = new Set(types_js_1.TWIN_REF_KINDS);
|
|
12
9
|
const SOURCE_SET = new Set(types_js_1.TWIN_EDGE_SOURCES);
|
|
13
|
-
const HUMAN_ACTOR_ROLES = new Set(['commander', 'configurator_elevated']);
|
|
14
|
-
const HUMAN_REASON_CLASSIFICATIONS = new Set([
|
|
15
|
-
'factual_correction',
|
|
16
|
-
'agent_inference_wrong',
|
|
17
|
-
'privacy_redaction',
|
|
18
|
-
'duplicate_merge',
|
|
19
|
-
'state_override',
|
|
20
|
-
'other',
|
|
21
|
-
]);
|
|
22
|
-
function sha256Hex(input) {
|
|
23
|
-
return (0, node_crypto_1.createHash)('sha256').update(input, 'utf8').digest('hex');
|
|
24
|
-
}
|
|
25
|
-
function sha256Buffer(input) {
|
|
26
|
-
return (0, node_crypto_1.createHash)('sha256').update(input, 'utf8').digest();
|
|
27
|
-
}
|
|
28
|
-
function generatedEdgeId() {
|
|
29
|
-
return `edge_${(0, node_crypto_1.randomUUID)().replace(/-/g, '')}`;
|
|
30
|
-
}
|
|
31
10
|
function nullableString(value) {
|
|
32
11
|
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
33
12
|
}
|
|
@@ -61,68 +40,6 @@ function normalizeRef(ref, field) {
|
|
|
61
40
|
id: requireNonEmptyString(ref.id, `${field}.id`),
|
|
62
41
|
};
|
|
63
42
|
}
|
|
64
|
-
function assertSubjectKindAllowed(ref) {
|
|
65
|
-
if (ref.kind === 'raw') {
|
|
66
|
-
throw new Error('twin_edges.subject_ref.kind cannot be raw');
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
function normalizeConfidence(value) {
|
|
70
|
-
if (value === undefined || value === null) {
|
|
71
|
-
return 1;
|
|
72
|
-
}
|
|
73
|
-
if (typeof value === 'number' && Number.isFinite(value) && value >= 0 && value <= 1) {
|
|
74
|
-
return value;
|
|
75
|
-
}
|
|
76
|
-
throw new Error(`twin_edges.confidence must be between 0 and 1: ${String(value)}`);
|
|
77
|
-
}
|
|
78
|
-
function hasDeterministicRule(value) {
|
|
79
|
-
return (value !== null &&
|
|
80
|
-
typeof value === 'object' &&
|
|
81
|
-
!Array.isArray(value) &&
|
|
82
|
-
typeof value.deterministic_rule === 'string' &&
|
|
83
|
-
value.deterministic_rule.length > 0);
|
|
84
|
-
}
|
|
85
|
-
function validateSourceMetadata(input, source) {
|
|
86
|
-
if (source === 'agent') {
|
|
87
|
-
requireNonEmptyString(input.agent_id, 'agent_id');
|
|
88
|
-
requireNonEmptyString(input.model_run_id, 'model_run_id');
|
|
89
|
-
requireNonEmptyString(input.envelope_hash, 'envelope_hash');
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
if (source === 'human') {
|
|
93
|
-
requireNonEmptyString(input.human_actor_id, 'human_actor_id');
|
|
94
|
-
const role = requireNonEmptyString(input.human_actor_role, 'human_actor_role');
|
|
95
|
-
if (!HUMAN_ACTOR_ROLES.has(role)) {
|
|
96
|
-
throw new Error(`Unsupported twin_edges.human_actor_role: ${role}`);
|
|
97
|
-
}
|
|
98
|
-
if (input.authority_scope_json === undefined || input.authority_scope_json === null) {
|
|
99
|
-
throw new Error('twin_edges.authority_scope_json is required for human edges');
|
|
100
|
-
}
|
|
101
|
-
const classification = requireNonEmptyString(input.reason_classification, 'reason_classification');
|
|
102
|
-
if (!HUMAN_REASON_CLASSIFICATIONS.has(classification)) {
|
|
103
|
-
throw new Error(`Unsupported twin_edges.reason_classification: ${classification}`);
|
|
104
|
-
}
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
if (!nullableString(input.reason_text) && !hasDeterministicRule(input.relation_attrs)) {
|
|
108
|
-
throw new Error('twin_edges.reason_text or relation_attrs.deterministic_rule is required for code edges');
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
function jsonField(value, field) {
|
|
112
|
-
if (value === undefined || value === null) {
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
try {
|
|
116
|
-
if (typeof value === 'string') {
|
|
117
|
-
return (0, canonicalize_js_1.canonicalizeJSON)(JSON.parse(value));
|
|
118
|
-
}
|
|
119
|
-
return (0, canonicalize_js_1.canonicalizeJSON)(value);
|
|
120
|
-
}
|
|
121
|
-
catch (error) {
|
|
122
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
123
|
-
throw new Error(`Invalid twin_edges.${field}: ${message}`);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
43
|
function parseJsonField(value, field, edgeId) {
|
|
127
44
|
if (value === null || value === undefined) {
|
|
128
45
|
return null;
|
|
@@ -148,62 +65,6 @@ function toBuffer(value) {
|
|
|
148
65
|
}
|
|
149
66
|
throw new Error('twin_edges.content_hash must be a 32-byte Buffer');
|
|
150
67
|
}
|
|
151
|
-
function exists(adapter, table, column, id) {
|
|
152
|
-
const row = adapter
|
|
153
|
-
.prepare(`SELECT 1 AS ok FROM ${table} WHERE ${column} = ? LIMIT 1`)
|
|
154
|
-
.get(id);
|
|
155
|
-
return Boolean(row?.ok);
|
|
156
|
-
}
|
|
157
|
-
function validateRefExists(adapter, ref, field) {
|
|
158
|
-
if (ref.kind === 'report') {
|
|
159
|
-
throw new Error(`${field} ${ref.kind}:${ref.id} is not supported by twin_edges yet`);
|
|
160
|
-
}
|
|
161
|
-
const found = (ref.kind === 'memory' && exists(adapter, 'decisions', 'id', ref.id)) ||
|
|
162
|
-
(ref.kind === 'case' && exists(adapter, 'case_truth', 'case_id', ref.id)) ||
|
|
163
|
-
(ref.kind === 'entity' && exists(adapter, 'entity_nodes', 'id', ref.id)) ||
|
|
164
|
-
(ref.kind === 'edge' && exists(adapter, 'twin_edges', 'edge_id', ref.id)) ||
|
|
165
|
-
(ref.kind === 'raw' && exists(adapter, 'connector_event_index', 'event_index_id', ref.id));
|
|
166
|
-
if (!found) {
|
|
167
|
-
throw new Error(`${field} not found: ${ref.kind}:${ref.id}`);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
function contentHashInput(input) {
|
|
171
|
-
return (0, canonicalize_js_1.canonicalizeJSON)(input);
|
|
172
|
-
}
|
|
173
|
-
function deriveEdgeIdempotencyKey(requestIdempotencyKey, contentHash) {
|
|
174
|
-
return `reqedge_${sha256Hex((0, canonicalize_js_1.canonicalizeJSON)({
|
|
175
|
-
request_idempotency_key: requestIdempotencyKey,
|
|
176
|
-
content_hash: contentHash.toString('hex'),
|
|
177
|
-
})).slice(0, 40)}`;
|
|
178
|
-
}
|
|
179
|
-
function selectByIdempotency(adapter, modelRunId, edgeIdempotencyKey) {
|
|
180
|
-
if (!edgeIdempotencyKey) {
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
183
|
-
if (!modelRunId) {
|
|
184
|
-
const row = adapter
|
|
185
|
-
.prepare(`
|
|
186
|
-
SELECT *
|
|
187
|
-
FROM twin_edges
|
|
188
|
-
WHERE model_run_id IS NULL
|
|
189
|
-
AND edge_idempotency_key = ?
|
|
190
|
-
ORDER BY created_at ASC, edge_id ASC
|
|
191
|
-
LIMIT 1
|
|
192
|
-
`)
|
|
193
|
-
.get(edgeIdempotencyKey);
|
|
194
|
-
return row ? mapTwinEdgeRow(row) : null;
|
|
195
|
-
}
|
|
196
|
-
const row = adapter
|
|
197
|
-
.prepare(`
|
|
198
|
-
SELECT *
|
|
199
|
-
FROM twin_edges
|
|
200
|
-
WHERE model_run_id = ?
|
|
201
|
-
AND edge_idempotency_key = ?
|
|
202
|
-
LIMIT 1
|
|
203
|
-
`)
|
|
204
|
-
.get(modelRunId, edgeIdempotencyKey);
|
|
205
|
-
return row ? mapTwinEdgeRow(row) : null;
|
|
206
|
-
}
|
|
207
68
|
function mapTwinEdgeRow(row) {
|
|
208
69
|
const edgeId = String(row.edge_id);
|
|
209
70
|
return {
|
|
@@ -265,79 +126,4 @@ function listTwinEdgesForRefs(adapter, refs) {
|
|
|
265
126
|
.all(...params);
|
|
266
127
|
return rows.map(mapTwinEdgeRow);
|
|
267
128
|
}
|
|
268
|
-
function insertTwinEdge(adapter, input) {
|
|
269
|
-
const edgeType = normalizeEdgeType(input.edge_type);
|
|
270
|
-
const subjectRef = normalizeRef(input.subject_ref, 'subject_ref');
|
|
271
|
-
const objectRef = normalizeRef(input.object_ref, 'object_ref');
|
|
272
|
-
assertSubjectKindAllowed(subjectRef);
|
|
273
|
-
const source = normalizeSource(input.source);
|
|
274
|
-
const confidence = normalizeConfidence(input.confidence);
|
|
275
|
-
validateSourceMetadata(input, source);
|
|
276
|
-
validateRefExists(adapter, subjectRef, 'subject_ref');
|
|
277
|
-
validateRefExists(adapter, objectRef, 'object_ref');
|
|
278
|
-
const relationAttrsJson = jsonField(input.relation_attrs, 'relation_attrs_json');
|
|
279
|
-
const evidenceRefsJson = jsonField(input.evidence_refs, 'evidence_refs_json');
|
|
280
|
-
const authorityScopeJson = jsonField(input.authority_scope_json, 'authority_scope_json');
|
|
281
|
-
const agentId = nullableString(input.agent_id);
|
|
282
|
-
const reasonText = nullableString(input.reason_text);
|
|
283
|
-
const envelopeHash = nullableString(input.envelope_hash);
|
|
284
|
-
const humanActorId = nullableString(input.human_actor_id);
|
|
285
|
-
const humanActorRole = nullableString(input.human_actor_role);
|
|
286
|
-
const reasonClassification = nullableString(input.reason_classification);
|
|
287
|
-
const requestIdempotencyKey = nullableString(input.request_idempotency_key);
|
|
288
|
-
const modelRunId = nullableString(input.model_run_id);
|
|
289
|
-
const contentHash = sha256Buffer(contentHashInput({
|
|
290
|
-
edge_type: edgeType,
|
|
291
|
-
subject_ref: subjectRef,
|
|
292
|
-
object_ref: objectRef,
|
|
293
|
-
relation_attrs_json: relationAttrsJson,
|
|
294
|
-
confidence,
|
|
295
|
-
source,
|
|
296
|
-
agent_id: agentId,
|
|
297
|
-
model_run_id: modelRunId,
|
|
298
|
-
envelope_hash: envelopeHash,
|
|
299
|
-
human_actor_id: humanActorId,
|
|
300
|
-
human_actor_role: humanActorRole,
|
|
301
|
-
authority_scope_json: authorityScopeJson,
|
|
302
|
-
reason_classification: reasonClassification,
|
|
303
|
-
reason_text: reasonText,
|
|
304
|
-
evidence_refs_json: evidenceRefsJson,
|
|
305
|
-
}));
|
|
306
|
-
const edgeIdempotencyKey = nullableString(input.edge_idempotency_key) ??
|
|
307
|
-
(requestIdempotencyKey ? deriveEdgeIdempotencyKey(requestIdempotencyKey, contentHash) : null);
|
|
308
|
-
const existing = selectByIdempotency(adapter, modelRunId, edgeIdempotencyKey);
|
|
309
|
-
if (existing) {
|
|
310
|
-
return existing;
|
|
311
|
-
}
|
|
312
|
-
const edgeId = nullableString(input.edge_id) ?? generatedEdgeId();
|
|
313
|
-
const createdAt = Date.now();
|
|
314
|
-
try {
|
|
315
|
-
return adapter.transaction(() => {
|
|
316
|
-
adapter
|
|
317
|
-
.prepare(`
|
|
318
|
-
INSERT INTO twin_edges (
|
|
319
|
-
edge_id, edge_type, subject_kind, subject_id, object_kind, object_id,
|
|
320
|
-
relation_attrs_json, confidence, source, agent_id, model_run_id, envelope_hash,
|
|
321
|
-
human_actor_id, human_actor_role, authority_scope_json, reason_classification,
|
|
322
|
-
reason_text, evidence_refs_json, request_idempotency_key, edge_idempotency_key,
|
|
323
|
-
content_hash, created_at
|
|
324
|
-
)
|
|
325
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
326
|
-
`)
|
|
327
|
-
.run(edgeId, edgeType, subjectRef.kind, subjectRef.id, objectRef.kind, objectRef.id, relationAttrsJson, confidence, source, agentId, modelRunId, envelopeHash, humanActorId, humanActorRole, authorityScopeJson, reasonClassification, reasonText, evidenceRefsJson, requestIdempotencyKey, edgeIdempotencyKey, contentHash, createdAt);
|
|
328
|
-
const inserted = getTwinEdge(adapter, edgeId);
|
|
329
|
-
if (!inserted) {
|
|
330
|
-
throw new Error(`Failed to load inserted twin edge: ${edgeId}`);
|
|
331
|
-
}
|
|
332
|
-
return inserted;
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
catch (error) {
|
|
336
|
-
const duplicate = selectByIdempotency(adapter, modelRunId, edgeIdempotencyKey);
|
|
337
|
-
if (duplicate) {
|
|
338
|
-
return duplicate;
|
|
339
|
-
}
|
|
340
|
-
throw error;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
129
|
//# sourceMappingURL=store.js.map
|
package/dist/edges/types.d.ts
CHANGED
|
@@ -29,6 +29,13 @@ export interface TwinVisibility {
|
|
|
29
29
|
tenantId?: string | null;
|
|
30
30
|
startMs?: number | null;
|
|
31
31
|
asOfMs?: number | null;
|
|
32
|
+
/**
|
|
33
|
+
* Which channels of each connector may be read. When present it DECIDES raw visibility
|
|
34
|
+
* here exactly as it does in the reader, so a ref reached through an edge or named as a
|
|
35
|
+
* seed satisfies the same rule as a row the reader would have returned. Without it a
|
|
36
|
+
* caller could reach past the read path by citing what it could not have read.
|
|
37
|
+
*/
|
|
38
|
+
channels?: Record<string, readonly string[]>;
|
|
32
39
|
}
|
|
33
40
|
export interface InsertTwinEdgeInput {
|
|
34
41
|
edge_id?: string;
|
|
@@ -84,5 +91,8 @@ export interface ListVisibleTwinEdgesOptions {
|
|
|
84
91
|
startMs?: number | null;
|
|
85
92
|
asOfMs?: number | null;
|
|
86
93
|
limit?: number;
|
|
94
|
+
/** Forwarded to the raw visibility rule. Declared so an injected implementation
|
|
95
|
+
* cannot silently drop the grant - it crossed this seam as an undeclared property. */
|
|
96
|
+
channels?: Record<string, readonly string[]>;
|
|
87
97
|
}
|
|
88
98
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
*
|
|
12
12
|
* @module embedding-client
|
|
13
13
|
*/
|
|
14
|
-
import type { EmbeddingRole } from './embeddings.js';
|
|
15
14
|
export declare const DEFAULT_PORT: number;
|
|
16
15
|
export declare const HOST = "127.0.0.1";
|
|
17
16
|
export declare const TIMEOUT_MS = 500;
|
|
@@ -26,20 +25,4 @@ export interface ServerStatus {
|
|
|
26
25
|
* Get server port from port file or default
|
|
27
26
|
*/
|
|
28
27
|
export declare function getServerPort(): number;
|
|
29
|
-
/**
|
|
30
|
-
* Check if embedding server is running
|
|
31
|
-
*/
|
|
32
|
-
export declare function isServerRunning(): Promise<boolean>;
|
|
33
|
-
/**
|
|
34
|
-
* Generate embedding via HTTP server
|
|
35
|
-
*
|
|
36
|
-
* @param text - Text to embed
|
|
37
|
-
* @param role - e5 role: 'passage' (default, stored text) or 'query' (search)
|
|
38
|
-
* @returns Embedding or null if failed
|
|
39
|
-
*/
|
|
40
|
-
export declare function getEmbeddingFromServer(text: string, role?: EmbeddingRole): Promise<Float32Array | null>;
|
|
41
|
-
/**
|
|
42
|
-
* Get server status
|
|
43
|
-
*/
|
|
44
|
-
export declare function getServerStatus(): Promise<ServerStatus | null>;
|
|
45
28
|
//# sourceMappingURL=embedding-client.d.ts.map
|