@hasna/knowledge 0.2.8 → 0.2.10

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/src/cli.ts CHANGED
@@ -5,14 +5,9 @@
5
5
  * Licensed under the Apache License, Version 2.0
6
6
  */
7
7
  import { defaultStorePath, loadStore, saveStore, withLock, makeId, makeShortId, ensureStore, type KnowledgeItem } from './store';
8
- import { ensureKnowledgeWorkspace, readKnowledgeConfig, resolveScopedWorkspace } from './workspace';
9
- import { getKnowledgeDbStats, migrateKnowledgeDb, openKnowledgeDb } from './knowledge-db';
10
- import { createArtifactStore } from './artifact-store';
11
- import { initializeWikiLayout } from './wiki-layout';
12
- import { ingestOpenFilesManifest } from './manifest-ingest';
13
- import { consumeOpenFilesOutbox } from './outbox-consume';
14
- import { resolveOpenFilesSource } from './source-resolver';
15
- import { approvalStatus, assertS3ReadAllowed, assertWebSearchAllowed, createApprovalGate, recordAuditEvent, recordRedactionFindings, redactSecrets, resolveSafetyPolicy } from './safety';
8
+ import { openKnowledgeDb } from './knowledge-db';
9
+ import { createKnowledgeService } from './service';
10
+ import { approvalStatus, assertS3ReadAllowed, assertWebSearchAllowed, createApprovalGate, recordAuditEvent, recordRedactionFindings, redactSecrets } from './safety';
16
11
  import pkg from '../package.json' with { type: 'json' };
17
12
 
18
13
  type LogLevel = 'debug' | 'info' | 'warn' | 'error';
@@ -170,6 +165,7 @@ Commands:
170
165
  wiki init Initialize scalable wiki/schema/index/log artifacts
171
166
  source resolve <source-ref> Resolve read-only source content and citation evidence
172
167
  ingest manifest <file|s3://> Ingest an open-files manifest into knowledge.db
168
+ ingest source <source-ref> Ingest a read-only source ref into knowledge.db
173
169
  reindex outbox <file|s3://> Consume open-files change events and invalidate chunks
174
170
  safety status|check|approve|audit|redact
175
171
  help [command] Show help
@@ -235,7 +231,7 @@ function printCommandHelp(command: string): void {
235
231
  if (command === 'db') { console.log('Usage: open-knowledge db init|stats [--scope local|global|project] [--json]'); return; }
236
232
  if (command === 'wiki') { console.log('Usage: open-knowledge wiki init [--scope local|global|project] [--json]'); return; }
237
233
  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; }
238
- if (command === 'ingest') { console.log('Usage: open-knowledge ingest manifest <file|s3://bucket/key> [--scope local|global|project] [--json]'); return; }
234
+ if (command === 'ingest') { console.log('Usage: open-knowledge ingest manifest <file|s3://bucket/key> | source <source-ref> [--purpose knowledge_index] [--scope local|global|project] [--json]'); return; }
239
235
  if (command === 'reindex') { console.log('Usage: open-knowledge reindex outbox <file|s3://bucket/key> [--scope local|global|project] [--json]'); return; }
240
236
  if (command === 'safety') { console.log('Usage: open-knowledge safety status|check|approve|audit|redact [args] [--scope local|global|project] [--json]'); return; }
241
237
  printGlobalHelp();
@@ -297,71 +293,49 @@ async function run(argv: string[]): Promise<void> {
297
293
 
298
294
  if (!command || flags.help || command === 'help') { printCommandHelp(positional[1]); return; }
299
295
 
300
- const workspace = resolveScopedWorkspace(flags.scope);
296
+ const service = createKnowledgeService({ scope: flags.scope });
301
297
  let storePath = flags.store;
302
298
  if (!storePath) {
303
299
  if (flags.scope === 'project' || flags.scope === 'local') {
304
- storePath = ensureKnowledgeWorkspace(workspace.home).jsonStorePath;
300
+ storePath = service.jsonStorePath();
305
301
  } else {
306
302
  storePath = defaultStorePath();
307
303
  }
308
304
  }
309
305
 
310
306
  if (command === 'paths') {
311
- const resolvedWorkspace = ensureKnowledgeWorkspace(workspace.home);
312
- output({
313
- ok: true,
314
- scope: flags.scope ?? 'global',
315
- home: resolvedWorkspace.home,
316
- config_path: resolvedWorkspace.configPath,
317
- json_store_path: resolvedWorkspace.jsonStorePath,
318
- knowledge_db_path: resolvedWorkspace.knowledgeDbPath,
319
- artifacts_dir: resolvedWorkspace.artifactsDir,
320
- indexes_dir: resolvedWorkspace.indexesDir,
321
- logs_dir: resolvedWorkspace.logsDir,
322
- runs_dir: resolvedWorkspace.runsDir,
323
- schemas_dir: resolvedWorkspace.schemasDir,
324
- wiki_dir: resolvedWorkspace.wikiDir,
325
- config: readKnowledgeConfig(resolvedWorkspace.configPath),
326
- message: resolvedWorkspace.home,
327
- }, flags.json);
307
+ output(service.paths(), flags.json);
328
308
  return;
329
309
  }
330
310
 
331
311
  if (command === 'db') {
332
312
  const action = positional[1] ?? 'init';
333
- const resolvedWorkspace = ensureKnowledgeWorkspace(workspace.home);
334
313
  if (action !== 'init' && action !== 'stats') {
335
314
  throw new Error("Invalid db action. Use 'init' or 'stats'.");
336
315
  }
337
316
  if (action === 'init') {
338
- const result = migrateKnowledgeDb(resolvedWorkspace.knowledgeDbPath);
317
+ const result = service.initDb();
339
318
  output({ ok: true, ...result, message: `Initialized ${result.path}` }, flags.json);
340
319
  return;
341
320
  }
342
- migrateKnowledgeDb(resolvedWorkspace.knowledgeDbPath);
343
- const stats = getKnowledgeDbStats(resolvedWorkspace.knowledgeDbPath);
344
- output({ ok: true, path: resolvedWorkspace.knowledgeDbPath, ...stats, message: `knowledge.db schema v${stats.schema_version}` }, flags.json);
321
+ const stats = service.dbStats();
322
+ output({ ok: true, path: service.workspace.knowledgeDbPath, ...stats, message: `knowledge.db schema v${stats.schema_version}` }, flags.json);
345
323
  return;
346
324
  }
347
325
 
348
326
  if (command === 'wiki') {
349
327
  const action = positional[1] ?? 'init';
350
328
  if (action !== 'init') throw new Error("Invalid wiki action. Use 'init'.");
351
- const resolvedWorkspace = ensureKnowledgeWorkspace(workspace.home);
352
- const config = readKnowledgeConfig(resolvedWorkspace.configPath);
353
- const artifactStore = createArtifactStore(config, resolvedWorkspace);
354
- const result = await initializeWikiLayout(artifactStore);
355
- output({ ok: true, ...result, message: `Initialized wiki layout in ${resolvedWorkspace.home}` }, flags.json);
329
+ const result = await service.initWiki();
330
+ output({ ok: true, ...result, message: `Initialized wiki layout in ${service.workspace.home}` }, flags.json);
356
331
  return;
357
332
  }
358
333
 
359
334
  if (command === 'safety') {
360
335
  const action = positional[1] ?? 'status';
361
- const resolvedWorkspace = ensureKnowledgeWorkspace(workspace.home);
362
- const config = readKnowledgeConfig(resolvedWorkspace.configPath);
363
- const policy = resolveSafetyPolicy(config, resolvedWorkspace);
364
- migrateKnowledgeDb(resolvedWorkspace.knowledgeDbPath);
336
+ const resolvedWorkspace = service.ensureWorkspace();
337
+ const policy = service.safetyPolicy();
338
+ service.initDb();
365
339
  const db = openKnowledgeDb(resolvedWorkspace.knowledgeDbPath);
366
340
  try {
367
341
  if (action === 'status') {
@@ -487,15 +461,9 @@ async function run(argv: string[]): Promise<void> {
487
461
  if (action !== 'resolve') throw new Error("Invalid source action. Use 'resolve'.");
488
462
  const sourceRef = positional[2];
489
463
  if (!sourceRef) throw new Error('Usage: open-knowledge source resolve <source-ref>');
490
- const resolvedWorkspace = ensureKnowledgeWorkspace(workspace.home);
491
- const config = readKnowledgeConfig(resolvedWorkspace.configPath);
492
- const safetyPolicy = resolveSafetyPolicy(config, resolvedWorkspace);
493
- const result = await resolveOpenFilesSource({
494
- dbPath: resolvedWorkspace.knowledgeDbPath,
495
- sourceRef,
464
+ const result = await service.resolveSource(sourceRef, {
496
465
  purpose: flags.purpose,
497
466
  limit: flags.limit,
498
- safetyPolicy,
499
467
  });
500
468
  output({
501
469
  ok: true,
@@ -509,20 +477,21 @@ async function run(argv: string[]): Promise<void> {
509
477
 
510
478
  if (command === 'ingest') {
511
479
  const action = positional[1] ?? '';
512
- if (action !== 'manifest') throw new Error("Invalid ingest action. Use 'manifest'.");
513
- const input = positional[2];
514
- if (!input) throw new Error('Usage: open-knowledge ingest manifest <file|s3://bucket/key>');
515
- const resolvedWorkspace = ensureKnowledgeWorkspace(workspace.home);
516
- const config = readKnowledgeConfig(resolvedWorkspace.configPath);
517
- const safetyPolicy = resolveSafetyPolicy(config, resolvedWorkspace);
518
- const result = await ingestOpenFilesManifest({
519
- dbPath: resolvedWorkspace.knowledgeDbPath,
520
- input,
521
- config,
522
- safetyPolicy,
523
- });
524
- output({ ok: true, ...result, message: `Ingested ${result.items_seen} manifest item(s)` }, flags.json);
525
- return;
480
+ if (action === 'manifest') {
481
+ const input = positional[2];
482
+ if (!input) throw new Error('Usage: open-knowledge ingest manifest <file|s3://bucket/key>');
483
+ const result = await service.ingestManifest(input);
484
+ output({ ok: true, ...result, message: `Ingested ${result.items_seen} manifest item(s)` }, flags.json);
485
+ return;
486
+ }
487
+ if (action === 'source') {
488
+ const sourceRef = positional[2];
489
+ if (!sourceRef) throw new Error('Usage: open-knowledge ingest source <source-ref>');
490
+ const result = await service.ingestSource(sourceRef, flags.purpose);
491
+ output({ ok: true, ...result, message: `Ingested source ${result.source_ref} (${result.chunks_inserted} chunks)` }, flags.json);
492
+ return;
493
+ }
494
+ throw new Error("Invalid ingest action. Use 'manifest' or 'source'.");
526
495
  }
527
496
 
528
497
  if (command === 'reindex') {
@@ -530,15 +499,7 @@ async function run(argv: string[]): Promise<void> {
530
499
  if (action !== 'outbox') throw new Error("Invalid reindex action. Use 'outbox'.");
531
500
  const input = positional[2];
532
501
  if (!input) throw new Error('Usage: open-knowledge reindex outbox <file|s3://bucket/key>');
533
- const resolvedWorkspace = ensureKnowledgeWorkspace(workspace.home);
534
- const config = readKnowledgeConfig(resolvedWorkspace.configPath);
535
- const safetyPolicy = resolveSafetyPolicy(config, resolvedWorkspace);
536
- const result = await consumeOpenFilesOutbox({
537
- dbPath: resolvedWorkspace.knowledgeDbPath,
538
- input,
539
- config,
540
- safetyPolicy,
541
- });
502
+ const result = await service.consumeOutbox(input);
542
503
  output({ ok: true, ...result, message: `Consumed ${result.events_seen} outbox event(s)` }, flags.json);
543
504
  return;
544
505
  }
@@ -24,6 +24,17 @@ export interface ManifestIngestOptions {
24
24
  chunkOverlapChars?: number;
25
25
  }
26
26
 
27
+ export interface ManifestItemsIngestOptions {
28
+ dbPath: string;
29
+ items: ManifestObject[];
30
+ sourceLabel: string;
31
+ readAction?: string;
32
+ safetyPolicy?: SafetyPolicy;
33
+ now?: Date;
34
+ maxChunkChars?: number;
35
+ chunkOverlapChars?: number;
36
+ }
37
+
27
38
  export interface ManifestIngestResult {
28
39
  path: string;
29
40
  db_path: string;
@@ -36,7 +47,7 @@ export interface ManifestIngestResult {
36
47
  skipped: number;
37
48
  }
38
49
 
39
- type ManifestObject = Record<string, unknown>;
50
+ export type ManifestObject = Record<string, unknown>;
40
51
 
41
52
  interface NormalizedManifestItem {
42
53
  raw: ManifestObject;
@@ -405,6 +416,23 @@ function insertChunks(db: Database, sourceRevisionId: string, item: NormalizedMa
405
416
  }
406
417
 
407
418
  export async function ingestOpenFilesManifest(options: ManifestIngestOptions): Promise<ManifestIngestResult> {
419
+ const now = options.now ?? new Date();
420
+ if (options.safetyPolicy) assertWriteAllowed(options.dbPath, options.safetyPolicy);
421
+ migrateKnowledgeDb(options.dbPath);
422
+ const text = await readManifestInput(options.input, options.config, options.safetyPolicy);
423
+ const items = parseManifestText(text);
424
+ return ingestOpenFilesManifestItems({
425
+ dbPath: options.dbPath,
426
+ items,
427
+ sourceLabel: options.input,
428
+ safetyPolicy: options.safetyPolicy,
429
+ now,
430
+ maxChunkChars: options.maxChunkChars,
431
+ chunkOverlapChars: options.chunkOverlapChars,
432
+ });
433
+ }
434
+
435
+ export async function ingestOpenFilesManifestItems(options: ManifestItemsIngestOptions): Promise<ManifestIngestResult> {
408
436
  const now = (options.now ?? new Date()).toISOString();
409
437
  const maxChunkChars = options.maxChunkChars ?? 4000;
410
438
  const chunkOverlapChars = options.chunkOverlapChars ?? 200;
@@ -413,8 +441,6 @@ export async function ingestOpenFilesManifest(options: ManifestIngestOptions): P
413
441
 
414
442
  if (options.safetyPolicy) assertWriteAllowed(options.dbPath, options.safetyPolicy);
415
443
  migrateKnowledgeDb(options.dbPath);
416
- const text = await readManifestInput(options.input, options.config, options.safetyPolicy);
417
- const items = parseManifestText(text);
418
444
  const db = openKnowledgeDb(options.dbPath);
419
445
  try {
420
446
  const result = db.transaction(() => {
@@ -426,13 +452,13 @@ export async function ingestOpenFilesManifest(options: ManifestIngestOptions): P
426
452
  let skipped = 0;
427
453
  recordAuditEvent(db, {
428
454
  event_type: 'source_read',
429
- action: options.input.startsWith('s3://') ? 's3_manifest_read' : 'local_manifest_read',
430
- target_uri: options.input,
455
+ action: options.readAction ?? (options.sourceLabel.startsWith('s3://') ? 's3_manifest_read' : 'local_manifest_read'),
456
+ target_uri: options.sourceLabel,
431
457
  decision: 'allow',
432
- metadata: { items: items.length, read_only: true },
458
+ metadata: { items: options.items.length, read_only: true },
433
459
  created_at: now,
434
460
  });
435
- for (const raw of items) {
461
+ for (const raw of options.items) {
436
462
  const item = normalizeManifestItem(raw, now);
437
463
  const sourceId = upsertSource(db, item, now);
438
464
  const revisionId = upsertRevision(db, sourceId, item, now);
@@ -450,13 +476,13 @@ export async function ingestOpenFilesManifest(options: ManifestIngestOptions): P
450
476
  action: 'knowledge_manifest_ingest',
451
477
  target_uri: options.dbPath,
452
478
  decision: 'allow',
453
- metadata: { items: items.length, sources: seenSources.size, revisions: seenRevisions.size, chunks_inserted: chunksInserted, redactions },
479
+ metadata: { items: options.items.length, sources: seenSources.size, revisions: seenRevisions.size, chunks_inserted: chunksInserted, redactions },
454
480
  created_at: now,
455
481
  });
456
482
  return {
457
- path: options.input,
483
+ path: options.sourceLabel,
458
484
  db_path: options.dbPath,
459
- items_seen: items.length,
485
+ items_seen: options.items.length,
460
486
  sources_upserted: seenSources.size,
461
487
  revisions_upserted: seenRevisions.size,
462
488
  chunks_inserted: chunksInserted,
package/src/mcp.js CHANGED
@@ -5,10 +5,8 @@ import { z } from 'zod';
5
5
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
6
6
  import pkg from '../package.json' with { type: 'json' };
7
7
  import { defaultStorePath, loadStore, saveStore, makeId, withLock } from './store.ts';
8
- import { ensureKnowledgeWorkspace, readKnowledgeConfig, resolveScopedWorkspace } from './workspace.ts';
9
8
  import { parseSourceRef } from './source-ref.ts';
10
- import { resolveOpenFilesSource } from './source-resolver.ts';
11
- import { resolveSafetyPolicy } from './safety.ts';
9
+ import { createKnowledgeService } from './service.ts';
12
10
 
13
11
  const storePathField = z.string().optional().describe('Path to the JSON store file');
14
12
  const scopeField = z.enum(['local', 'global', 'project']).optional().describe('Workspace scope');
@@ -28,7 +26,7 @@ function shortIdFor(id) {
28
26
  function resolveStorePath(storePath, scope) {
29
27
  if (storePath) return storePath;
30
28
  if (scope === 'project' || scope === 'local') {
31
- return ensureKnowledgeWorkspace(resolveScopedWorkspace(scope).home).jsonStorePath;
29
+ return createKnowledgeService({ scope }).jsonStorePath();
32
30
  }
33
31
  return defaultStorePath();
34
32
  }
@@ -76,22 +74,7 @@ export function buildServer() {
76
74
  registerTool(server, 'ok_paths', 'Knowledge workspace paths', 'Show resolved workspace and store paths', {
77
75
  scope: scopeField,
78
76
  }, async ({ scope }) => {
79
- const workspace = ensureKnowledgeWorkspace(resolveScopedWorkspace(scope).home);
80
- return jsonText({
81
- ok: true,
82
- scope: scope ?? 'global',
83
- home: workspace.home,
84
- config_path: workspace.configPath,
85
- json_store_path: workspace.jsonStorePath,
86
- knowledge_db_path: workspace.knowledgeDbPath,
87
- artifacts_dir: workspace.artifactsDir,
88
- indexes_dir: workspace.indexesDir,
89
- logs_dir: workspace.logsDir,
90
- runs_dir: workspace.runsDir,
91
- schemas_dir: workspace.schemasDir,
92
- wiki_dir: workspace.wikiDir,
93
- config: readKnowledgeConfig(workspace.configPath),
94
- });
77
+ return jsonText(createKnowledgeService({ scope }).paths());
95
78
  });
96
79
 
97
80
  registerTool(server, 'ok_parse_source_ref', 'Parse source reference', 'Parse and validate an open-files, S3, file, or web source ref', {
@@ -110,16 +93,11 @@ export function buildServer() {
110
93
  limit: z.number().optional().describe('Maximum chunks to return, default 10'),
111
94
  scope: scopeField,
112
95
  }, async ({ source_ref, purpose, limit, scope }) => {
113
- const workspace = ensureKnowledgeWorkspace(resolveScopedWorkspace(scope).home);
114
- const config = readKnowledgeConfig(workspace.configPath);
115
- const safetyPolicy = resolveSafetyPolicy(config, workspace);
96
+ const service = createKnowledgeService({ scope });
116
97
  try {
117
- const result = await resolveOpenFilesSource({
118
- dbPath: workspace.knowledgeDbPath,
119
- sourceRef: source_ref,
98
+ const result = await service.resolveSource(source_ref, {
120
99
  purpose,
121
100
  limit,
122
- safetyPolicy,
123
101
  });
124
102
  return jsonText({ ok: true, ...result });
125
103
  } catch (error) {
package/src/service.ts ADDED
@@ -0,0 +1,157 @@
1
+ import { createArtifactStore } from './artifact-store';
2
+ import { consumeOpenFilesOutbox } from './outbox-consume';
3
+ import { getKnowledgeDbStats, migrateKnowledgeDb } from './knowledge-db';
4
+ import { ingestOpenFilesManifest } from './manifest-ingest';
5
+ import { ingestSourceRef } from './source-ingest';
6
+ import { resolveOpenFilesSource } from './source-resolver';
7
+ import { resolveSafetyPolicy } from './safety';
8
+ import { initializeWikiLayout } from './wiki-layout';
9
+ import {
10
+ ensureKnowledgeWorkspace,
11
+ readKnowledgeConfig,
12
+ resolveScopedWorkspace,
13
+ type KnowledgeConfig,
14
+ type KnowledgeWorkspace,
15
+ } from './workspace';
16
+
17
+ export interface KnowledgeServiceOptions {
18
+ scope?: string;
19
+ cwd?: string;
20
+ }
21
+
22
+ export interface KnowledgePathsResult {
23
+ ok: true;
24
+ scope: string;
25
+ home: string;
26
+ config_path: string;
27
+ json_store_path: string;
28
+ knowledge_db_path: string;
29
+ artifacts_dir: string;
30
+ indexes_dir: string;
31
+ logs_dir: string;
32
+ runs_dir: string;
33
+ schemas_dir: string;
34
+ wiki_dir: string;
35
+ config: KnowledgeConfig;
36
+ message: string;
37
+ }
38
+
39
+ export class KnowledgeService {
40
+ private ensuredWorkspace?: KnowledgeWorkspace;
41
+ private cachedConfig?: KnowledgeConfig;
42
+
43
+ constructor(private readonly options: KnowledgeServiceOptions = {}) {}
44
+
45
+ get scope(): string {
46
+ return this.options.scope ?? 'global';
47
+ }
48
+
49
+ get workspace(): KnowledgeWorkspace {
50
+ return this.ensuredWorkspace ?? resolveScopedWorkspace(this.options.scope, this.options.cwd);
51
+ }
52
+
53
+ ensureWorkspace(): KnowledgeWorkspace {
54
+ if (!this.ensuredWorkspace) this.ensuredWorkspace = ensureKnowledgeWorkspace(this.workspace.home);
55
+ return this.ensuredWorkspace;
56
+ }
57
+
58
+ jsonStorePath(): string {
59
+ return this.ensureWorkspace().jsonStorePath;
60
+ }
61
+
62
+ config(): KnowledgeConfig {
63
+ if (!this.cachedConfig) {
64
+ const workspace = this.ensureWorkspace();
65
+ this.cachedConfig = readKnowledgeConfig(workspace.configPath);
66
+ }
67
+ return this.cachedConfig;
68
+ }
69
+
70
+ safetyPolicy() {
71
+ return resolveSafetyPolicy(this.config(), this.ensureWorkspace());
72
+ }
73
+
74
+ artifactStore() {
75
+ return createArtifactStore(this.config(), this.ensureWorkspace());
76
+ }
77
+
78
+ paths(): KnowledgePathsResult {
79
+ const workspace = this.ensureWorkspace();
80
+ return {
81
+ ok: true,
82
+ scope: this.scope,
83
+ home: workspace.home,
84
+ config_path: workspace.configPath,
85
+ json_store_path: workspace.jsonStorePath,
86
+ knowledge_db_path: workspace.knowledgeDbPath,
87
+ artifacts_dir: workspace.artifactsDir,
88
+ indexes_dir: workspace.indexesDir,
89
+ logs_dir: workspace.logsDir,
90
+ runs_dir: workspace.runsDir,
91
+ schemas_dir: workspace.schemasDir,
92
+ wiki_dir: workspace.wikiDir,
93
+ config: this.config(),
94
+ message: workspace.home,
95
+ };
96
+ }
97
+
98
+ initDb() {
99
+ return migrateKnowledgeDb(this.ensureWorkspace().knowledgeDbPath);
100
+ }
101
+
102
+ dbStats() {
103
+ const workspace = this.ensureWorkspace();
104
+ migrateKnowledgeDb(workspace.knowledgeDbPath);
105
+ return getKnowledgeDbStats(workspace.knowledgeDbPath);
106
+ }
107
+
108
+ async initWiki() {
109
+ return initializeWikiLayout(this.artifactStore());
110
+ }
111
+
112
+ async ingestManifest(input: string) {
113
+ const workspace = this.ensureWorkspace();
114
+ return ingestOpenFilesManifest({
115
+ dbPath: workspace.knowledgeDbPath,
116
+ input,
117
+ config: this.config(),
118
+ safetyPolicy: this.safetyPolicy(),
119
+ });
120
+ }
121
+
122
+ async ingestSource(sourceRef: string, purpose?: string) {
123
+ const workspace = this.ensureWorkspace();
124
+ return ingestSourceRef({
125
+ dbPath: workspace.knowledgeDbPath,
126
+ sourceRef,
127
+ purpose,
128
+ config: this.config(),
129
+ safetyPolicy: this.safetyPolicy(),
130
+ });
131
+ }
132
+
133
+ async resolveSource(sourceRef: string, options: { purpose?: string; limit?: number } = {}) {
134
+ const workspace = this.ensureWorkspace();
135
+ return resolveOpenFilesSource({
136
+ dbPath: workspace.knowledgeDbPath,
137
+ sourceRef,
138
+ purpose: options.purpose,
139
+ limit: options.limit,
140
+ safetyPolicy: this.safetyPolicy(),
141
+ });
142
+ }
143
+
144
+ async consumeOutbox(input: string) {
145
+ const workspace = this.ensureWorkspace();
146
+ return consumeOpenFilesOutbox({
147
+ dbPath: workspace.knowledgeDbPath,
148
+ input,
149
+ config: this.config(),
150
+ safetyPolicy: this.safetyPolicy(),
151
+ });
152
+ }
153
+ }
154
+
155
+ export function createKnowledgeService(options: KnowledgeServiceOptions = {}): KnowledgeService {
156
+ return new KnowledgeService(options);
157
+ }