Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
774 changes: 737 additions & 37 deletions bin/knowledge-mcp.js

Large diffs are not rendered by default.

586 changes: 367 additions & 219 deletions bin/knowledge.js

Large diffs are not rendered by default.

776 changes: 738 additions & 38 deletions dist/index.js

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion dist/knowledge-db.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Database } from 'bun:sqlite';
* directly — so this guard applies to CLI/MCP/SDK clients only.
*/
export declare function assertLocalCatalogMode(operation?: string): void;
export declare const CURRENT_SCHEMA_VERSION = 9;
export declare const CURRENT_SCHEMA_VERSION = 10;
/**
* FTS5 tokenizer for the chunk index. `porter` keeps English stemming; the
* wrapped `unicode61 remove_diacritics 2` folds accents/diacritics fully
Expand Down Expand Up @@ -41,6 +41,8 @@ export interface KnowledgeDbStats {
sync_conflicts: number;
sync_table_clocks: number;
sync_imports: number;
promotion_candidates: number;
durable_records: number;
}
export declare function openKnowledgeDb(path: string): Database;
/**
Expand Down
136 changes: 136 additions & 0 deletions dist/promotion-inbox.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
export type KnowledgePromotionKind = 'lesson' | 'decision' | 'claim';
export type KnowledgePromotionSourceKind = 'memento' | 'session' | 'report';
export type KnowledgePromotionStatus = 'ready' | 'needs_approval' | 'blocked' | 'duplicate' | 'promoted' | 'rejected';
export interface KnowledgePromotionEvidenceRefInput {
ref: string;
citation_id?: string | null;
chunk_id?: string | null;
revision?: string | null;
hash?: string | null;
observed_at?: string | null;
expires_at?: string | null;
status?: string | null;
}
export interface KnowledgePromotionEvidenceRef {
ref: string;
citation_id: string | null;
chunk_id: string | null;
revision: string | null;
hash: string | null;
observed_at: string | null;
expires_at: string | null;
status: string | null;
}
export interface KnowledgePromotionCitationCheck {
ref: string;
valid: boolean;
resolved_by: 'citation' | 'chunk' | 'source' | 'run' | 'external_uri' | 'none';
stale: boolean;
reason: string | null;
}
export interface KnowledgePromotionChecks {
citations: {
provided: number;
valid: number;
invalid: number;
entries: KnowledgePromotionCitationCheck[];
};
invalid_source_refs: string[];
stale_refs: string[];
duplicate_record_ids: string[];
duplicate_candidate_ids: string[];
conflicting_record_ids: string[];
conflicting_candidate_ids: string[];
approval_reasons: string[];
}
export interface KnowledgePromotionCandidate {
id: string;
record_kind: KnowledgePromotionKind;
title: string;
content: string;
canonical_key: string;
content_hash: string;
source_kind: KnowledgePromotionSourceKind;
source_refs: string[];
evidence_refs: KnowledgePromotionEvidenceRef[];
status: KnowledgePromotionStatus;
requires_approval: boolean;
checks: KnowledgePromotionChecks;
idempotency_key: string;
duplicate_of: string | null;
approved_by: string | null;
promoted_record_id: string | null;
metadata: Record<string, unknown>;
created_at: string;
updated_at: string;
reviewed_at: string | null;
promoted_at: string | null;
}
export interface DurableKnowledgeRecord {
id: string;
record_kind: KnowledgePromotionKind;
title: string;
content: string;
canonical_key: string;
content_hash: string;
status: string;
source_refs: string[];
evidence_refs: KnowledgePromotionEvidenceRef[];
confidence: number | null;
valid_from: string;
valid_to: string | null;
promoted_from_candidate_id: string;
approved_by: string | null;
metadata: Record<string, unknown>;
created_at: string;
updated_at: string;
}
export interface EnqueueKnowledgePromotionInput {
kind: KnowledgePromotionKind;
title: string;
content: string;
sourceKind: KnowledgePromotionSourceKind;
sourceRefs: string[];
evidenceRefs: Array<string | KnowledgePromotionEvidenceRefInput>;
canonicalKey?: string;
requiresApproval?: boolean;
confidence?: number;
validFrom?: string;
validTo?: string | null;
metadata?: Record<string, unknown>;
now?: Date;
}
export interface PromoteKnowledgeCandidateOptions {
approveWrite?: boolean;
approvedBy?: string;
now?: Date;
}
export declare function enqueueKnowledgePromotion(dbPath: string, input: EnqueueKnowledgePromotionInput): {
created: boolean;
candidate: KnowledgePromotionCandidate;
};
export declare function getKnowledgePromotion(dbPath: string, id: string): KnowledgePromotionCandidate | null;
export declare function listKnowledgePromotions(dbPath: string, options?: {
status?: KnowledgePromotionStatus | 'inbox';
kind?: KnowledgePromotionKind;
limit?: number;
}): KnowledgePromotionCandidate[];
export declare function reviewKnowledgePromotion(dbPath: string, id: string, now?: Date): KnowledgePromotionCandidate;
export declare function promoteKnowledgeCandidate(dbPath: string, id: string, options?: PromoteKnowledgeCandidateOptions): {
ok: boolean;
promoted: boolean;
requires_approval: boolean;
candidate: KnowledgePromotionCandidate;
record: DurableKnowledgeRecord | null;
approval_id: string | null;
reason: string | null;
};
export declare function rejectKnowledgePromotion(dbPath: string, id: string, options?: {
rejectedBy?: string;
now?: Date;
}): KnowledgePromotionCandidate;
export declare function listDurableKnowledgeRecords(dbPath: string, options?: {
kind?: KnowledgePromotionKind;
status?: string;
limit?: number;
}): DurableKnowledgeRecord[];
32 changes: 32 additions & 0 deletions dist/service.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { type KnowledgeSyncConflictAiProposalOptions } from './conflict-agent';
import { type EmbeddingIndexOptions, type EmbeddingSearchOptions } from './embeddings';
import { type KnowledgeMachinePreflightOptions, type KnowledgeMachineRouteResolution, type KnowledgeMachineWorkspaceResolution, type KnowledgeMachineTopologyOptions } from './machines';
import { type ProviderStatusResult, type ModelRegistryEntry } from './providers';
import { type DurableKnowledgeRecord, type EnqueueKnowledgePromotionInput, type KnowledgePromotionCandidate, type KnowledgePromotionKind, type KnowledgePromotionStatus, type PromoteKnowledgeCandidateOptions } from './promotion-inbox';
import { type ReindexRuntimeOptions } from './reindex';
import { type KnowledgeContextPack, type RetrievalOptions } from './retrieval';
import { type RulesProvenanceImportResult } from './rules-provenance';
Expand Down Expand Up @@ -99,6 +100,8 @@ export interface KnowledgeInventoryResult {
sync_conflicts: Array<Record<string, unknown>>;
approval_gates: Array<Record<string, unknown>>;
audit_events: Array<Record<string, unknown>>;
promotion_candidates: Array<Record<string, unknown>>;
durable_records: Array<Record<string, unknown>>;
message: string;
}
export interface KnowledgeSetupResult {
Expand Down Expand Up @@ -423,6 +426,35 @@ export declare class KnowledgeService {
schema_version: number;
};
dbStats(): import("./knowledge-db").KnowledgeDbStats;
enqueuePromotion(input: EnqueueKnowledgePromotionInput): {
created: boolean;
candidate: KnowledgePromotionCandidate;
};
promotionInbox(options?: {
status?: KnowledgePromotionStatus | 'inbox';
kind?: KnowledgePromotionKind;
limit?: number;
}): KnowledgePromotionCandidate[];
getPromotion(id: string): KnowledgePromotionCandidate | null;
reviewPromotion(id: string, now?: Date): KnowledgePromotionCandidate;
promoteCandidate(id: string, options?: PromoteKnowledgeCandidateOptions): {
ok: boolean;
promoted: boolean;
requires_approval: boolean;
candidate: KnowledgePromotionCandidate;
record: DurableKnowledgeRecord | null;
approval_id: string | null;
reason: string | null;
};
rejectPromotion(id: string, options?: {
rejectedBy?: string;
now?: Date;
}): KnowledgePromotionCandidate;
durableRecords(options?: {
kind?: KnowledgePromotionKind;
status?: string;
limit?: number;
}): DurableKnowledgeRecord[];
/**
* Build a knowledge inventory from a bare item list (no local sqlite catalog).
* Shared by the local no-db path and the cloud path so both produce the exact
Expand Down
76 changes: 74 additions & 2 deletions dist/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -19367,7 +19367,7 @@ function assertLocalCatalogMode(operation = "catalog") {
throw new Error(`knowledge: ${operation} builds/reads the on-box sqlite RAG catalog (source ingestion, chunk embeddings, ` + `wiki compilation, cross-machine sync, machine registry). That local indexing pipeline is not available in ` + `cloud mode. In cloud mode the shared corpus is the cloud knowledge-items: 'add/list/get/update/delete' item ` + `commands AND 'search/ask/build/context' over that shared corpus all route to the cloud. Set ${modeKey}=local ` + `(or unset it \u2014 local is the default) to use the full local catalog pipeline; run 'knowledge mode' to see ` + `which variable selected the current backend.`);
}
}
var CURRENT_SCHEMA_VERSION = 9;
var CURRENT_SCHEMA_VERSION = 10;
var CHUNKS_FTS_TOKENIZE = "porter unicode61 remove_diacritics 2";
var MIGRATION_1 = `
PRAGMA journal_mode = WAL;
Expand Down Expand Up @@ -19789,6 +19789,68 @@ VALUES (9, datetime('now'));

COMMIT;
`;
var MIGRATION_10_PROMOTION_INBOX = `
CREATE TABLE IF NOT EXISTS knowledge_promotion_candidates (
id TEXT PRIMARY KEY,
record_kind TEXT NOT NULL,
title TEXT NOT NULL,
content TEXT NOT NULL,
canonical_key TEXT NOT NULL,
content_hash TEXT NOT NULL,
source_kind TEXT NOT NULL,
source_refs_json TEXT NOT NULL DEFAULT '[]',
evidence_refs_json TEXT NOT NULL DEFAULT '[]',
status TEXT NOT NULL DEFAULT 'pending',
requires_approval INTEGER NOT NULL DEFAULT 0,
checks_json TEXT NOT NULL DEFAULT '{}',
idempotency_key TEXT NOT NULL UNIQUE,
duplicate_of TEXT,
approved_by TEXT,
promoted_record_id TEXT,
metadata_json TEXT NOT NULL DEFAULT '{}',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
reviewed_at TEXT,
promoted_at TEXT
);

CREATE TABLE IF NOT EXISTS durable_knowledge_records (
id TEXT PRIMARY KEY,
record_kind TEXT NOT NULL,
title TEXT NOT NULL,
content TEXT NOT NULL,
canonical_key TEXT NOT NULL,
content_hash TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
source_refs_json TEXT NOT NULL DEFAULT '[]',
evidence_refs_json TEXT NOT NULL DEFAULT '[]',
confidence REAL,
valid_from TEXT NOT NULL,
valid_to TEXT,
promoted_from_candidate_id TEXT NOT NULL UNIQUE
REFERENCES knowledge_promotion_candidates(id) ON DELETE RESTRICT,
approved_by TEXT,
metadata_json TEXT NOT NULL DEFAULT '{}',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_promotion_candidates_status
ON knowledge_promotion_candidates(status, updated_at);
CREATE INDEX IF NOT EXISTS idx_promotion_candidates_kind_key
ON knowledge_promotion_candidates(record_kind, canonical_key);
CREATE INDEX IF NOT EXISTS idx_promotion_candidates_hash
ON knowledge_promotion_candidates(record_kind, content_hash);
CREATE INDEX IF NOT EXISTS idx_durable_records_kind_key
ON durable_knowledge_records(record_kind, canonical_key, status);
CREATE INDEX IF NOT EXISTS idx_durable_records_hash
ON durable_knowledge_records(record_kind, content_hash, status);
CREATE INDEX IF NOT EXISTS idx_durable_records_validity
ON durable_knowledge_records(status, valid_to);

INSERT OR IGNORE INTO schema_versions(version, applied_at)
VALUES (10, datetime('now'));
`;
function openKnowledgeDb(path) {
assertLocalCatalogMode("opening the local knowledge.db catalog");
ensureParentDir(path);
Expand Down Expand Up @@ -19821,6 +19883,8 @@ function migrateKnowledgeDb(path) {
applyMigration8(db);
if (needsMigration9(db))
applyMigration9(db);
if (needsMigration10(db))
applyMigration10(db);
return { path, schema_version: getSchemaVersion(db) };
} finally {
db.close();
Expand Down Expand Up @@ -19901,6 +19965,12 @@ function applyMigration9(db) {
}
db.exec(MIGRATION_9_REBUILD_FTS);
}
function needsMigration10(db) {
return getSchemaVersion(db) < 10 || !tableExists(db, "knowledge_promotion_candidates") || !tableExists(db, "durable_knowledge_records");
}
function applyMigration10(db) {
db.exec(MIGRATION_10_PROMOTION_INBOX);
}
function getKnowledgeDbStats(path) {
const db = openKnowledgeDb(path);
try {
Expand All @@ -19926,7 +19996,9 @@ function getKnowledgeDbStats(path) {
sync_changes: count(db, "knowledge_sync_changes"),
sync_conflicts: count(db, "knowledge_sync_conflicts"),
sync_table_clocks: count(db, "knowledge_sync_table_clocks"),
sync_imports: count(db, "knowledge_sync_imports")
sync_imports: count(db, "knowledge_sync_imports"),
promotion_candidates: count(db, "knowledge_promotion_candidates"),
durable_records: count(db, "durable_knowledge_records")
};
} finally {
db.close();
Expand Down
Loading
Loading