@hasna/knowledge 0.2.11 → 0.2.13
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 +24 -2
- package/bin/open-knowledge-mcp.js +370 -14
- package/bin/open-knowledge.js +57 -31
- package/docs/architecture/ai-native-knowledge-base.md +27 -0
- package/package.json +1 -1
- package/src/cli.ts +31 -4
- package/src/knowledge-db.ts +3 -0
- package/src/manifest-ingest.ts +19 -2
- package/src/mcp.js +12 -0
- package/src/provenance.ts +93 -0
- package/src/service.ts +28 -3
- package/src/source-resolver.ts +18 -0
- package/src/storage-contract.ts +265 -0
- package/src/wiki-layout.ts +113 -6
|
@@ -89,6 +89,21 @@ file revisions, hashes, extraction state, permissions, and storage metadata.
|
|
|
89
89
|
Direct `s3://`, `file://`, and `https://` refs are useful for bootstrap and
|
|
90
90
|
interop, but should be normalized into source records when possible.
|
|
91
91
|
|
|
92
|
+
## Provenance Contract
|
|
93
|
+
|
|
94
|
+
Every durable search/wiki artifact should carry a provenance object in metadata:
|
|
95
|
+
`source_owner`, `source_ref`, `source_uri`, `source_kind`, `source_revision_id`,
|
|
96
|
+
`revision`, `hash`, optional `chunk_id`, offsets, `read_only`,
|
|
97
|
+
`citation_required`, resolver name, and stale status. For generated artifacts
|
|
98
|
+
that are not source-backed yet, metadata still records that `open-files` owns
|
|
99
|
+
source bytes and that citations are required before durable facts are filed.
|
|
100
|
+
|
|
101
|
+
`wiki init` now catalogs the starter `wiki/README.md` and `indexes/root.md`
|
|
102
|
+
records with generated-artifact provenance. Source ingestion stores source
|
|
103
|
+
provenance on every chunk, and source resolution returns that provenance with
|
|
104
|
+
chunks and citations so semantic search can pass through trustworthy evidence
|
|
105
|
+
without reconstructing it later.
|
|
106
|
+
|
|
92
107
|
## Resolver Boundary
|
|
93
108
|
|
|
94
109
|
The local resolver is exposed through:
|
|
@@ -133,6 +148,18 @@ Raw files still route through `open-files`. Knowledge S3 storage is for derived
|
|
|
133
148
|
artifacts such as wiki pages, index shards, schema versions, logs, exports, and
|
|
134
149
|
run outputs.
|
|
135
150
|
|
|
151
|
+
The storage contract is inspectable through:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
open-knowledge storage status --scope project --json
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
That contract names the local app path, SQLite catalog, generated artifact
|
|
158
|
+
classes, S3 bucket/prefix when configured, and the source ownership rule that
|
|
159
|
+
raw source bytes stay in `open-files`. The `storage_objects` table catalogs
|
|
160
|
+
generated artifacts by URI, kind, hash, size, and metadata so local mode and
|
|
161
|
+
remote/S3 mode share the same DB-facing shape.
|
|
162
|
+
|
|
136
163
|
## Wiki Model
|
|
137
164
|
|
|
138
165
|
The Karpathy-style wiki pattern is implemented as scalable artifacts, not three
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -60,7 +60,7 @@ interface ParseResult {
|
|
|
60
60
|
flags: Flags;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
const COMMANDS = ['add', 'list', 'get', 'delete', 'update', 'archive', 'restore', 'upsert', 'untag', 'export', 'prune', 'dedupe', 'stats', 'paths', 'db', 'wiki', 'source', 'ingest', 'reindex', 'providers', 'safety', 'help'];
|
|
63
|
+
const COMMANDS = ['add', 'list', 'get', 'delete', 'update', 'archive', 'restore', 'upsert', 'untag', 'export', 'prune', 'dedupe', 'stats', 'paths', 'storage', 'db', 'wiki', 'source', 'ingest', 'reindex', 'providers', 'safety', 'help'];
|
|
64
64
|
const COMMAND_ALIASES: Record<string, string> = {
|
|
65
65
|
ls: 'list',
|
|
66
66
|
rm: 'delete',
|
|
@@ -162,6 +162,7 @@ Commands:
|
|
|
162
162
|
dedupe Remove duplicate items by title+content (requires --yes)
|
|
163
163
|
stats Show knowledge base statistics
|
|
164
164
|
paths Show resolved workspace/store paths
|
|
165
|
+
storage status|validate Inspect local/S3 artifact storage contract
|
|
165
166
|
db init|stats Initialize or inspect local knowledge.db
|
|
166
167
|
wiki init Initialize scalable wiki/schema/index/log artifacts
|
|
167
168
|
source resolve <source-ref> Resolve read-only source content and citation evidence
|
|
@@ -230,6 +231,7 @@ function printCommandHelp(command: string): void {
|
|
|
230
231
|
if (command === 'dedupe') { console.log('Usage: open-knowledge dedupe --yes [--json]'); return; }
|
|
231
232
|
if (command === 'stats') { console.log('Usage: open-knowledge stats [--json]'); return; }
|
|
232
233
|
if (command === 'paths') { console.log('Usage: open-knowledge paths [--scope local|global|project] [--json]'); return; }
|
|
234
|
+
if (command === 'storage') { console.log('Usage: open-knowledge storage status|validate [--scope local|global|project] [--json]'); return; }
|
|
233
235
|
if (command === 'db') { console.log('Usage: open-knowledge db init|stats [--scope local|global|project] [--json]'); return; }
|
|
234
236
|
if (command === 'wiki') { console.log('Usage: open-knowledge wiki init [--scope local|global|project] [--json]'); return; }
|
|
235
237
|
if (command === 'source') { console.log('Usage: open-knowledge source resolve <source-ref> [--purpose knowledge_answer|knowledge_index] [--limit <n>] [--scope local|global|project] [--json]'); return; }
|
|
@@ -281,11 +283,11 @@ async function run(argv: string[]): Promise<void> {
|
|
|
281
283
|
if (flags.completions) {
|
|
282
284
|
const shell = flags.completions;
|
|
283
285
|
if (shell === 'bash') {
|
|
284
|
-
console.log(`_open_knowledge() { local cur; cur="${"$"}{COMP_WORDS[COMP_CWORD]}"; COMPREPLY=($(compgen -W "add list get update archive restore upsert untag delete export prune dedupe stats paths db wiki source ingest reindex providers safety help ls rm edit unarchive --json --yes --help --version --desc --page --limit --search --sort --id --store --title --content --url --tag --format --completions --purpose --no-color --scope --archived --include-archived" -- "$cur")); }; complete -F _open_knowledge open-knowledge`);
|
|
286
|
+
console.log(`_open_knowledge() { local cur; cur="${"$"}{COMP_WORDS[COMP_CWORD]}"; COMPREPLY=($(compgen -W "add list get update archive restore upsert untag delete export prune dedupe stats paths storage db wiki source ingest reindex providers safety help ls rm edit unarchive --json --yes --help --version --desc --page --limit --search --sort --id --store --title --content --url --tag --format --completions --purpose --no-color --scope --archived --include-archived" -- "$cur")); }; complete -F _open_knowledge open-knowledge`);
|
|
285
287
|
} else if (shell === 'zsh') {
|
|
286
|
-
console.log(`#compdef open-knowledge\n_open_knowledge() { _arguments -C "1: :(add list get update archive restore upsert untag delete export prune dedupe stats paths db wiki source ingest reindex providers safety help ls rm edit unarchive)" "(--json)--json" "(--yes)-y" "(--help)--help" "(--version)--version" "(--desc)--desc" "(--archived)--archived" "(--include-archived)--include-archived" "(-p --page)"{-p,--page}"[page number]:number:" "(-l --limit)"{-l,--limit}"[items per page]:number:" "(-s --search)"{-s,--search}"[search text]:text:" "(--sort)--sort"\{created,title\}:" "(--id)--id[item id]:id:" "(--store)--store[store path]:path:" "(--title)--title[new title]:" "(--content)--content[new content]:" "(--url)--url[source url]:" "(-t --tag)"{-t,--tag}"[tag]:tag:" "(--format)--format[json|jsonl]:" "(--completions)--completions[output completions]:shell:(bash zsh fish):" "(--purpose)--purpose[purpose]:" "(--no-color)--no-color[disable color]" "(--scope)--scope"\{local,global,project\}:" }; _open_knowledge`);
|
|
288
|
+
console.log(`#compdef open-knowledge\n_open_knowledge() { _arguments -C "1: :(add list get update archive restore upsert untag delete export prune dedupe stats paths storage db wiki source ingest reindex providers safety help ls rm edit unarchive)" "(--json)--json" "(--yes)-y" "(--help)--help" "(--version)--version" "(--desc)--desc" "(--archived)--archived" "(--include-archived)--include-archived" "(-p --page)"{-p,--page}"[page number]:number:" "(-l --limit)"{-l,--limit}"[items per page]:number:" "(-s --search)"{-s,--search}"[search text]:text:" "(--sort)--sort"\{created,title\}:" "(--id)--id[item id]:id:" "(--store)--store[store path]:path:" "(--title)--title[new title]:" "(--content)--content[new content]:" "(--url)--url[source url]:" "(-t --tag)"{-t,--tag}"[tag]:tag:" "(--format)--format[json|jsonl]:" "(--completions)--completions[output completions]:shell:(bash zsh fish):" "(--purpose)--purpose[purpose]:" "(--no-color)--no-color[disable color]" "(--scope)--scope"\{local,global,project\}:" }; _open_knowledge`);
|
|
287
289
|
} else if (shell === 'fish') {
|
|
288
|
-
console.log(`complete -c open-knowledge -f; complete -c open-knowledge -a "add list get update archive restore upsert untag delete export prune dedupe stats paths db wiki source ingest reindex providers safety help ls rm edit unarchive"; complete -c open-knowledge -l json; complete -c open-knowledge -l yes -s y; complete -c open-knowledge -l help -s h; complete -c open-knowledge -l version -s v; complete -c open-knowledge -l desc; complete -c open-knowledge -l archived; complete -c open-knowledge -l include-archived; complete -c open-knowledge -s p -l page; complete -c open-knowledge -s l -l limit; complete -c open-knowledge -s s -l search; complete -c open-knowledge -l sort; complete -c open-knowledge -l id; complete -c open-knowledge -l store; complete -c open-knowledge -l title; complete -c open-knowledge -l content; complete -c open-knowledge -l url; complete -c open-knowledge -s t -l tag; complete -c open-knowledge -l format; complete -c open-knowledge -l completions; complete -c open-knowledge -l purpose; complete -c open-knowledge -l no-color; complete -c open-knowledge -l scope -a "local global project"`);
|
|
290
|
+
console.log(`complete -c open-knowledge -f; complete -c open-knowledge -a "add list get update archive restore upsert untag delete export prune dedupe stats paths storage db wiki source ingest reindex providers safety help ls rm edit unarchive"; complete -c open-knowledge -l json; complete -c open-knowledge -l yes -s y; complete -c open-knowledge -l help -s h; complete -c open-knowledge -l version -s v; complete -c open-knowledge -l desc; complete -c open-knowledge -l archived; complete -c open-knowledge -l include-archived; complete -c open-knowledge -s p -l page; complete -c open-knowledge -s l -l limit; complete -c open-knowledge -s s -l search; complete -c open-knowledge -l sort; complete -c open-knowledge -l id; complete -c open-knowledge -l store; complete -c open-knowledge -l title; complete -c open-knowledge -l content; complete -c open-knowledge -l url; complete -c open-knowledge -s t -l tag; complete -c open-knowledge -l format; complete -c open-knowledge -l completions; complete -c open-knowledge -l purpose; complete -c open-knowledge -l no-color; complete -c open-knowledge -l scope -a "local global project"`);
|
|
289
291
|
} else {
|
|
290
292
|
throw new Error("Invalid --completions value. Use 'bash', 'zsh', or 'fish'.");
|
|
291
293
|
}
|
|
@@ -311,6 +313,31 @@ async function run(argv: string[]): Promise<void> {
|
|
|
311
313
|
return;
|
|
312
314
|
}
|
|
313
315
|
|
|
316
|
+
if (command === 'storage') {
|
|
317
|
+
const action = positional[1] ?? 'status';
|
|
318
|
+
if (action === 'status') {
|
|
319
|
+
const contract = service.storageContract();
|
|
320
|
+
const validation = service.validateStorage();
|
|
321
|
+
output({
|
|
322
|
+
ok: validation.ok,
|
|
323
|
+
...contract,
|
|
324
|
+
validation,
|
|
325
|
+
message: `${contract.storage_type} artifact storage at ${contract.artifact_store.uri_prefix}`,
|
|
326
|
+
}, flags.json);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
if (action === 'validate') {
|
|
330
|
+
const validation = service.validateStorage();
|
|
331
|
+
output({
|
|
332
|
+
ok: validation.ok,
|
|
333
|
+
validation,
|
|
334
|
+
message: validation.ok ? 'Storage contract valid' : `Storage contract invalid: ${validation.errors.join('; ')}`,
|
|
335
|
+
}, flags.json);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
throw new Error("Invalid storage action. Use 'status' or 'validate'.");
|
|
339
|
+
}
|
|
340
|
+
|
|
314
341
|
if (command === 'db') {
|
|
315
342
|
const action = positional[1] ?? 'init';
|
|
316
343
|
if (action !== 'init' && action !== 'stats') {
|
package/src/knowledge-db.ts
CHANGED
|
@@ -16,6 +16,7 @@ export interface KnowledgeDbStats {
|
|
|
16
16
|
redaction_findings: number;
|
|
17
17
|
audit_events: number;
|
|
18
18
|
approval_gates: number;
|
|
19
|
+
storage_objects: number;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
const MIGRATION_1 = `
|
|
@@ -239,6 +240,7 @@ export function openKnowledgeDb(path: string): Database {
|
|
|
239
240
|
ensureParentDir(path);
|
|
240
241
|
const db = new Database(path);
|
|
241
242
|
db.exec('PRAGMA foreign_keys = ON;');
|
|
243
|
+
db.exec('PRAGMA busy_timeout = 5000;');
|
|
242
244
|
return db;
|
|
243
245
|
}
|
|
244
246
|
|
|
@@ -280,6 +282,7 @@ export function getKnowledgeDbStats(path: string): KnowledgeDbStats {
|
|
|
280
282
|
redaction_findings: count(db, 'redaction_findings'),
|
|
281
283
|
audit_events: count(db, 'audit_events'),
|
|
282
284
|
approval_gates: count(db, 'approval_gates'),
|
|
285
|
+
storage_objects: count(db, 'storage_objects'),
|
|
283
286
|
};
|
|
284
287
|
} finally {
|
|
285
288
|
db.close();
|
package/src/manifest-ingest.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { basename } from 'node:path';
|
|
|
4
4
|
import type { Database } from 'bun:sqlite';
|
|
5
5
|
import { migrateKnowledgeDb, openKnowledgeDb } from './knowledge-db';
|
|
6
6
|
import { parseSourceRef, type SourceRef } from './source-ref';
|
|
7
|
+
import { sourceProvenance, withProvenance } from './provenance';
|
|
7
8
|
import type { KnowledgeConfig } from './workspace';
|
|
8
9
|
import {
|
|
9
10
|
assertS3ReadAllowed,
|
|
@@ -382,15 +383,31 @@ function insertChunks(db: Database, sourceRevisionId: string, item: NormalizedMa
|
|
|
382
383
|
const chunks = chunkText(redacted.text, maxChars, overlapChars);
|
|
383
384
|
for (const chunk of chunks) {
|
|
384
385
|
const chunkId = stableId('chk', `${sourceRevisionId}\u0000${chunk.ordinal}\u0000${chunk.text}`);
|
|
385
|
-
const
|
|
386
|
+
const provenance = sourceProvenance({
|
|
386
387
|
source_ref: item.sourceRef,
|
|
387
388
|
source_uri: item.sourceUri,
|
|
389
|
+
source_kind: item.kind,
|
|
390
|
+
source_revision_id: sourceRevisionId,
|
|
391
|
+
revision: item.revision,
|
|
392
|
+
hash: item.hash,
|
|
393
|
+
chunk_id: chunkId,
|
|
394
|
+
start_offset: chunk.startOffset,
|
|
395
|
+
end_offset: chunk.endOffset,
|
|
396
|
+
status: item.status,
|
|
397
|
+
resolver: 'open-files-read-only',
|
|
398
|
+
});
|
|
399
|
+
const metadata = withProvenance({
|
|
400
|
+
source_ref: item.sourceRef,
|
|
401
|
+
source_uri: item.sourceUri,
|
|
402
|
+
source_kind: item.kind,
|
|
403
|
+
source_revision_id: sourceRevisionId,
|
|
404
|
+
revision: item.revision,
|
|
388
405
|
hash: item.hash,
|
|
389
406
|
status: item.status,
|
|
390
407
|
path: asString(item.raw.path) ?? null,
|
|
391
408
|
mime: asString(item.raw.mime) ?? asString(item.raw.content_type) ?? null,
|
|
392
409
|
size: asNumber(item.raw.size) ?? null,
|
|
393
|
-
};
|
|
410
|
+
}, provenance);
|
|
394
411
|
db.run(
|
|
395
412
|
`INSERT INTO chunks (id, source_revision_id, kind, ordinal, text, token_count, start_offset, end_offset, metadata_json, created_at)
|
|
396
413
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
package/src/mcp.js
CHANGED
|
@@ -77,6 +77,18 @@ export function buildServer() {
|
|
|
77
77
|
return jsonText(createKnowledgeService({ scope }).paths());
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
+
registerTool(server, 'ok_storage_status', 'Knowledge storage status', 'Inspect local/S3 artifact storage, source ownership, and scalability contract', {
|
|
81
|
+
scope: scopeField,
|
|
82
|
+
}, async ({ scope }) => {
|
|
83
|
+
const service = createKnowledgeService({ scope });
|
|
84
|
+
const validation = service.validateStorage();
|
|
85
|
+
return jsonText({
|
|
86
|
+
ok: validation.ok,
|
|
87
|
+
...service.storageContract(),
|
|
88
|
+
validation,
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
80
92
|
registerTool(server, 'ok_parse_source_ref', 'Parse source reference', 'Parse and validate an open-files, S3, file, or web source ref', {
|
|
81
93
|
uri: z.string().describe('Source reference URI'),
|
|
82
94
|
}, async ({ uri }) => {
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export interface KnowledgeProvenance {
|
|
2
|
+
source_owner: 'open-files';
|
|
3
|
+
source_ref: string | null;
|
|
4
|
+
source_uri: string | null;
|
|
5
|
+
source_kind: string | null;
|
|
6
|
+
source_revision_id: string | null;
|
|
7
|
+
revision: string | null;
|
|
8
|
+
hash: string | null;
|
|
9
|
+
chunk_id: string | null;
|
|
10
|
+
start_offset: number | null;
|
|
11
|
+
end_offset: number | null;
|
|
12
|
+
status: string | null;
|
|
13
|
+
read_only: true;
|
|
14
|
+
citation_required: boolean;
|
|
15
|
+
resolver: string | null;
|
|
16
|
+
stale: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface GeneratedArtifactProvenance {
|
|
20
|
+
source_owner: 'open-files';
|
|
21
|
+
generated_from: string;
|
|
22
|
+
artifact_key: string;
|
|
23
|
+
source_refs: string[];
|
|
24
|
+
read_only_sources: true;
|
|
25
|
+
citation_required: boolean;
|
|
26
|
+
raw_source_bytes_stored_in_open_knowledge: false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface SourceProvenanceInput {
|
|
30
|
+
source_ref?: string | null;
|
|
31
|
+
source_uri?: string | null;
|
|
32
|
+
source_kind?: string | null;
|
|
33
|
+
source_revision_id?: string | null;
|
|
34
|
+
revision?: string | null;
|
|
35
|
+
hash?: string | null;
|
|
36
|
+
chunk_id?: string | null;
|
|
37
|
+
start_offset?: number | null;
|
|
38
|
+
end_offset?: number | null;
|
|
39
|
+
status?: string | null;
|
|
40
|
+
resolver?: string | null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function isStaleStatus(status: string | null | undefined): boolean {
|
|
44
|
+
return ['deleted', 'stale', 'invalidated', 'reindex_required'].includes((status ?? '').toLowerCase());
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function sourceProvenance(input: SourceProvenanceInput): KnowledgeProvenance {
|
|
48
|
+
const status = input.status ?? null;
|
|
49
|
+
return {
|
|
50
|
+
source_owner: 'open-files',
|
|
51
|
+
source_ref: input.source_ref ?? null,
|
|
52
|
+
source_uri: input.source_uri ?? null,
|
|
53
|
+
source_kind: input.source_kind ?? null,
|
|
54
|
+
source_revision_id: input.source_revision_id ?? null,
|
|
55
|
+
revision: input.revision ?? null,
|
|
56
|
+
hash: input.hash ?? null,
|
|
57
|
+
chunk_id: input.chunk_id ?? null,
|
|
58
|
+
start_offset: input.start_offset ?? null,
|
|
59
|
+
end_offset: input.end_offset ?? null,
|
|
60
|
+
status,
|
|
61
|
+
read_only: true,
|
|
62
|
+
citation_required: true,
|
|
63
|
+
resolver: input.resolver ?? null,
|
|
64
|
+
stale: isStaleStatus(status),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function generatedArtifactProvenance(input: {
|
|
69
|
+
generated_from: string;
|
|
70
|
+
artifact_key: string;
|
|
71
|
+
source_refs?: string[];
|
|
72
|
+
citation_required?: boolean;
|
|
73
|
+
}): GeneratedArtifactProvenance {
|
|
74
|
+
return {
|
|
75
|
+
source_owner: 'open-files',
|
|
76
|
+
generated_from: input.generated_from,
|
|
77
|
+
artifact_key: input.artifact_key,
|
|
78
|
+
source_refs: input.source_refs ?? [],
|
|
79
|
+
read_only_sources: true,
|
|
80
|
+
citation_required: input.citation_required ?? true,
|
|
81
|
+
raw_source_bytes_stored_in_open_knowledge: false,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function withProvenance<T extends Record<string, unknown>>(
|
|
86
|
+
metadata: T,
|
|
87
|
+
provenance: KnowledgeProvenance | GeneratedArtifactProvenance,
|
|
88
|
+
): T & { provenance: KnowledgeProvenance | GeneratedArtifactProvenance } {
|
|
89
|
+
return {
|
|
90
|
+
...metadata,
|
|
91
|
+
provenance,
|
|
92
|
+
};
|
|
93
|
+
}
|
package/src/service.ts
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { createArtifactStore } from './artifact-store';
|
|
2
2
|
import { consumeOpenFilesOutbox } from './outbox-consume';
|
|
3
|
-
import { getKnowledgeDbStats, migrateKnowledgeDb } from './knowledge-db';
|
|
3
|
+
import { getKnowledgeDbStats, migrateKnowledgeDb, openKnowledgeDb } from './knowledge-db';
|
|
4
4
|
import { ingestOpenFilesManifest } from './manifest-ingest';
|
|
5
5
|
import { ingestSourceRef } from './source-ingest';
|
|
6
6
|
import { resolveOpenFilesSource } from './source-resolver';
|
|
7
7
|
import { providerStatus, listModelRegistry, type ProviderStatusResult, type ModelRegistryEntry } from './providers';
|
|
8
8
|
import { resolveSafetyPolicy } from './safety';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
recordStorageObjects,
|
|
11
|
+
resolveStorageContract,
|
|
12
|
+
validateStorageConfig,
|
|
13
|
+
type StorageContract,
|
|
14
|
+
type StorageValidationResult,
|
|
15
|
+
} from './storage-contract';
|
|
16
|
+
import { initializeWikiLayout, recordWikiLayoutCatalog } from './wiki-layout';
|
|
10
17
|
import {
|
|
11
18
|
ensureKnowledgeWorkspace,
|
|
12
19
|
readKnowledgeConfig,
|
|
@@ -76,6 +83,14 @@ export class KnowledgeService {
|
|
|
76
83
|
return createArtifactStore(this.config(), this.ensureWorkspace());
|
|
77
84
|
}
|
|
78
85
|
|
|
86
|
+
storageContract(): StorageContract {
|
|
87
|
+
return resolveStorageContract(this.config(), this.ensureWorkspace(), this.scope);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
validateStorage(): StorageValidationResult {
|
|
91
|
+
return validateStorageConfig(this.config(), this.ensureWorkspace());
|
|
92
|
+
}
|
|
93
|
+
|
|
79
94
|
paths(): KnowledgePathsResult {
|
|
80
95
|
const workspace = this.ensureWorkspace();
|
|
81
96
|
return {
|
|
@@ -107,7 +122,17 @@ export class KnowledgeService {
|
|
|
107
122
|
}
|
|
108
123
|
|
|
109
124
|
async initWiki() {
|
|
110
|
-
|
|
125
|
+
const workspace = this.ensureWorkspace();
|
|
126
|
+
migrateKnowledgeDb(workspace.knowledgeDbPath);
|
|
127
|
+
const result = await initializeWikiLayout(this.artifactStore());
|
|
128
|
+
const db = openKnowledgeDb(workspace.knowledgeDbPath);
|
|
129
|
+
try {
|
|
130
|
+
recordStorageObjects(db, result.artifacts);
|
|
131
|
+
recordWikiLayoutCatalog(db, result.artifacts);
|
|
132
|
+
} finally {
|
|
133
|
+
db.close();
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
111
136
|
}
|
|
112
137
|
|
|
113
138
|
async ingestManifest(input: string) {
|
package/src/source-resolver.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Database } from 'bun:sqlite';
|
|
2
2
|
import { migrateKnowledgeDb, openKnowledgeDb } from './knowledge-db';
|
|
3
|
+
import { sourceProvenance, type KnowledgeProvenance } from './provenance';
|
|
3
4
|
import { catalogSourceUriForRef, parseSourceRef, revisionIdForSourceRef } from './source-ref';
|
|
4
5
|
import { assertWriteAllowed, recordAuditEvent, type SafetyPolicy } from './safety';
|
|
5
6
|
|
|
@@ -38,6 +39,7 @@ export interface ResolvedSourceChunk {
|
|
|
38
39
|
end_offset: number | null;
|
|
39
40
|
metadata: Record<string, unknown>;
|
|
40
41
|
evidence: SourceResolverEvidence;
|
|
42
|
+
provenance: KnowledgeProvenance;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
export interface ResolvedSourceCitation {
|
|
@@ -48,6 +50,7 @@ export interface ResolvedSourceCitation {
|
|
|
48
50
|
start_offset: number | null;
|
|
49
51
|
end_offset: number | null;
|
|
50
52
|
evidence: SourceResolverEvidence;
|
|
53
|
+
provenance: KnowledgeProvenance;
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
export interface SourceResolveResult {
|
|
@@ -326,6 +329,19 @@ export async function resolveOpenFilesSource(options: SourceResolveOptions): Pro
|
|
|
326
329
|
end_offset: row.end_offset,
|
|
327
330
|
resolved_at: resolvedAt,
|
|
328
331
|
};
|
|
332
|
+
const provenance = sourceProvenance({
|
|
333
|
+
source_ref: evidence.source_ref,
|
|
334
|
+
source_uri: evidence.source_uri,
|
|
335
|
+
source_kind: source.kind,
|
|
336
|
+
source_revision_id: evidence.source_revision_id,
|
|
337
|
+
revision: evidence.revision,
|
|
338
|
+
hash: evidence.hash,
|
|
339
|
+
chunk_id: row.id,
|
|
340
|
+
start_offset: row.start_offset,
|
|
341
|
+
end_offset: row.end_offset,
|
|
342
|
+
status: metadataString(metadata, ['status']),
|
|
343
|
+
resolver: evidence.resolver,
|
|
344
|
+
});
|
|
329
345
|
return {
|
|
330
346
|
id: row.id,
|
|
331
347
|
kind: row.kind,
|
|
@@ -336,6 +352,7 @@ export async function resolveOpenFilesSource(options: SourceResolveOptions): Pro
|
|
|
336
352
|
end_offset: row.end_offset,
|
|
337
353
|
metadata,
|
|
338
354
|
evidence,
|
|
355
|
+
provenance,
|
|
339
356
|
};
|
|
340
357
|
});
|
|
341
358
|
|
|
@@ -347,6 +364,7 @@ export async function resolveOpenFilesSource(options: SourceResolveOptions): Pro
|
|
|
347
364
|
start_offset: chunk.start_offset,
|
|
348
365
|
end_offset: chunk.end_offset,
|
|
349
366
|
evidence: chunk.evidence,
|
|
367
|
+
provenance: chunk.provenance,
|
|
350
368
|
}));
|
|
351
369
|
|
|
352
370
|
recordAuditEvent(db, {
|