@objectstack/service-knowledge 6.8.0 → 6.8.1

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 CHANGED
@@ -6,3 +6,7 @@ Orchestrator implementing `IKnowledgeService` over pluggable
6
6
  `knowledge-ragflow`, …).
7
7
 
8
8
  See [`content/docs/protocol/knowledge.mdx`](../../../content/docs/protocol/knowledge.mdx).
9
+
10
+ ## License
11
+
12
+ Apache-2.0. See [LICENSING.md](../../../LICENSING.md).
package/dist/index.cjs CHANGED
@@ -103,7 +103,7 @@ var KnowledgeService = class {
103
103
  };
104
104
  }
105
105
  const objSource = source.source;
106
- const adminCtx = { roles: [], permissions: [], isSystem: true };
106
+ const adminCtx = { roles: [], permissions: [], systemPermissions: [], isSystem: true };
107
107
  const records = await this.options.dataEngine.find(objSource.object, {
108
108
  where: objSource.where,
109
109
  limit: opts.limit,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/knowledge-service.ts","../src/knowledge-service-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport { KnowledgeService, documentIdFor, recordToDocument } from './knowledge-service.js';\nexport type {\n KnowledgeLogger,\n KnowledgeServiceOptions,\n} from './knowledge-service.js';\nexport { KnowledgeServicePlugin } from './knowledge-service-plugin.js';\nexport type { KnowledgeServicePluginOptions } from './knowledge-service-plugin.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type {\n IDataEngine,\n IKnowledgeAdapter,\n IKnowledgeService,\n KnowledgeReindexOptions,\n KnowledgeReindexResult,\n KnowledgeSearchOptions,\n} from '@objectstack/spec/contracts';\nimport type { ExecutionContext } from '@objectstack/spec/kernel';\nimport type {\n KnowledgeDocument,\n KnowledgeHit,\n KnowledgeSource,\n ObjectKnowledgeSource,\n} from '@objectstack/spec/ai';\n\n/**\n * Minimal logger shape; falls back to no-op when none is provided.\n */\nexport interface KnowledgeLogger {\n info?(msg: string, ...rest: unknown[]): void;\n warn?(msg: string, ...rest: unknown[]): void;\n error?(msg: string, ...rest: unknown[]): void;\n debug?(msg: string, ...rest: unknown[]): void;\n}\n\n/**\n * Constructor options for `KnowledgeService`.\n */\nexport interface KnowledgeServiceOptions {\n /** Data engine used for RLS re-checks and bulk reindex walks. Optional in tests. */\n dataEngine?: IDataEngine;\n /** Optional structured logger. */\n logger?: KnowledgeLogger;\n /**\n * Default top-K when callers don't specify. The adapter may cap\n * further; the service does not enforce an upper bound itself.\n * @default 10\n */\n defaultTopK?: number;\n}\n\n/**\n * `KnowledgeService` — production `IKnowledgeService` implementation.\n *\n * Responsibilities (the parts the framework owns):\n * - Routes search / index calls to the right `IKnowledgeAdapter`.\n * - Re-checks every hit's `sourceRecordId` against the caller's\n * `ExecutionContext` so row-level security is preserved end-to-end.\n * - Walks ObjectQL when reindexing an `object` source.\n *\n * Non-responsibilities (the parts plugins own):\n * - Chunking, embedding, vector storage, hybrid retrieval, rerank.\n */\nexport class KnowledgeService implements IKnowledgeService {\n private readonly adapters = new Map<string, IKnowledgeAdapter>();\n private readonly sources = new Map<string, KnowledgeSource>();\n private readonly defaultTopK: number;\n\n constructor(private readonly options: KnowledgeServiceOptions = {}) {\n this.defaultTopK = options.defaultTopK ?? 10;\n }\n\n // ── Adapter registry ──────────────────────────────────────────────\n\n registerAdapter(id: string, adapter: IKnowledgeAdapter): void {\n this.adapters.set(id, adapter);\n this.options.logger?.info?.(`[knowledge] adapter registered: ${id}`);\n }\n\n getAdapter(id: string): IKnowledgeAdapter {\n const adapter = this.adapters.get(id);\n if (!adapter) {\n throw new Error(\n `[knowledge] unknown adapter '${id}'. Registered: [${[...this.adapters.keys()].join(', ')}]`,\n );\n }\n return adapter;\n }\n\n listAdapters(): string[] {\n return [...this.adapters.keys()];\n }\n\n // ── Source registry ───────────────────────────────────────────────\n\n registerSource(source: KnowledgeSource): void {\n if (this.sources.has(source.id)) {\n this.options.logger?.warn?.(`[knowledge] source overwritten: ${source.id}`);\n }\n this.sources.set(source.id, source);\n this.options.logger?.info?.(\n `[knowledge] source registered: ${source.id} (adapter=${source.adapter}, kind=${source.source.kind})`,\n );\n }\n\n unregisterSource(sourceId: string): void {\n this.sources.delete(sourceId);\n }\n\n listSources(): KnowledgeSource[] {\n return [...this.sources.values()];\n }\n\n getSource(sourceId: string): KnowledgeSource | undefined {\n return this.sources.get(sourceId);\n }\n\n // ── Document mutations ────────────────────────────────────────────\n\n async indexDocument(sourceId: string, doc: KnowledgeDocument): Promise<void> {\n const source = this.requireSource(sourceId);\n const adapter = this.getAdapter(source.adapter);\n await adapter.upsert([doc], { source, reason: 'manual' });\n }\n\n async deleteDocument(sourceId: string, documentId: string): Promise<void> {\n const source = this.requireSource(sourceId);\n const adapter = this.getAdapter(source.adapter);\n await adapter.delete([documentId], { source, reason: 'manual' });\n }\n\n // ── Reindex ───────────────────────────────────────────────────────\n\n async reindexSource(\n sourceId: string,\n opts: KnowledgeReindexOptions = {},\n ): Promise<KnowledgeReindexResult> {\n const source = this.requireSource(sourceId);\n const adapter = this.getAdapter(source.adapter);\n\n if (source.source.kind !== 'object') {\n // File / HTTP sources delegate to adapter-internal ingestion.\n return {\n ok: false,\n discovered: 0,\n indexed: 0,\n message:\n `Reindex for kind=${source.source.kind} is not handled by KnowledgeService. ` +\n `Trigger ingestion through the adapter (${source.adapter}) directly.`,\n };\n }\n\n if (!this.options.dataEngine) {\n return {\n ok: false,\n discovered: 0,\n indexed: 0,\n message: 'KnowledgeService has no IDataEngine bound; cannot walk object source.',\n };\n }\n\n const objSource = source.source as ObjectKnowledgeSource;\n // RLS-bypassing system context — this is a server-side admin op.\n const adminCtx: ExecutionContext = { roles: [], permissions: [], isSystem: true };\n const records = (await this.options.dataEngine.find(objSource.object, {\n where: objSource.where,\n limit: opts.limit,\n context: adminCtx,\n } as never)) as Array<Record<string, unknown>>;\n\n if (opts.dryRun) {\n return {\n ok: true,\n object: objSource.object,\n discovered: records.length,\n indexed: 0,\n message: 'dryRun=true; no documents pushed.',\n };\n }\n\n const docs = records.map((rec) => recordToDocument(source, objSource, rec));\n if (docs.length > 0) {\n await adapter.upsert(docs, { source, reason: 'reindex' });\n }\n return {\n ok: true,\n object: objSource.object,\n discovered: records.length,\n indexed: docs.length,\n };\n }\n\n // ── Permission-aware search ───────────────────────────────────────\n\n async search(query: string, opts: KnowledgeSearchOptions = {}): Promise<KnowledgeHit[]> {\n const topK = opts.topK ?? this.defaultTopK;\n const sources = this.resolveSearchTargets(opts.sourceIds);\n if (sources.length === 0) return [];\n\n const rawHits: KnowledgeHit[] = [];\n for (const source of sources) {\n const adapter = this.adapters.get(source.adapter);\n if (!adapter) {\n this.options.logger?.warn?.(\n `[knowledge] source '${source.id}' references unknown adapter '${source.adapter}'; skipping.`,\n );\n continue;\n }\n try {\n const hits = await adapter.search(query, {\n source,\n topK,\n filter: opts.filter,\n });\n for (const hit of hits) rawHits.push(hit);\n } catch (err) {\n this.options.logger?.error?.(\n `[knowledge] adapter '${source.adapter}' search failed for source '${source.id}': ${(err as Error).message}`,\n );\n }\n }\n\n const filtered = await this.applyPermissionFilter(rawHits, opts.executionContext);\n filtered.sort((a, b) => b.score - a.score);\n return filtered.slice(0, topK);\n }\n\n // ── Sync entrypoints (called by the host plugin's event bridge) ───\n\n /**\n * Apply an ObjectQL `record.created` / `record.updated` event to\n * every `object` source bound to the matching object. Failures are\n * logged but never thrown — sync must not block writes.\n */\n async handleRecordUpsert(object: string, record: Record<string, unknown>): Promise<void> {\n const targets = this.sourcesForObject(object);\n for (const source of targets) {\n try {\n const objSource = source.source as ObjectKnowledgeSource;\n const doc = recordToDocument(source, objSource, record);\n const adapter = this.getAdapter(source.adapter);\n await adapter.upsert([doc], { source, reason: 'event-sync' });\n } catch (err) {\n this.options.logger?.warn?.(\n `[knowledge] event-sync upsert failed for source '${source.id}': ${(err as Error).message}`,\n );\n }\n }\n }\n\n /** Apply an ObjectQL `record.deleted` event. */\n async handleRecordDelete(object: string, recordId: string): Promise<void> {\n const targets = this.sourcesForObject(object);\n for (const source of targets) {\n try {\n const docId = documentIdFor(source.id, recordId);\n const adapter = this.getAdapter(source.adapter);\n await adapter.delete([docId], { source, reason: 'event-sync' });\n } catch (err) {\n this.options.logger?.warn?.(\n `[knowledge] event-sync delete failed for source '${source.id}': ${(err as Error).message}`,\n );\n }\n }\n }\n\n // ── Internals ─────────────────────────────────────────────────────\n\n private requireSource(sourceId: string): KnowledgeSource {\n const source = this.sources.get(sourceId);\n if (!source) {\n throw new Error(\n `[knowledge] unknown source '${sourceId}'. Registered: [${[...this.sources.keys()].join(', ')}]`,\n );\n }\n return source;\n }\n\n private resolveSearchTargets(sourceIds?: string[]): KnowledgeSource[] {\n if (!sourceIds || sourceIds.length === 0) {\n return [...this.sources.values()].filter((s) => s.aiExposed !== false);\n }\n const out: KnowledgeSource[] = [];\n for (const id of sourceIds) {\n const s = this.sources.get(id);\n if (s) out.push(s);\n else this.options.logger?.warn?.(`[knowledge] search target '${id}' not registered; skipping.`);\n }\n return out;\n }\n\n private sourcesForObject(object: string): KnowledgeSource[] {\n const out: KnowledgeSource[] = [];\n for (const source of this.sources.values()) {\n if (\n source.source.kind === 'object' &&\n (source.source as ObjectKnowledgeSource).object === object &&\n (source.refresh?.onRecordChange ?? true) !== false\n ) {\n out.push(source);\n }\n }\n return out;\n }\n\n /**\n * Drop hits whose underlying ObjectQL record the caller can't read.\n * For hits without a `sourceRecordId` (file/http sources) we keep\n * them — adapter is responsible for any ACL enforcement there.\n *\n * When the caller's context is `isSystem: true` or no context is\n * supplied, every hit passes through — preserves today's behaviour\n * for cron jobs / tests.\n */\n private async applyPermissionFilter(\n hits: KnowledgeHit[],\n ctx: ExecutionContext | undefined,\n ): Promise<KnowledgeHit[]> {\n if (!ctx || ctx.isSystem) return hits;\n if (!this.options.dataEngine) {\n this.options.logger?.warn?.(\n '[knowledge] no IDataEngine bound — dropping object-source hits to stay safe.',\n );\n return hits.filter((h) => !h.sourceRecordId);\n }\n\n // Group hits by object so we make one query per object.\n const byObject = new Map<string, KnowledgeHit[]>();\n for (const hit of hits) {\n if (!hit.sourceRecordId) continue;\n const source = this.sources.get(hit.sourceId);\n if (!source || source.source.kind !== 'object') continue;\n const objName = (source.source as ObjectKnowledgeSource).object;\n const bucket = byObject.get(objName) ?? [];\n bucket.push(hit);\n byObject.set(objName, bucket);\n }\n\n const allowed = new Set<string>(); // `${object}#${recordId}`\n for (const [object, group] of byObject) {\n const ids = [...new Set(group.map((h) => h.sourceRecordId!).filter(Boolean))];\n if (ids.length === 0) continue;\n try {\n const rows = (await this.options.dataEngine.find(object, {\n where: { id: { $in: ids } } as Record<string, unknown>,\n fields: ['id'],\n context: ctx,\n } as never)) as Array<{ id?: string }>;\n for (const row of rows) if (row?.id) allowed.add(`${object}#${row.id}`);\n } catch (err) {\n this.options.logger?.warn?.(\n `[knowledge] RLS lookup failed for object='${object}': ${(err as Error).message}; ` +\n 'dropping that object\\'s hits to stay safe.',\n );\n }\n }\n\n return hits.filter((hit) => {\n if (!hit.sourceRecordId) return true; // file / http hit\n const source = this.sources.get(hit.sourceId);\n if (!source || source.source.kind !== 'object') return true;\n const objName = (source.source as ObjectKnowledgeSource).object;\n return allowed.has(`${objName}#${hit.sourceRecordId}`);\n });\n }\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────\n\n/** Deterministic document id derived from the source + record id. */\nexport function documentIdFor(sourceId: string, recordId: string): string {\n return `${sourceId}:${recordId}`;\n}\n\n/**\n * Project an ObjectQL record into a `KnowledgeDocument` per the\n * source's `contentFields` / `metadataFields` config. Pure function.\n */\nexport function recordToDocument(\n source: KnowledgeSource,\n objSource: ObjectKnowledgeSource,\n record: Record<string, unknown>,\n): KnowledgeDocument {\n const recordId = String(record.id ?? (record as any)._id ?? '');\n const contentParts: string[] = [];\n for (const field of objSource.contentFields) {\n if (field === '*') {\n for (const [k, v] of Object.entries(record)) {\n if (typeof v === 'string' && v.length > 0 && k !== 'id') contentParts.push(v);\n }\n } else {\n const v = record[field];\n if (v != null) contentParts.push(String(v));\n }\n }\n const metadata: Record<string, unknown> = {};\n for (const field of objSource.metadataFields ?? []) {\n if (record[field] !== undefined) metadata[field] = record[field];\n }\n return {\n id: documentIdFor(source.id, recordId || `unknown-${Date.now()}`),\n sourceId: source.id,\n sourceRecordId: recordId || undefined,\n content: contentParts.join('\\n\\n'),\n title: typeof record.title === 'string' ? record.title : undefined,\n metadata,\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type {\n IDataEngine,\n IRealtimeService,\n} from '@objectstack/spec/contracts';\nimport type { KnowledgeSource } from '@objectstack/spec/ai';\nimport { KNOWLEDGE_SERVICE } from '@objectstack/spec/contracts';\nimport { KnowledgeService } from './knowledge-service.js';\nimport type { KnowledgeLogger } from './knowledge-service.js';\n\n/**\n * Configuration options for the `KnowledgeServicePlugin`.\n */\nexport interface KnowledgeServicePluginOptions {\n /**\n * Knowledge sources to register at boot. Sources may also be\n * registered programmatically later via `service.registerSource`.\n */\n sources?: KnowledgeSource[];\n /**\n * Subscribe to ObjectQL `record.*` events from `IRealtimeService`\n * for `object` sources. Defaults to `true`. Set to `false` to\n * disable inline event sync (e.g. when an external indexer drives\n * upserts).\n * @default true\n */\n enableEventSync?: boolean;\n /** Default top-K when callers omit it. @default 10 */\n defaultTopK?: number;\n}\n\n/**\n * `KnowledgeServicePlugin` — registers `IKnowledgeService` with the\n * kernel, binds it to `IDataEngine` for RLS-aware permission filtering,\n * and (optionally) subscribes to `IRealtimeService` so ObjectQL record\n * mutations automatically propagate to adapter backends.\n *\n * @example\n * ```ts\n * import { ObjectKernel } from '@objectstack/core';\n * import { KnowledgeServicePlugin } from '@objectstack/service-knowledge';\n *\n * const kernel = new ObjectKernel();\n * kernel.use(new KnowledgeServicePlugin({\n * sources: [{\n * id: 'task_notes', label: 'Task notes', adapter: 'memory',\n * source: { kind: 'object', object: 'task', contentFields: ['notes'] },\n * }],\n * }));\n * await kernel.bootstrap();\n *\n * const knowledge = kernel.getService('knowledge');\n * const hits = await knowledge.search('shopping list', { executionContext });\n * ```\n */\nexport class KnowledgeServicePlugin implements Plugin {\n name = 'com.objectstack.service.knowledge';\n version = '0.1.0';\n type = 'standard';\n\n private service: KnowledgeService | null = null;\n private subscriptionId: string | undefined;\n\n constructor(private readonly options: KnowledgeServicePluginOptions = {}) {}\n\n async init(ctx: PluginContext): Promise<void> {\n let engine: IDataEngine | undefined;\n try {\n engine = ctx.getService<IDataEngine>('objectql');\n } catch {\n // Data engine not wired — service still works in pure-search mode\n // but RLS re-checks will be conservative (drop object-source hits\n // when caller is non-system).\n }\n\n const logger: KnowledgeLogger = {\n info: (msg, ...rest) => {\n (ctx.logger as { info?: (m: string, ...r: unknown[]) => void }).info?.(msg, ...rest);\n },\n warn: (msg, ...rest) => {\n (ctx.logger as { warn?: (m: string, ...r: unknown[]) => void }).warn?.(msg, ...rest);\n },\n error: (msg, ...rest) => {\n (ctx.logger as { error?: (m: string, ...r: unknown[]) => void }).error?.(msg, ...rest);\n },\n debug: (msg, ...rest) => {\n (ctx.logger as { debug?: (m: string, ...r: unknown[]) => void }).debug?.(msg, ...rest);\n },\n };\n\n this.service = new KnowledgeService({\n dataEngine: engine,\n logger,\n defaultTopK: this.options.defaultTopK,\n });\n\n for (const source of this.options.sources ?? []) {\n this.service.registerSource(source);\n }\n\n ctx.registerService(KNOWLEDGE_SERVICE, this.service);\n ctx.logger.info?.(\n `KnowledgeServicePlugin: registered '${KNOWLEDGE_SERVICE}' service (eventSync=${\n this.options.enableEventSync !== false\n }, dataEngine=${engine ? 'yes' : 'no'})`,\n );\n }\n\n async start(ctx: PluginContext): Promise<void> {\n if (this.options.enableEventSync === false) return;\n const service = this.service;\n if (!service) return;\n\n ctx.hook('kernel:ready', async () => {\n let realtime: IRealtimeService | null = null;\n try {\n realtime = ctx.getService<IRealtimeService>('realtime');\n } catch {\n // realtime service not available — sync becomes opt-in via\n // explicit handleRecordUpsert / handleRecordDelete calls.\n ctx.logger.warn?.(\n 'KnowledgeServicePlugin: IRealtimeService unavailable — event sync disabled. ' +\n 'Adapters can still be driven manually via the service API.',\n );\n return;\n }\n\n this.subscriptionId = await realtime.subscribe('knowledge-event-sync', async (event) => {\n const object = event.object;\n if (!object) return;\n const type = event.type;\n const payload = (event.payload ?? {}) as Record<string, unknown>;\n if (\n type === 'record.created' ||\n type === 'record.updated' ||\n type === 'data.record.created' ||\n type === 'data.record.updated'\n ) {\n const record =\n (payload.record as Record<string, unknown> | undefined) ?? payload;\n if (record && typeof record === 'object') {\n await service.handleRecordUpsert(object, record as Record<string, unknown>);\n }\n return;\n }\n if (type === 'record.deleted' || type === 'data.record.deleted') {\n const recordObj = payload.record as Record<string, unknown> | undefined;\n const id =\n (payload.id as string | undefined) ?? (recordObj?.id as string | undefined);\n if (id) await service.handleRecordDelete(object, id);\n }\n });\n ctx.logger.info?.('KnowledgeServicePlugin: event sync subscription active.');\n });\n }\n\n async stop(ctx: PluginContext): Promise<void> {\n if (!this.subscriptionId) return;\n try {\n const realtime = ctx.getService<IRealtimeService>('realtime');\n await realtime.unsubscribe(this.subscriptionId);\n } catch {\n // best-effort\n }\n this.subscriptionId = undefined;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/knowledge-service.ts","../src/knowledge-service-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport { KnowledgeService, documentIdFor, recordToDocument } from './knowledge-service.js';\nexport type {\n KnowledgeLogger,\n KnowledgeServiceOptions,\n} from './knowledge-service.js';\nexport { KnowledgeServicePlugin } from './knowledge-service-plugin.js';\nexport type { KnowledgeServicePluginOptions } from './knowledge-service-plugin.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type {\n IDataEngine,\n IKnowledgeAdapter,\n IKnowledgeService,\n KnowledgeReindexOptions,\n KnowledgeReindexResult,\n KnowledgeSearchOptions,\n} from '@objectstack/spec/contracts';\nimport type { ExecutionContext } from '@objectstack/spec/kernel';\nimport type {\n KnowledgeDocument,\n KnowledgeHit,\n KnowledgeSource,\n ObjectKnowledgeSource,\n} from '@objectstack/spec/ai';\n\n/**\n * Minimal logger shape; falls back to no-op when none is provided.\n */\nexport interface KnowledgeLogger {\n info?(msg: string, ...rest: unknown[]): void;\n warn?(msg: string, ...rest: unknown[]): void;\n error?(msg: string, ...rest: unknown[]): void;\n debug?(msg: string, ...rest: unknown[]): void;\n}\n\n/**\n * Constructor options for `KnowledgeService`.\n */\nexport interface KnowledgeServiceOptions {\n /** Data engine used for RLS re-checks and bulk reindex walks. Optional in tests. */\n dataEngine?: IDataEngine;\n /** Optional structured logger. */\n logger?: KnowledgeLogger;\n /**\n * Default top-K when callers don't specify. The adapter may cap\n * further; the service does not enforce an upper bound itself.\n * @default 10\n */\n defaultTopK?: number;\n}\n\n/**\n * `KnowledgeService` — production `IKnowledgeService` implementation.\n *\n * Responsibilities (the parts the framework owns):\n * - Routes search / index calls to the right `IKnowledgeAdapter`.\n * - Re-checks every hit's `sourceRecordId` against the caller's\n * `ExecutionContext` so row-level security is preserved end-to-end.\n * - Walks ObjectQL when reindexing an `object` source.\n *\n * Non-responsibilities (the parts plugins own):\n * - Chunking, embedding, vector storage, hybrid retrieval, rerank.\n */\nexport class KnowledgeService implements IKnowledgeService {\n private readonly adapters = new Map<string, IKnowledgeAdapter>();\n private readonly sources = new Map<string, KnowledgeSource>();\n private readonly defaultTopK: number;\n\n constructor(private readonly options: KnowledgeServiceOptions = {}) {\n this.defaultTopK = options.defaultTopK ?? 10;\n }\n\n // ── Adapter registry ──────────────────────────────────────────────\n\n registerAdapter(id: string, adapter: IKnowledgeAdapter): void {\n this.adapters.set(id, adapter);\n this.options.logger?.info?.(`[knowledge] adapter registered: ${id}`);\n }\n\n getAdapter(id: string): IKnowledgeAdapter {\n const adapter = this.adapters.get(id);\n if (!adapter) {\n throw new Error(\n `[knowledge] unknown adapter '${id}'. Registered: [${[...this.adapters.keys()].join(', ')}]`,\n );\n }\n return adapter;\n }\n\n listAdapters(): string[] {\n return [...this.adapters.keys()];\n }\n\n // ── Source registry ───────────────────────────────────────────────\n\n registerSource(source: KnowledgeSource): void {\n if (this.sources.has(source.id)) {\n this.options.logger?.warn?.(`[knowledge] source overwritten: ${source.id}`);\n }\n this.sources.set(source.id, source);\n this.options.logger?.info?.(\n `[knowledge] source registered: ${source.id} (adapter=${source.adapter}, kind=${source.source.kind})`,\n );\n }\n\n unregisterSource(sourceId: string): void {\n this.sources.delete(sourceId);\n }\n\n listSources(): KnowledgeSource[] {\n return [...this.sources.values()];\n }\n\n getSource(sourceId: string): KnowledgeSource | undefined {\n return this.sources.get(sourceId);\n }\n\n // ── Document mutations ────────────────────────────────────────────\n\n async indexDocument(sourceId: string, doc: KnowledgeDocument): Promise<void> {\n const source = this.requireSource(sourceId);\n const adapter = this.getAdapter(source.adapter);\n await adapter.upsert([doc], { source, reason: 'manual' });\n }\n\n async deleteDocument(sourceId: string, documentId: string): Promise<void> {\n const source = this.requireSource(sourceId);\n const adapter = this.getAdapter(source.adapter);\n await adapter.delete([documentId], { source, reason: 'manual' });\n }\n\n // ── Reindex ───────────────────────────────────────────────────────\n\n async reindexSource(\n sourceId: string,\n opts: KnowledgeReindexOptions = {},\n ): Promise<KnowledgeReindexResult> {\n const source = this.requireSource(sourceId);\n const adapter = this.getAdapter(source.adapter);\n\n if (source.source.kind !== 'object') {\n // File / HTTP sources delegate to adapter-internal ingestion.\n return {\n ok: false,\n discovered: 0,\n indexed: 0,\n message:\n `Reindex for kind=${source.source.kind} is not handled by KnowledgeService. ` +\n `Trigger ingestion through the adapter (${source.adapter}) directly.`,\n };\n }\n\n if (!this.options.dataEngine) {\n return {\n ok: false,\n discovered: 0,\n indexed: 0,\n message: 'KnowledgeService has no IDataEngine bound; cannot walk object source.',\n };\n }\n\n const objSource = source.source as ObjectKnowledgeSource;\n // RLS-bypassing system context — this is a server-side admin op.\n const adminCtx: ExecutionContext = { roles: [], permissions: [], systemPermissions: [], isSystem: true };\n const records = (await this.options.dataEngine.find(objSource.object, {\n where: objSource.where,\n limit: opts.limit,\n context: adminCtx,\n } as never)) as Array<Record<string, unknown>>;\n\n if (opts.dryRun) {\n return {\n ok: true,\n object: objSource.object,\n discovered: records.length,\n indexed: 0,\n message: 'dryRun=true; no documents pushed.',\n };\n }\n\n const docs = records.map((rec) => recordToDocument(source, objSource, rec));\n if (docs.length > 0) {\n await adapter.upsert(docs, { source, reason: 'reindex' });\n }\n return {\n ok: true,\n object: objSource.object,\n discovered: records.length,\n indexed: docs.length,\n };\n }\n\n // ── Permission-aware search ───────────────────────────────────────\n\n async search(query: string, opts: KnowledgeSearchOptions = {}): Promise<KnowledgeHit[]> {\n const topK = opts.topK ?? this.defaultTopK;\n const sources = this.resolveSearchTargets(opts.sourceIds);\n if (sources.length === 0) return [];\n\n const rawHits: KnowledgeHit[] = [];\n for (const source of sources) {\n const adapter = this.adapters.get(source.adapter);\n if (!adapter) {\n this.options.logger?.warn?.(\n `[knowledge] source '${source.id}' references unknown adapter '${source.adapter}'; skipping.`,\n );\n continue;\n }\n try {\n const hits = await adapter.search(query, {\n source,\n topK,\n filter: opts.filter,\n });\n for (const hit of hits) rawHits.push(hit);\n } catch (err) {\n this.options.logger?.error?.(\n `[knowledge] adapter '${source.adapter}' search failed for source '${source.id}': ${(err as Error).message}`,\n );\n }\n }\n\n const filtered = await this.applyPermissionFilter(rawHits, opts.executionContext);\n filtered.sort((a, b) => b.score - a.score);\n return filtered.slice(0, topK);\n }\n\n // ── Sync entrypoints (called by the host plugin's event bridge) ───\n\n /**\n * Apply an ObjectQL `record.created` / `record.updated` event to\n * every `object` source bound to the matching object. Failures are\n * logged but never thrown — sync must not block writes.\n */\n async handleRecordUpsert(object: string, record: Record<string, unknown>): Promise<void> {\n const targets = this.sourcesForObject(object);\n for (const source of targets) {\n try {\n const objSource = source.source as ObjectKnowledgeSource;\n const doc = recordToDocument(source, objSource, record);\n const adapter = this.getAdapter(source.adapter);\n await adapter.upsert([doc], { source, reason: 'event-sync' });\n } catch (err) {\n this.options.logger?.warn?.(\n `[knowledge] event-sync upsert failed for source '${source.id}': ${(err as Error).message}`,\n );\n }\n }\n }\n\n /** Apply an ObjectQL `record.deleted` event. */\n async handleRecordDelete(object: string, recordId: string): Promise<void> {\n const targets = this.sourcesForObject(object);\n for (const source of targets) {\n try {\n const docId = documentIdFor(source.id, recordId);\n const adapter = this.getAdapter(source.adapter);\n await adapter.delete([docId], { source, reason: 'event-sync' });\n } catch (err) {\n this.options.logger?.warn?.(\n `[knowledge] event-sync delete failed for source '${source.id}': ${(err as Error).message}`,\n );\n }\n }\n }\n\n // ── Internals ─────────────────────────────────────────────────────\n\n private requireSource(sourceId: string): KnowledgeSource {\n const source = this.sources.get(sourceId);\n if (!source) {\n throw new Error(\n `[knowledge] unknown source '${sourceId}'. Registered: [${[...this.sources.keys()].join(', ')}]`,\n );\n }\n return source;\n }\n\n private resolveSearchTargets(sourceIds?: string[]): KnowledgeSource[] {\n if (!sourceIds || sourceIds.length === 0) {\n return [...this.sources.values()].filter((s) => s.aiExposed !== false);\n }\n const out: KnowledgeSource[] = [];\n for (const id of sourceIds) {\n const s = this.sources.get(id);\n if (s) out.push(s);\n else this.options.logger?.warn?.(`[knowledge] search target '${id}' not registered; skipping.`);\n }\n return out;\n }\n\n private sourcesForObject(object: string): KnowledgeSource[] {\n const out: KnowledgeSource[] = [];\n for (const source of this.sources.values()) {\n if (\n source.source.kind === 'object' &&\n (source.source as ObjectKnowledgeSource).object === object &&\n (source.refresh?.onRecordChange ?? true) !== false\n ) {\n out.push(source);\n }\n }\n return out;\n }\n\n /**\n * Drop hits whose underlying ObjectQL record the caller can't read.\n * For hits without a `sourceRecordId` (file/http sources) we keep\n * them — adapter is responsible for any ACL enforcement there.\n *\n * When the caller's context is `isSystem: true` or no context is\n * supplied, every hit passes through — preserves today's behaviour\n * for cron jobs / tests.\n */\n private async applyPermissionFilter(\n hits: KnowledgeHit[],\n ctx: ExecutionContext | undefined,\n ): Promise<KnowledgeHit[]> {\n if (!ctx || ctx.isSystem) return hits;\n if (!this.options.dataEngine) {\n this.options.logger?.warn?.(\n '[knowledge] no IDataEngine bound — dropping object-source hits to stay safe.',\n );\n return hits.filter((h) => !h.sourceRecordId);\n }\n\n // Group hits by object so we make one query per object.\n const byObject = new Map<string, KnowledgeHit[]>();\n for (const hit of hits) {\n if (!hit.sourceRecordId) continue;\n const source = this.sources.get(hit.sourceId);\n if (!source || source.source.kind !== 'object') continue;\n const objName = (source.source as ObjectKnowledgeSource).object;\n const bucket = byObject.get(objName) ?? [];\n bucket.push(hit);\n byObject.set(objName, bucket);\n }\n\n const allowed = new Set<string>(); // `${object}#${recordId}`\n for (const [object, group] of byObject) {\n const ids = [...new Set(group.map((h) => h.sourceRecordId!).filter(Boolean))];\n if (ids.length === 0) continue;\n try {\n const rows = (await this.options.dataEngine.find(object, {\n where: { id: { $in: ids } } as Record<string, unknown>,\n fields: ['id'],\n context: ctx,\n } as never)) as Array<{ id?: string }>;\n for (const row of rows) if (row?.id) allowed.add(`${object}#${row.id}`);\n } catch (err) {\n this.options.logger?.warn?.(\n `[knowledge] RLS lookup failed for object='${object}': ${(err as Error).message}; ` +\n 'dropping that object\\'s hits to stay safe.',\n );\n }\n }\n\n return hits.filter((hit) => {\n if (!hit.sourceRecordId) return true; // file / http hit\n const source = this.sources.get(hit.sourceId);\n if (!source || source.source.kind !== 'object') return true;\n const objName = (source.source as ObjectKnowledgeSource).object;\n return allowed.has(`${objName}#${hit.sourceRecordId}`);\n });\n }\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────\n\n/** Deterministic document id derived from the source + record id. */\nexport function documentIdFor(sourceId: string, recordId: string): string {\n return `${sourceId}:${recordId}`;\n}\n\n/**\n * Project an ObjectQL record into a `KnowledgeDocument` per the\n * source's `contentFields` / `metadataFields` config. Pure function.\n */\nexport function recordToDocument(\n source: KnowledgeSource,\n objSource: ObjectKnowledgeSource,\n record: Record<string, unknown>,\n): KnowledgeDocument {\n const recordId = String(record.id ?? (record as any)._id ?? '');\n const contentParts: string[] = [];\n for (const field of objSource.contentFields) {\n if (field === '*') {\n for (const [k, v] of Object.entries(record)) {\n if (typeof v === 'string' && v.length > 0 && k !== 'id') contentParts.push(v);\n }\n } else {\n const v = record[field];\n if (v != null) contentParts.push(String(v));\n }\n }\n const metadata: Record<string, unknown> = {};\n for (const field of objSource.metadataFields ?? []) {\n if (record[field] !== undefined) metadata[field] = record[field];\n }\n return {\n id: documentIdFor(source.id, recordId || `unknown-${Date.now()}`),\n sourceId: source.id,\n sourceRecordId: recordId || undefined,\n content: contentParts.join('\\n\\n'),\n title: typeof record.title === 'string' ? record.title : undefined,\n metadata,\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type {\n IDataEngine,\n IRealtimeService,\n} from '@objectstack/spec/contracts';\nimport type { KnowledgeSource } from '@objectstack/spec/ai';\nimport { KNOWLEDGE_SERVICE } from '@objectstack/spec/contracts';\nimport { KnowledgeService } from './knowledge-service.js';\nimport type { KnowledgeLogger } from './knowledge-service.js';\n\n/**\n * Configuration options for the `KnowledgeServicePlugin`.\n */\nexport interface KnowledgeServicePluginOptions {\n /**\n * Knowledge sources to register at boot. Sources may also be\n * registered programmatically later via `service.registerSource`.\n */\n sources?: KnowledgeSource[];\n /**\n * Subscribe to ObjectQL `record.*` events from `IRealtimeService`\n * for `object` sources. Defaults to `true`. Set to `false` to\n * disable inline event sync (e.g. when an external indexer drives\n * upserts).\n * @default true\n */\n enableEventSync?: boolean;\n /** Default top-K when callers omit it. @default 10 */\n defaultTopK?: number;\n}\n\n/**\n * `KnowledgeServicePlugin` — registers `IKnowledgeService` with the\n * kernel, binds it to `IDataEngine` for RLS-aware permission filtering,\n * and (optionally) subscribes to `IRealtimeService` so ObjectQL record\n * mutations automatically propagate to adapter backends.\n *\n * @example\n * ```ts\n * import { ObjectKernel } from '@objectstack/core';\n * import { KnowledgeServicePlugin } from '@objectstack/service-knowledge';\n *\n * const kernel = new ObjectKernel();\n * kernel.use(new KnowledgeServicePlugin({\n * sources: [{\n * id: 'task_notes', label: 'Task notes', adapter: 'memory',\n * source: { kind: 'object', object: 'task', contentFields: ['notes'] },\n * }],\n * }));\n * await kernel.bootstrap();\n *\n * const knowledge = kernel.getService('knowledge');\n * const hits = await knowledge.search('shopping list', { executionContext });\n * ```\n */\nexport class KnowledgeServicePlugin implements Plugin {\n name = 'com.objectstack.service.knowledge';\n version = '0.1.0';\n type = 'standard';\n\n private service: KnowledgeService | null = null;\n private subscriptionId: string | undefined;\n\n constructor(private readonly options: KnowledgeServicePluginOptions = {}) {}\n\n async init(ctx: PluginContext): Promise<void> {\n let engine: IDataEngine | undefined;\n try {\n engine = ctx.getService<IDataEngine>('objectql');\n } catch {\n // Data engine not wired — service still works in pure-search mode\n // but RLS re-checks will be conservative (drop object-source hits\n // when caller is non-system).\n }\n\n const logger: KnowledgeLogger = {\n info: (msg, ...rest) => {\n (ctx.logger as { info?: (m: string, ...r: unknown[]) => void }).info?.(msg, ...rest);\n },\n warn: (msg, ...rest) => {\n (ctx.logger as { warn?: (m: string, ...r: unknown[]) => void }).warn?.(msg, ...rest);\n },\n error: (msg, ...rest) => {\n (ctx.logger as { error?: (m: string, ...r: unknown[]) => void }).error?.(msg, ...rest);\n },\n debug: (msg, ...rest) => {\n (ctx.logger as { debug?: (m: string, ...r: unknown[]) => void }).debug?.(msg, ...rest);\n },\n };\n\n this.service = new KnowledgeService({\n dataEngine: engine,\n logger,\n defaultTopK: this.options.defaultTopK,\n });\n\n for (const source of this.options.sources ?? []) {\n this.service.registerSource(source);\n }\n\n ctx.registerService(KNOWLEDGE_SERVICE, this.service);\n ctx.logger.info?.(\n `KnowledgeServicePlugin: registered '${KNOWLEDGE_SERVICE}' service (eventSync=${\n this.options.enableEventSync !== false\n }, dataEngine=${engine ? 'yes' : 'no'})`,\n );\n }\n\n async start(ctx: PluginContext): Promise<void> {\n if (this.options.enableEventSync === false) return;\n const service = this.service;\n if (!service) return;\n\n ctx.hook('kernel:ready', async () => {\n let realtime: IRealtimeService | null = null;\n try {\n realtime = ctx.getService<IRealtimeService>('realtime');\n } catch {\n // realtime service not available — sync becomes opt-in via\n // explicit handleRecordUpsert / handleRecordDelete calls.\n ctx.logger.warn?.(\n 'KnowledgeServicePlugin: IRealtimeService unavailable — event sync disabled. ' +\n 'Adapters can still be driven manually via the service API.',\n );\n return;\n }\n\n this.subscriptionId = await realtime.subscribe('knowledge-event-sync', async (event) => {\n const object = event.object;\n if (!object) return;\n const type = event.type;\n const payload = (event.payload ?? {}) as Record<string, unknown>;\n if (\n type === 'record.created' ||\n type === 'record.updated' ||\n type === 'data.record.created' ||\n type === 'data.record.updated'\n ) {\n const record =\n (payload.record as Record<string, unknown> | undefined) ?? payload;\n if (record && typeof record === 'object') {\n await service.handleRecordUpsert(object, record as Record<string, unknown>);\n }\n return;\n }\n if (type === 'record.deleted' || type === 'data.record.deleted') {\n const recordObj = payload.record as Record<string, unknown> | undefined;\n const id =\n (payload.id as string | undefined) ?? (recordObj?.id as string | undefined);\n if (id) await service.handleRecordDelete(object, id);\n }\n });\n ctx.logger.info?.('KnowledgeServicePlugin: event sync subscription active.');\n });\n }\n\n async stop(ctx: PluginContext): Promise<void> {\n if (!this.subscriptionId) return;\n try {\n const realtime = ctx.getService<IRealtimeService>('realtime');\n await realtime.unsubscribe(this.subscriptionId);\n } catch {\n // best-effort\n }\n this.subscriptionId = undefined;\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -74,7 +74,7 @@ var KnowledgeService = class {
74
74
  };
75
75
  }
76
76
  const objSource = source.source;
77
- const adminCtx = { roles: [], permissions: [], isSystem: true };
77
+ const adminCtx = { roles: [], permissions: [], systemPermissions: [], isSystem: true };
78
78
  const records = await this.options.dataEngine.find(objSource.object, {
79
79
  where: objSource.where,
80
80
  limit: opts.limit,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/knowledge-service.ts","../src/knowledge-service-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type {\n IDataEngine,\n IKnowledgeAdapter,\n IKnowledgeService,\n KnowledgeReindexOptions,\n KnowledgeReindexResult,\n KnowledgeSearchOptions,\n} from '@objectstack/spec/contracts';\nimport type { ExecutionContext } from '@objectstack/spec/kernel';\nimport type {\n KnowledgeDocument,\n KnowledgeHit,\n KnowledgeSource,\n ObjectKnowledgeSource,\n} from '@objectstack/spec/ai';\n\n/**\n * Minimal logger shape; falls back to no-op when none is provided.\n */\nexport interface KnowledgeLogger {\n info?(msg: string, ...rest: unknown[]): void;\n warn?(msg: string, ...rest: unknown[]): void;\n error?(msg: string, ...rest: unknown[]): void;\n debug?(msg: string, ...rest: unknown[]): void;\n}\n\n/**\n * Constructor options for `KnowledgeService`.\n */\nexport interface KnowledgeServiceOptions {\n /** Data engine used for RLS re-checks and bulk reindex walks. Optional in tests. */\n dataEngine?: IDataEngine;\n /** Optional structured logger. */\n logger?: KnowledgeLogger;\n /**\n * Default top-K when callers don't specify. The adapter may cap\n * further; the service does not enforce an upper bound itself.\n * @default 10\n */\n defaultTopK?: number;\n}\n\n/**\n * `KnowledgeService` — production `IKnowledgeService` implementation.\n *\n * Responsibilities (the parts the framework owns):\n * - Routes search / index calls to the right `IKnowledgeAdapter`.\n * - Re-checks every hit's `sourceRecordId` against the caller's\n * `ExecutionContext` so row-level security is preserved end-to-end.\n * - Walks ObjectQL when reindexing an `object` source.\n *\n * Non-responsibilities (the parts plugins own):\n * - Chunking, embedding, vector storage, hybrid retrieval, rerank.\n */\nexport class KnowledgeService implements IKnowledgeService {\n private readonly adapters = new Map<string, IKnowledgeAdapter>();\n private readonly sources = new Map<string, KnowledgeSource>();\n private readonly defaultTopK: number;\n\n constructor(private readonly options: KnowledgeServiceOptions = {}) {\n this.defaultTopK = options.defaultTopK ?? 10;\n }\n\n // ── Adapter registry ──────────────────────────────────────────────\n\n registerAdapter(id: string, adapter: IKnowledgeAdapter): void {\n this.adapters.set(id, adapter);\n this.options.logger?.info?.(`[knowledge] adapter registered: ${id}`);\n }\n\n getAdapter(id: string): IKnowledgeAdapter {\n const adapter = this.adapters.get(id);\n if (!adapter) {\n throw new Error(\n `[knowledge] unknown adapter '${id}'. Registered: [${[...this.adapters.keys()].join(', ')}]`,\n );\n }\n return adapter;\n }\n\n listAdapters(): string[] {\n return [...this.adapters.keys()];\n }\n\n // ── Source registry ───────────────────────────────────────────────\n\n registerSource(source: KnowledgeSource): void {\n if (this.sources.has(source.id)) {\n this.options.logger?.warn?.(`[knowledge] source overwritten: ${source.id}`);\n }\n this.sources.set(source.id, source);\n this.options.logger?.info?.(\n `[knowledge] source registered: ${source.id} (adapter=${source.adapter}, kind=${source.source.kind})`,\n );\n }\n\n unregisterSource(sourceId: string): void {\n this.sources.delete(sourceId);\n }\n\n listSources(): KnowledgeSource[] {\n return [...this.sources.values()];\n }\n\n getSource(sourceId: string): KnowledgeSource | undefined {\n return this.sources.get(sourceId);\n }\n\n // ── Document mutations ────────────────────────────────────────────\n\n async indexDocument(sourceId: string, doc: KnowledgeDocument): Promise<void> {\n const source = this.requireSource(sourceId);\n const adapter = this.getAdapter(source.adapter);\n await adapter.upsert([doc], { source, reason: 'manual' });\n }\n\n async deleteDocument(sourceId: string, documentId: string): Promise<void> {\n const source = this.requireSource(sourceId);\n const adapter = this.getAdapter(source.adapter);\n await adapter.delete([documentId], { source, reason: 'manual' });\n }\n\n // ── Reindex ───────────────────────────────────────────────────────\n\n async reindexSource(\n sourceId: string,\n opts: KnowledgeReindexOptions = {},\n ): Promise<KnowledgeReindexResult> {\n const source = this.requireSource(sourceId);\n const adapter = this.getAdapter(source.adapter);\n\n if (source.source.kind !== 'object') {\n // File / HTTP sources delegate to adapter-internal ingestion.\n return {\n ok: false,\n discovered: 0,\n indexed: 0,\n message:\n `Reindex for kind=${source.source.kind} is not handled by KnowledgeService. ` +\n `Trigger ingestion through the adapter (${source.adapter}) directly.`,\n };\n }\n\n if (!this.options.dataEngine) {\n return {\n ok: false,\n discovered: 0,\n indexed: 0,\n message: 'KnowledgeService has no IDataEngine bound; cannot walk object source.',\n };\n }\n\n const objSource = source.source as ObjectKnowledgeSource;\n // RLS-bypassing system context — this is a server-side admin op.\n const adminCtx: ExecutionContext = { roles: [], permissions: [], isSystem: true };\n const records = (await this.options.dataEngine.find(objSource.object, {\n where: objSource.where,\n limit: opts.limit,\n context: adminCtx,\n } as never)) as Array<Record<string, unknown>>;\n\n if (opts.dryRun) {\n return {\n ok: true,\n object: objSource.object,\n discovered: records.length,\n indexed: 0,\n message: 'dryRun=true; no documents pushed.',\n };\n }\n\n const docs = records.map((rec) => recordToDocument(source, objSource, rec));\n if (docs.length > 0) {\n await adapter.upsert(docs, { source, reason: 'reindex' });\n }\n return {\n ok: true,\n object: objSource.object,\n discovered: records.length,\n indexed: docs.length,\n };\n }\n\n // ── Permission-aware search ───────────────────────────────────────\n\n async search(query: string, opts: KnowledgeSearchOptions = {}): Promise<KnowledgeHit[]> {\n const topK = opts.topK ?? this.defaultTopK;\n const sources = this.resolveSearchTargets(opts.sourceIds);\n if (sources.length === 0) return [];\n\n const rawHits: KnowledgeHit[] = [];\n for (const source of sources) {\n const adapter = this.adapters.get(source.adapter);\n if (!adapter) {\n this.options.logger?.warn?.(\n `[knowledge] source '${source.id}' references unknown adapter '${source.adapter}'; skipping.`,\n );\n continue;\n }\n try {\n const hits = await adapter.search(query, {\n source,\n topK,\n filter: opts.filter,\n });\n for (const hit of hits) rawHits.push(hit);\n } catch (err) {\n this.options.logger?.error?.(\n `[knowledge] adapter '${source.adapter}' search failed for source '${source.id}': ${(err as Error).message}`,\n );\n }\n }\n\n const filtered = await this.applyPermissionFilter(rawHits, opts.executionContext);\n filtered.sort((a, b) => b.score - a.score);\n return filtered.slice(0, topK);\n }\n\n // ── Sync entrypoints (called by the host plugin's event bridge) ───\n\n /**\n * Apply an ObjectQL `record.created` / `record.updated` event to\n * every `object` source bound to the matching object. Failures are\n * logged but never thrown — sync must not block writes.\n */\n async handleRecordUpsert(object: string, record: Record<string, unknown>): Promise<void> {\n const targets = this.sourcesForObject(object);\n for (const source of targets) {\n try {\n const objSource = source.source as ObjectKnowledgeSource;\n const doc = recordToDocument(source, objSource, record);\n const adapter = this.getAdapter(source.adapter);\n await adapter.upsert([doc], { source, reason: 'event-sync' });\n } catch (err) {\n this.options.logger?.warn?.(\n `[knowledge] event-sync upsert failed for source '${source.id}': ${(err as Error).message}`,\n );\n }\n }\n }\n\n /** Apply an ObjectQL `record.deleted` event. */\n async handleRecordDelete(object: string, recordId: string): Promise<void> {\n const targets = this.sourcesForObject(object);\n for (const source of targets) {\n try {\n const docId = documentIdFor(source.id, recordId);\n const adapter = this.getAdapter(source.adapter);\n await adapter.delete([docId], { source, reason: 'event-sync' });\n } catch (err) {\n this.options.logger?.warn?.(\n `[knowledge] event-sync delete failed for source '${source.id}': ${(err as Error).message}`,\n );\n }\n }\n }\n\n // ── Internals ─────────────────────────────────────────────────────\n\n private requireSource(sourceId: string): KnowledgeSource {\n const source = this.sources.get(sourceId);\n if (!source) {\n throw new Error(\n `[knowledge] unknown source '${sourceId}'. Registered: [${[...this.sources.keys()].join(', ')}]`,\n );\n }\n return source;\n }\n\n private resolveSearchTargets(sourceIds?: string[]): KnowledgeSource[] {\n if (!sourceIds || sourceIds.length === 0) {\n return [...this.sources.values()].filter((s) => s.aiExposed !== false);\n }\n const out: KnowledgeSource[] = [];\n for (const id of sourceIds) {\n const s = this.sources.get(id);\n if (s) out.push(s);\n else this.options.logger?.warn?.(`[knowledge] search target '${id}' not registered; skipping.`);\n }\n return out;\n }\n\n private sourcesForObject(object: string): KnowledgeSource[] {\n const out: KnowledgeSource[] = [];\n for (const source of this.sources.values()) {\n if (\n source.source.kind === 'object' &&\n (source.source as ObjectKnowledgeSource).object === object &&\n (source.refresh?.onRecordChange ?? true) !== false\n ) {\n out.push(source);\n }\n }\n return out;\n }\n\n /**\n * Drop hits whose underlying ObjectQL record the caller can't read.\n * For hits without a `sourceRecordId` (file/http sources) we keep\n * them — adapter is responsible for any ACL enforcement there.\n *\n * When the caller's context is `isSystem: true` or no context is\n * supplied, every hit passes through — preserves today's behaviour\n * for cron jobs / tests.\n */\n private async applyPermissionFilter(\n hits: KnowledgeHit[],\n ctx: ExecutionContext | undefined,\n ): Promise<KnowledgeHit[]> {\n if (!ctx || ctx.isSystem) return hits;\n if (!this.options.dataEngine) {\n this.options.logger?.warn?.(\n '[knowledge] no IDataEngine bound — dropping object-source hits to stay safe.',\n );\n return hits.filter((h) => !h.sourceRecordId);\n }\n\n // Group hits by object so we make one query per object.\n const byObject = new Map<string, KnowledgeHit[]>();\n for (const hit of hits) {\n if (!hit.sourceRecordId) continue;\n const source = this.sources.get(hit.sourceId);\n if (!source || source.source.kind !== 'object') continue;\n const objName = (source.source as ObjectKnowledgeSource).object;\n const bucket = byObject.get(objName) ?? [];\n bucket.push(hit);\n byObject.set(objName, bucket);\n }\n\n const allowed = new Set<string>(); // `${object}#${recordId}`\n for (const [object, group] of byObject) {\n const ids = [...new Set(group.map((h) => h.sourceRecordId!).filter(Boolean))];\n if (ids.length === 0) continue;\n try {\n const rows = (await this.options.dataEngine.find(object, {\n where: { id: { $in: ids } } as Record<string, unknown>,\n fields: ['id'],\n context: ctx,\n } as never)) as Array<{ id?: string }>;\n for (const row of rows) if (row?.id) allowed.add(`${object}#${row.id}`);\n } catch (err) {\n this.options.logger?.warn?.(\n `[knowledge] RLS lookup failed for object='${object}': ${(err as Error).message}; ` +\n 'dropping that object\\'s hits to stay safe.',\n );\n }\n }\n\n return hits.filter((hit) => {\n if (!hit.sourceRecordId) return true; // file / http hit\n const source = this.sources.get(hit.sourceId);\n if (!source || source.source.kind !== 'object') return true;\n const objName = (source.source as ObjectKnowledgeSource).object;\n return allowed.has(`${objName}#${hit.sourceRecordId}`);\n });\n }\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────\n\n/** Deterministic document id derived from the source + record id. */\nexport function documentIdFor(sourceId: string, recordId: string): string {\n return `${sourceId}:${recordId}`;\n}\n\n/**\n * Project an ObjectQL record into a `KnowledgeDocument` per the\n * source's `contentFields` / `metadataFields` config. Pure function.\n */\nexport function recordToDocument(\n source: KnowledgeSource,\n objSource: ObjectKnowledgeSource,\n record: Record<string, unknown>,\n): KnowledgeDocument {\n const recordId = String(record.id ?? (record as any)._id ?? '');\n const contentParts: string[] = [];\n for (const field of objSource.contentFields) {\n if (field === '*') {\n for (const [k, v] of Object.entries(record)) {\n if (typeof v === 'string' && v.length > 0 && k !== 'id') contentParts.push(v);\n }\n } else {\n const v = record[field];\n if (v != null) contentParts.push(String(v));\n }\n }\n const metadata: Record<string, unknown> = {};\n for (const field of objSource.metadataFields ?? []) {\n if (record[field] !== undefined) metadata[field] = record[field];\n }\n return {\n id: documentIdFor(source.id, recordId || `unknown-${Date.now()}`),\n sourceId: source.id,\n sourceRecordId: recordId || undefined,\n content: contentParts.join('\\n\\n'),\n title: typeof record.title === 'string' ? record.title : undefined,\n metadata,\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type {\n IDataEngine,\n IRealtimeService,\n} from '@objectstack/spec/contracts';\nimport type { KnowledgeSource } from '@objectstack/spec/ai';\nimport { KNOWLEDGE_SERVICE } from '@objectstack/spec/contracts';\nimport { KnowledgeService } from './knowledge-service.js';\nimport type { KnowledgeLogger } from './knowledge-service.js';\n\n/**\n * Configuration options for the `KnowledgeServicePlugin`.\n */\nexport interface KnowledgeServicePluginOptions {\n /**\n * Knowledge sources to register at boot. Sources may also be\n * registered programmatically later via `service.registerSource`.\n */\n sources?: KnowledgeSource[];\n /**\n * Subscribe to ObjectQL `record.*` events from `IRealtimeService`\n * for `object` sources. Defaults to `true`. Set to `false` to\n * disable inline event sync (e.g. when an external indexer drives\n * upserts).\n * @default true\n */\n enableEventSync?: boolean;\n /** Default top-K when callers omit it. @default 10 */\n defaultTopK?: number;\n}\n\n/**\n * `KnowledgeServicePlugin` — registers `IKnowledgeService` with the\n * kernel, binds it to `IDataEngine` for RLS-aware permission filtering,\n * and (optionally) subscribes to `IRealtimeService` so ObjectQL record\n * mutations automatically propagate to adapter backends.\n *\n * @example\n * ```ts\n * import { ObjectKernel } from '@objectstack/core';\n * import { KnowledgeServicePlugin } from '@objectstack/service-knowledge';\n *\n * const kernel = new ObjectKernel();\n * kernel.use(new KnowledgeServicePlugin({\n * sources: [{\n * id: 'task_notes', label: 'Task notes', adapter: 'memory',\n * source: { kind: 'object', object: 'task', contentFields: ['notes'] },\n * }],\n * }));\n * await kernel.bootstrap();\n *\n * const knowledge = kernel.getService('knowledge');\n * const hits = await knowledge.search('shopping list', { executionContext });\n * ```\n */\nexport class KnowledgeServicePlugin implements Plugin {\n name = 'com.objectstack.service.knowledge';\n version = '0.1.0';\n type = 'standard';\n\n private service: KnowledgeService | null = null;\n private subscriptionId: string | undefined;\n\n constructor(private readonly options: KnowledgeServicePluginOptions = {}) {}\n\n async init(ctx: PluginContext): Promise<void> {\n let engine: IDataEngine | undefined;\n try {\n engine = ctx.getService<IDataEngine>('objectql');\n } catch {\n // Data engine not wired — service still works in pure-search mode\n // but RLS re-checks will be conservative (drop object-source hits\n // when caller is non-system).\n }\n\n const logger: KnowledgeLogger = {\n info: (msg, ...rest) => {\n (ctx.logger as { info?: (m: string, ...r: unknown[]) => void }).info?.(msg, ...rest);\n },\n warn: (msg, ...rest) => {\n (ctx.logger as { warn?: (m: string, ...r: unknown[]) => void }).warn?.(msg, ...rest);\n },\n error: (msg, ...rest) => {\n (ctx.logger as { error?: (m: string, ...r: unknown[]) => void }).error?.(msg, ...rest);\n },\n debug: (msg, ...rest) => {\n (ctx.logger as { debug?: (m: string, ...r: unknown[]) => void }).debug?.(msg, ...rest);\n },\n };\n\n this.service = new KnowledgeService({\n dataEngine: engine,\n logger,\n defaultTopK: this.options.defaultTopK,\n });\n\n for (const source of this.options.sources ?? []) {\n this.service.registerSource(source);\n }\n\n ctx.registerService(KNOWLEDGE_SERVICE, this.service);\n ctx.logger.info?.(\n `KnowledgeServicePlugin: registered '${KNOWLEDGE_SERVICE}' service (eventSync=${\n this.options.enableEventSync !== false\n }, dataEngine=${engine ? 'yes' : 'no'})`,\n );\n }\n\n async start(ctx: PluginContext): Promise<void> {\n if (this.options.enableEventSync === false) return;\n const service = this.service;\n if (!service) return;\n\n ctx.hook('kernel:ready', async () => {\n let realtime: IRealtimeService | null = null;\n try {\n realtime = ctx.getService<IRealtimeService>('realtime');\n } catch {\n // realtime service not available — sync becomes opt-in via\n // explicit handleRecordUpsert / handleRecordDelete calls.\n ctx.logger.warn?.(\n 'KnowledgeServicePlugin: IRealtimeService unavailable — event sync disabled. ' +\n 'Adapters can still be driven manually via the service API.',\n );\n return;\n }\n\n this.subscriptionId = await realtime.subscribe('knowledge-event-sync', async (event) => {\n const object = event.object;\n if (!object) return;\n const type = event.type;\n const payload = (event.payload ?? {}) as Record<string, unknown>;\n if (\n type === 'record.created' ||\n type === 'record.updated' ||\n type === 'data.record.created' ||\n type === 'data.record.updated'\n ) {\n const record =\n (payload.record as Record<string, unknown> | undefined) ?? payload;\n if (record && typeof record === 'object') {\n await service.handleRecordUpsert(object, record as Record<string, unknown>);\n }\n return;\n }\n if (type === 'record.deleted' || type === 'data.record.deleted') {\n const recordObj = payload.record as Record<string, unknown> | undefined;\n const id =\n (payload.id as string | undefined) ?? (recordObj?.id as string | undefined);\n if (id) await service.handleRecordDelete(object, id);\n }\n });\n ctx.logger.info?.('KnowledgeServicePlugin: event sync subscription active.');\n });\n }\n\n async stop(ctx: PluginContext): Promise<void> {\n if (!this.subscriptionId) return;\n try {\n const realtime = ctx.getService<IRealtimeService>('realtime');\n await realtime.unsubscribe(this.subscriptionId);\n } catch {\n // best-effort\n }\n this.subscriptionId = undefined;\n }\n}\n"],"mappings":";AAwDO,IAAM,mBAAN,MAAoD;AAAA,EAKzD,YAA6B,UAAmC,CAAC,GAAG;AAAvC;AAJ7B,SAAiB,WAAW,oBAAI,IAA+B;AAC/D,SAAiB,UAAU,oBAAI,IAA6B;AAI1D,SAAK,cAAc,QAAQ,eAAe;AAAA,EAC5C;AAAA;AAAA,EAIA,gBAAgB,IAAY,SAAkC;AAC5D,SAAK,SAAS,IAAI,IAAI,OAAO;AAC7B,SAAK,QAAQ,QAAQ,OAAO,mCAAmC,EAAE,EAAE;AAAA,EACrE;AAAA,EAEA,WAAW,IAA+B;AACxC,UAAM,UAAU,KAAK,SAAS,IAAI,EAAE;AACpC,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI;AAAA,QACR,gCAAgC,EAAE,mBAAmB,CAAC,GAAG,KAAK,SAAS,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,MAC3F;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,eAAyB;AACvB,WAAO,CAAC,GAAG,KAAK,SAAS,KAAK,CAAC;AAAA,EACjC;AAAA;AAAA,EAIA,eAAe,QAA+B;AAC5C,QAAI,KAAK,QAAQ,IAAI,OAAO,EAAE,GAAG;AAC/B,WAAK,QAAQ,QAAQ,OAAO,mCAAmC,OAAO,EAAE,EAAE;AAAA,IAC5E;AACA,SAAK,QAAQ,IAAI,OAAO,IAAI,MAAM;AAClC,SAAK,QAAQ,QAAQ;AAAA,MACnB,kCAAkC,OAAO,EAAE,aAAa,OAAO,OAAO,UAAU,OAAO,OAAO,IAAI;AAAA,IACpG;AAAA,EACF;AAAA,EAEA,iBAAiB,UAAwB;AACvC,SAAK,QAAQ,OAAO,QAAQ;AAAA,EAC9B;AAAA,EAEA,cAAiC;AAC/B,WAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC;AAAA,EAClC;AAAA,EAEA,UAAU,UAA+C;AACvD,WAAO,KAAK,QAAQ,IAAI,QAAQ;AAAA,EAClC;AAAA;AAAA,EAIA,MAAM,cAAc,UAAkB,KAAuC;AAC3E,UAAM,SAAS,KAAK,cAAc,QAAQ;AAC1C,UAAM,UAAU,KAAK,WAAW,OAAO,OAAO;AAC9C,UAAM,QAAQ,OAAO,CAAC,GAAG,GAAG,EAAE,QAAQ,QAAQ,SAAS,CAAC;AAAA,EAC1D;AAAA,EAEA,MAAM,eAAe,UAAkB,YAAmC;AACxE,UAAM,SAAS,KAAK,cAAc,QAAQ;AAC1C,UAAM,UAAU,KAAK,WAAW,OAAO,OAAO;AAC9C,UAAM,QAAQ,OAAO,CAAC,UAAU,GAAG,EAAE,QAAQ,QAAQ,SAAS,CAAC;AAAA,EACjE;AAAA;AAAA,EAIA,MAAM,cACJ,UACA,OAAgC,CAAC,GACA;AACjC,UAAM,SAAS,KAAK,cAAc,QAAQ;AAC1C,UAAM,UAAU,KAAK,WAAW,OAAO,OAAO;AAE9C,QAAI,OAAO,OAAO,SAAS,UAAU;AAEnC,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,YAAY;AAAA,QACZ,SAAS;AAAA,QACT,SACE,oBAAoB,OAAO,OAAO,IAAI,+EACI,OAAO,OAAO;AAAA,MAC5D;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,QAAQ,YAAY;AAC5B,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,YAAY;AAAA,QACZ,SAAS;AAAA,QACT,SAAS;AAAA,MACX;AAAA,IACF;AAEA,UAAM,YAAY,OAAO;AAEzB,UAAM,WAA6B,EAAE,OAAO,CAAC,GAAG,aAAa,CAAC,GAAG,UAAU,KAAK;AAChF,UAAM,UAAW,MAAM,KAAK,QAAQ,WAAW,KAAK,UAAU,QAAQ;AAAA,MACpE,OAAO,UAAU;AAAA,MACjB,OAAO,KAAK;AAAA,MACZ,SAAS;AAAA,IACX,CAAU;AAEV,QAAI,KAAK,QAAQ;AACf,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ,UAAU;AAAA,QAClB,YAAY,QAAQ;AAAA,QACpB,SAAS;AAAA,QACT,SAAS;AAAA,MACX;AAAA,IACF;AAEA,UAAM,OAAO,QAAQ,IAAI,CAAC,QAAQ,iBAAiB,QAAQ,WAAW,GAAG,CAAC;AAC1E,QAAI,KAAK,SAAS,GAAG;AACnB,YAAM,QAAQ,OAAO,MAAM,EAAE,QAAQ,QAAQ,UAAU,CAAC;AAAA,IAC1D;AACA,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ,UAAU;AAAA,MAClB,YAAY,QAAQ;AAAA,MACpB,SAAS,KAAK;AAAA,IAChB;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,OAAO,OAAe,OAA+B,CAAC,GAA4B;AACtF,UAAM,OAAO,KAAK,QAAQ,KAAK;AAC/B,UAAM,UAAU,KAAK,qBAAqB,KAAK,SAAS;AACxD,QAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAElC,UAAM,UAA0B,CAAC;AACjC,eAAW,UAAU,SAAS;AAC5B,YAAM,UAAU,KAAK,SAAS,IAAI,OAAO,OAAO;AAChD,UAAI,CAAC,SAAS;AACZ,aAAK,QAAQ,QAAQ;AAAA,UACnB,uBAAuB,OAAO,EAAE,iCAAiC,OAAO,OAAO;AAAA,QACjF;AACA;AAAA,MACF;AACA,UAAI;AACF,cAAM,OAAO,MAAM,QAAQ,OAAO,OAAO;AAAA,UACvC;AAAA,UACA;AAAA,UACA,QAAQ,KAAK;AAAA,QACf,CAAC;AACD,mBAAW,OAAO,KAAM,SAAQ,KAAK,GAAG;AAAA,MAC1C,SAAS,KAAK;AACZ,aAAK,QAAQ,QAAQ;AAAA,UACnB,wBAAwB,OAAO,OAAO,+BAA+B,OAAO,EAAE,MAAO,IAAc,OAAO;AAAA,QAC5G;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,KAAK,sBAAsB,SAAS,KAAK,gBAAgB;AAChF,aAAS,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AACzC,WAAO,SAAS,MAAM,GAAG,IAAI;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,mBAAmB,QAAgB,QAAgD;AACvF,UAAM,UAAU,KAAK,iBAAiB,MAAM;AAC5C,eAAW,UAAU,SAAS;AAC5B,UAAI;AACF,cAAM,YAAY,OAAO;AACzB,cAAM,MAAM,iBAAiB,QAAQ,WAAW,MAAM;AACtD,cAAM,UAAU,KAAK,WAAW,OAAO,OAAO;AAC9C,cAAM,QAAQ,OAAO,CAAC,GAAG,GAAG,EAAE,QAAQ,QAAQ,aAAa,CAAC;AAAA,MAC9D,SAAS,KAAK;AACZ,aAAK,QAAQ,QAAQ;AAAA,UACnB,oDAAoD,OAAO,EAAE,MAAO,IAAc,OAAO;AAAA,QAC3F;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,mBAAmB,QAAgB,UAAiC;AACxE,UAAM,UAAU,KAAK,iBAAiB,MAAM;AAC5C,eAAW,UAAU,SAAS;AAC5B,UAAI;AACF,cAAM,QAAQ,cAAc,OAAO,IAAI,QAAQ;AAC/C,cAAM,UAAU,KAAK,WAAW,OAAO,OAAO;AAC9C,cAAM,QAAQ,OAAO,CAAC,KAAK,GAAG,EAAE,QAAQ,QAAQ,aAAa,CAAC;AAAA,MAChE,SAAS,KAAK;AACZ,aAAK,QAAQ,QAAQ;AAAA,UACnB,oDAAoD,OAAO,EAAE,MAAO,IAAc,OAAO;AAAA,QAC3F;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAIQ,cAAc,UAAmC;AACvD,UAAM,SAAS,KAAK,QAAQ,IAAI,QAAQ;AACxC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR,+BAA+B,QAAQ,mBAAmB,CAAC,GAAG,KAAK,QAAQ,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,MAC/F;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,qBAAqB,WAAyC;AACpE,QAAI,CAAC,aAAa,UAAU,WAAW,GAAG;AACxC,aAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,cAAc,KAAK;AAAA,IACvE;AACA,UAAM,MAAyB,CAAC;AAChC,eAAW,MAAM,WAAW;AAC1B,YAAM,IAAI,KAAK,QAAQ,IAAI,EAAE;AAC7B,UAAI,EAAG,KAAI,KAAK,CAAC;AAAA,UACZ,MAAK,QAAQ,QAAQ,OAAO,8BAA8B,EAAE,6BAA6B;AAAA,IAChG;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,iBAAiB,QAAmC;AAC1D,UAAM,MAAyB,CAAC;AAChC,eAAW,UAAU,KAAK,QAAQ,OAAO,GAAG;AAC1C,UACE,OAAO,OAAO,SAAS,YACtB,OAAO,OAAiC,WAAW,WACnD,OAAO,SAAS,kBAAkB,UAAU,OAC7C;AACA,YAAI,KAAK,MAAM;AAAA,MACjB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAc,sBACZ,MACA,KACyB;AACzB,QAAI,CAAC,OAAO,IAAI,SAAU,QAAO;AACjC,QAAI,CAAC,KAAK,QAAQ,YAAY;AAC5B,WAAK,QAAQ,QAAQ;AAAA,QACnB;AAAA,MACF;AACA,aAAO,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,cAAc;AAAA,IAC7C;AAGA,UAAM,WAAW,oBAAI,IAA4B;AACjD,eAAW,OAAO,MAAM;AACtB,UAAI,CAAC,IAAI,eAAgB;AACzB,YAAM,SAAS,KAAK,QAAQ,IAAI,IAAI,QAAQ;AAC5C,UAAI,CAAC,UAAU,OAAO,OAAO,SAAS,SAAU;AAChD,YAAM,UAAW,OAAO,OAAiC;AACzD,YAAM,SAAS,SAAS,IAAI,OAAO,KAAK,CAAC;AACzC,aAAO,KAAK,GAAG;AACf,eAAS,IAAI,SAAS,MAAM;AAAA,IAC9B;AAEA,UAAM,UAAU,oBAAI,IAAY;AAChC,eAAW,CAAC,QAAQ,KAAK,KAAK,UAAU;AACtC,YAAM,MAAM,CAAC,GAAG,IAAI,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,cAAe,EAAE,OAAO,OAAO,CAAC,CAAC;AAC5E,UAAI,IAAI,WAAW,EAAG;AACtB,UAAI;AACF,cAAM,OAAQ,MAAM,KAAK,QAAQ,WAAW,KAAK,QAAQ;AAAA,UACvD,OAAO,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE;AAAA,UAC1B,QAAQ,CAAC,IAAI;AAAA,UACb,SAAS;AAAA,QACX,CAAU;AACV,mBAAW,OAAO,KAAM,KAAI,KAAK,GAAI,SAAQ,IAAI,GAAG,MAAM,IAAI,IAAI,EAAE,EAAE;AAAA,MACxE,SAAS,KAAK;AACZ,aAAK,QAAQ,QAAQ;AAAA,UACnB,6CAA6C,MAAM,MAAO,IAAc,OAAO;AAAA,QAEjF;AAAA,MACF;AAAA,IACF;AAEA,WAAO,KAAK,OAAO,CAAC,QAAQ;AAC1B,UAAI,CAAC,IAAI,eAAgB,QAAO;AAChC,YAAM,SAAS,KAAK,QAAQ,IAAI,IAAI,QAAQ;AAC5C,UAAI,CAAC,UAAU,OAAO,OAAO,SAAS,SAAU,QAAO;AACvD,YAAM,UAAW,OAAO,OAAiC;AACzD,aAAO,QAAQ,IAAI,GAAG,OAAO,IAAI,IAAI,cAAc,EAAE;AAAA,IACvD,CAAC;AAAA,EACH;AACF;AAKO,SAAS,cAAc,UAAkB,UAA0B;AACxE,SAAO,GAAG,QAAQ,IAAI,QAAQ;AAChC;AAMO,SAAS,iBACd,QACA,WACA,QACmB;AACnB,QAAM,WAAW,OAAO,OAAO,MAAO,OAAe,OAAO,EAAE;AAC9D,QAAM,eAAyB,CAAC;AAChC,aAAW,SAAS,UAAU,eAAe;AAC3C,QAAI,UAAU,KAAK;AACjB,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC3C,YAAI,OAAO,MAAM,YAAY,EAAE,SAAS,KAAK,MAAM,KAAM,cAAa,KAAK,CAAC;AAAA,MAC9E;AAAA,IACF,OAAO;AACL,YAAM,IAAI,OAAO,KAAK;AACtB,UAAI,KAAK,KAAM,cAAa,KAAK,OAAO,CAAC,CAAC;AAAA,IAC5C;AAAA,EACF;AACA,QAAM,WAAoC,CAAC;AAC3C,aAAW,SAAS,UAAU,kBAAkB,CAAC,GAAG;AAClD,QAAI,OAAO,KAAK,MAAM,OAAW,UAAS,KAAK,IAAI,OAAO,KAAK;AAAA,EACjE;AACA,SAAO;AAAA,IACL,IAAI,cAAc,OAAO,IAAI,YAAY,WAAW,KAAK,IAAI,CAAC,EAAE;AAAA,IAChE,UAAU,OAAO;AAAA,IACjB,gBAAgB,YAAY;AAAA,IAC5B,SAAS,aAAa,KAAK,MAAM;AAAA,IACjC,OAAO,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAAA,IACzD;AAAA,EACF;AACF;;;ACxYA,SAAS,yBAAyB;AAiD3B,IAAM,yBAAN,MAA+C;AAAA,EAQpD,YAA6B,UAAyC,CAAC,GAAG;AAA7C;AAP7B,gBAAO;AACP,mBAAU;AACV,gBAAO;AAEP,SAAQ,UAAmC;AAAA,EAGgC;AAAA,EAE3E,MAAM,KAAK,KAAmC;AAC5C,QAAI;AACJ,QAAI;AACF,eAAS,IAAI,WAAwB,UAAU;AAAA,IACjD,QAAQ;AAAA,IAIR;AAEA,UAAM,SAA0B;AAAA,MAC9B,MAAM,CAAC,QAAQ,SAAS;AACtB,QAAC,IAAI,OAA2D,OAAO,KAAK,GAAG,IAAI;AAAA,MACrF;AAAA,MACA,MAAM,CAAC,QAAQ,SAAS;AACtB,QAAC,IAAI,OAA2D,OAAO,KAAK,GAAG,IAAI;AAAA,MACrF;AAAA,MACA,OAAO,CAAC,QAAQ,SAAS;AACvB,QAAC,IAAI,OAA4D,QAAQ,KAAK,GAAG,IAAI;AAAA,MACvF;AAAA,MACA,OAAO,CAAC,QAAQ,SAAS;AACvB,QAAC,IAAI,OAA4D,QAAQ,KAAK,GAAG,IAAI;AAAA,MACvF;AAAA,IACF;AAEA,SAAK,UAAU,IAAI,iBAAiB;AAAA,MAClC,YAAY;AAAA,MACZ;AAAA,MACA,aAAa,KAAK,QAAQ;AAAA,IAC5B,CAAC;AAED,eAAW,UAAU,KAAK,QAAQ,WAAW,CAAC,GAAG;AAC/C,WAAK,QAAQ,eAAe,MAAM;AAAA,IACpC;AAEA,QAAI,gBAAgB,mBAAmB,KAAK,OAAO;AACnD,QAAI,OAAO;AAAA,MACT,uCAAuC,iBAAiB,wBACtD,KAAK,QAAQ,oBAAoB,KACnC,gBAAgB,SAAS,QAAQ,IAAI;AAAA,IACvC;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,KAAmC;AAC7C,QAAI,KAAK,QAAQ,oBAAoB,MAAO;AAC5C,UAAM,UAAU,KAAK;AACrB,QAAI,CAAC,QAAS;AAEd,QAAI,KAAK,gBAAgB,YAAY;AACnC,UAAI,WAAoC;AACxC,UAAI;AACF,mBAAW,IAAI,WAA6B,UAAU;AAAA,MACxD,QAAQ;AAGN,YAAI,OAAO;AAAA,UACT;AAAA,QAEF;AACA;AAAA,MACF;AAEA,WAAK,iBAAiB,MAAM,SAAS,UAAU,wBAAwB,OAAO,UAAU;AACtF,cAAM,SAAS,MAAM;AACrB,YAAI,CAAC,OAAQ;AACb,cAAM,OAAO,MAAM;AACnB,cAAM,UAAW,MAAM,WAAW,CAAC;AACnC,YACE,SAAS,oBACT,SAAS,oBACT,SAAS,yBACT,SAAS,uBACT;AACA,gBAAM,SACH,QAAQ,UAAkD;AAC7D,cAAI,UAAU,OAAO,WAAW,UAAU;AACxC,kBAAM,QAAQ,mBAAmB,QAAQ,MAAiC;AAAA,UAC5E;AACA;AAAA,QACF;AACA,YAAI,SAAS,oBAAoB,SAAS,uBAAuB;AAC/D,gBAAM,YAAY,QAAQ;AAC1B,gBAAM,KACH,QAAQ,MAA8B,WAAW;AACpD,cAAI,GAAI,OAAM,QAAQ,mBAAmB,QAAQ,EAAE;AAAA,QACrD;AAAA,MACF,CAAC;AACD,UAAI,OAAO,OAAO,yDAAyD;AAAA,IAC7E,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,KAAK,KAAmC;AAC5C,QAAI,CAAC,KAAK,eAAgB;AAC1B,QAAI;AACF,YAAM,WAAW,IAAI,WAA6B,UAAU;AAC5D,YAAM,SAAS,YAAY,KAAK,cAAc;AAAA,IAChD,QAAQ;AAAA,IAER;AACA,SAAK,iBAAiB;AAAA,EACxB;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/knowledge-service.ts","../src/knowledge-service-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type {\n IDataEngine,\n IKnowledgeAdapter,\n IKnowledgeService,\n KnowledgeReindexOptions,\n KnowledgeReindexResult,\n KnowledgeSearchOptions,\n} from '@objectstack/spec/contracts';\nimport type { ExecutionContext } from '@objectstack/spec/kernel';\nimport type {\n KnowledgeDocument,\n KnowledgeHit,\n KnowledgeSource,\n ObjectKnowledgeSource,\n} from '@objectstack/spec/ai';\n\n/**\n * Minimal logger shape; falls back to no-op when none is provided.\n */\nexport interface KnowledgeLogger {\n info?(msg: string, ...rest: unknown[]): void;\n warn?(msg: string, ...rest: unknown[]): void;\n error?(msg: string, ...rest: unknown[]): void;\n debug?(msg: string, ...rest: unknown[]): void;\n}\n\n/**\n * Constructor options for `KnowledgeService`.\n */\nexport interface KnowledgeServiceOptions {\n /** Data engine used for RLS re-checks and bulk reindex walks. Optional in tests. */\n dataEngine?: IDataEngine;\n /** Optional structured logger. */\n logger?: KnowledgeLogger;\n /**\n * Default top-K when callers don't specify. The adapter may cap\n * further; the service does not enforce an upper bound itself.\n * @default 10\n */\n defaultTopK?: number;\n}\n\n/**\n * `KnowledgeService` — production `IKnowledgeService` implementation.\n *\n * Responsibilities (the parts the framework owns):\n * - Routes search / index calls to the right `IKnowledgeAdapter`.\n * - Re-checks every hit's `sourceRecordId` against the caller's\n * `ExecutionContext` so row-level security is preserved end-to-end.\n * - Walks ObjectQL when reindexing an `object` source.\n *\n * Non-responsibilities (the parts plugins own):\n * - Chunking, embedding, vector storage, hybrid retrieval, rerank.\n */\nexport class KnowledgeService implements IKnowledgeService {\n private readonly adapters = new Map<string, IKnowledgeAdapter>();\n private readonly sources = new Map<string, KnowledgeSource>();\n private readonly defaultTopK: number;\n\n constructor(private readonly options: KnowledgeServiceOptions = {}) {\n this.defaultTopK = options.defaultTopK ?? 10;\n }\n\n // ── Adapter registry ──────────────────────────────────────────────\n\n registerAdapter(id: string, adapter: IKnowledgeAdapter): void {\n this.adapters.set(id, adapter);\n this.options.logger?.info?.(`[knowledge] adapter registered: ${id}`);\n }\n\n getAdapter(id: string): IKnowledgeAdapter {\n const adapter = this.adapters.get(id);\n if (!adapter) {\n throw new Error(\n `[knowledge] unknown adapter '${id}'. Registered: [${[...this.adapters.keys()].join(', ')}]`,\n );\n }\n return adapter;\n }\n\n listAdapters(): string[] {\n return [...this.adapters.keys()];\n }\n\n // ── Source registry ───────────────────────────────────────────────\n\n registerSource(source: KnowledgeSource): void {\n if (this.sources.has(source.id)) {\n this.options.logger?.warn?.(`[knowledge] source overwritten: ${source.id}`);\n }\n this.sources.set(source.id, source);\n this.options.logger?.info?.(\n `[knowledge] source registered: ${source.id} (adapter=${source.adapter}, kind=${source.source.kind})`,\n );\n }\n\n unregisterSource(sourceId: string): void {\n this.sources.delete(sourceId);\n }\n\n listSources(): KnowledgeSource[] {\n return [...this.sources.values()];\n }\n\n getSource(sourceId: string): KnowledgeSource | undefined {\n return this.sources.get(sourceId);\n }\n\n // ── Document mutations ────────────────────────────────────────────\n\n async indexDocument(sourceId: string, doc: KnowledgeDocument): Promise<void> {\n const source = this.requireSource(sourceId);\n const adapter = this.getAdapter(source.adapter);\n await adapter.upsert([doc], { source, reason: 'manual' });\n }\n\n async deleteDocument(sourceId: string, documentId: string): Promise<void> {\n const source = this.requireSource(sourceId);\n const adapter = this.getAdapter(source.adapter);\n await adapter.delete([documentId], { source, reason: 'manual' });\n }\n\n // ── Reindex ───────────────────────────────────────────────────────\n\n async reindexSource(\n sourceId: string,\n opts: KnowledgeReindexOptions = {},\n ): Promise<KnowledgeReindexResult> {\n const source = this.requireSource(sourceId);\n const adapter = this.getAdapter(source.adapter);\n\n if (source.source.kind !== 'object') {\n // File / HTTP sources delegate to adapter-internal ingestion.\n return {\n ok: false,\n discovered: 0,\n indexed: 0,\n message:\n `Reindex for kind=${source.source.kind} is not handled by KnowledgeService. ` +\n `Trigger ingestion through the adapter (${source.adapter}) directly.`,\n };\n }\n\n if (!this.options.dataEngine) {\n return {\n ok: false,\n discovered: 0,\n indexed: 0,\n message: 'KnowledgeService has no IDataEngine bound; cannot walk object source.',\n };\n }\n\n const objSource = source.source as ObjectKnowledgeSource;\n // RLS-bypassing system context — this is a server-side admin op.\n const adminCtx: ExecutionContext = { roles: [], permissions: [], systemPermissions: [], isSystem: true };\n const records = (await this.options.dataEngine.find(objSource.object, {\n where: objSource.where,\n limit: opts.limit,\n context: adminCtx,\n } as never)) as Array<Record<string, unknown>>;\n\n if (opts.dryRun) {\n return {\n ok: true,\n object: objSource.object,\n discovered: records.length,\n indexed: 0,\n message: 'dryRun=true; no documents pushed.',\n };\n }\n\n const docs = records.map((rec) => recordToDocument(source, objSource, rec));\n if (docs.length > 0) {\n await adapter.upsert(docs, { source, reason: 'reindex' });\n }\n return {\n ok: true,\n object: objSource.object,\n discovered: records.length,\n indexed: docs.length,\n };\n }\n\n // ── Permission-aware search ───────────────────────────────────────\n\n async search(query: string, opts: KnowledgeSearchOptions = {}): Promise<KnowledgeHit[]> {\n const topK = opts.topK ?? this.defaultTopK;\n const sources = this.resolveSearchTargets(opts.sourceIds);\n if (sources.length === 0) return [];\n\n const rawHits: KnowledgeHit[] = [];\n for (const source of sources) {\n const adapter = this.adapters.get(source.adapter);\n if (!adapter) {\n this.options.logger?.warn?.(\n `[knowledge] source '${source.id}' references unknown adapter '${source.adapter}'; skipping.`,\n );\n continue;\n }\n try {\n const hits = await adapter.search(query, {\n source,\n topK,\n filter: opts.filter,\n });\n for (const hit of hits) rawHits.push(hit);\n } catch (err) {\n this.options.logger?.error?.(\n `[knowledge] adapter '${source.adapter}' search failed for source '${source.id}': ${(err as Error).message}`,\n );\n }\n }\n\n const filtered = await this.applyPermissionFilter(rawHits, opts.executionContext);\n filtered.sort((a, b) => b.score - a.score);\n return filtered.slice(0, topK);\n }\n\n // ── Sync entrypoints (called by the host plugin's event bridge) ───\n\n /**\n * Apply an ObjectQL `record.created` / `record.updated` event to\n * every `object` source bound to the matching object. Failures are\n * logged but never thrown — sync must not block writes.\n */\n async handleRecordUpsert(object: string, record: Record<string, unknown>): Promise<void> {\n const targets = this.sourcesForObject(object);\n for (const source of targets) {\n try {\n const objSource = source.source as ObjectKnowledgeSource;\n const doc = recordToDocument(source, objSource, record);\n const adapter = this.getAdapter(source.adapter);\n await adapter.upsert([doc], { source, reason: 'event-sync' });\n } catch (err) {\n this.options.logger?.warn?.(\n `[knowledge] event-sync upsert failed for source '${source.id}': ${(err as Error).message}`,\n );\n }\n }\n }\n\n /** Apply an ObjectQL `record.deleted` event. */\n async handleRecordDelete(object: string, recordId: string): Promise<void> {\n const targets = this.sourcesForObject(object);\n for (const source of targets) {\n try {\n const docId = documentIdFor(source.id, recordId);\n const adapter = this.getAdapter(source.adapter);\n await adapter.delete([docId], { source, reason: 'event-sync' });\n } catch (err) {\n this.options.logger?.warn?.(\n `[knowledge] event-sync delete failed for source '${source.id}': ${(err as Error).message}`,\n );\n }\n }\n }\n\n // ── Internals ─────────────────────────────────────────────────────\n\n private requireSource(sourceId: string): KnowledgeSource {\n const source = this.sources.get(sourceId);\n if (!source) {\n throw new Error(\n `[knowledge] unknown source '${sourceId}'. Registered: [${[...this.sources.keys()].join(', ')}]`,\n );\n }\n return source;\n }\n\n private resolveSearchTargets(sourceIds?: string[]): KnowledgeSource[] {\n if (!sourceIds || sourceIds.length === 0) {\n return [...this.sources.values()].filter((s) => s.aiExposed !== false);\n }\n const out: KnowledgeSource[] = [];\n for (const id of sourceIds) {\n const s = this.sources.get(id);\n if (s) out.push(s);\n else this.options.logger?.warn?.(`[knowledge] search target '${id}' not registered; skipping.`);\n }\n return out;\n }\n\n private sourcesForObject(object: string): KnowledgeSource[] {\n const out: KnowledgeSource[] = [];\n for (const source of this.sources.values()) {\n if (\n source.source.kind === 'object' &&\n (source.source as ObjectKnowledgeSource).object === object &&\n (source.refresh?.onRecordChange ?? true) !== false\n ) {\n out.push(source);\n }\n }\n return out;\n }\n\n /**\n * Drop hits whose underlying ObjectQL record the caller can't read.\n * For hits without a `sourceRecordId` (file/http sources) we keep\n * them — adapter is responsible for any ACL enforcement there.\n *\n * When the caller's context is `isSystem: true` or no context is\n * supplied, every hit passes through — preserves today's behaviour\n * for cron jobs / tests.\n */\n private async applyPermissionFilter(\n hits: KnowledgeHit[],\n ctx: ExecutionContext | undefined,\n ): Promise<KnowledgeHit[]> {\n if (!ctx || ctx.isSystem) return hits;\n if (!this.options.dataEngine) {\n this.options.logger?.warn?.(\n '[knowledge] no IDataEngine bound — dropping object-source hits to stay safe.',\n );\n return hits.filter((h) => !h.sourceRecordId);\n }\n\n // Group hits by object so we make one query per object.\n const byObject = new Map<string, KnowledgeHit[]>();\n for (const hit of hits) {\n if (!hit.sourceRecordId) continue;\n const source = this.sources.get(hit.sourceId);\n if (!source || source.source.kind !== 'object') continue;\n const objName = (source.source as ObjectKnowledgeSource).object;\n const bucket = byObject.get(objName) ?? [];\n bucket.push(hit);\n byObject.set(objName, bucket);\n }\n\n const allowed = new Set<string>(); // `${object}#${recordId}`\n for (const [object, group] of byObject) {\n const ids = [...new Set(group.map((h) => h.sourceRecordId!).filter(Boolean))];\n if (ids.length === 0) continue;\n try {\n const rows = (await this.options.dataEngine.find(object, {\n where: { id: { $in: ids } } as Record<string, unknown>,\n fields: ['id'],\n context: ctx,\n } as never)) as Array<{ id?: string }>;\n for (const row of rows) if (row?.id) allowed.add(`${object}#${row.id}`);\n } catch (err) {\n this.options.logger?.warn?.(\n `[knowledge] RLS lookup failed for object='${object}': ${(err as Error).message}; ` +\n 'dropping that object\\'s hits to stay safe.',\n );\n }\n }\n\n return hits.filter((hit) => {\n if (!hit.sourceRecordId) return true; // file / http hit\n const source = this.sources.get(hit.sourceId);\n if (!source || source.source.kind !== 'object') return true;\n const objName = (source.source as ObjectKnowledgeSource).object;\n return allowed.has(`${objName}#${hit.sourceRecordId}`);\n });\n }\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────\n\n/** Deterministic document id derived from the source + record id. */\nexport function documentIdFor(sourceId: string, recordId: string): string {\n return `${sourceId}:${recordId}`;\n}\n\n/**\n * Project an ObjectQL record into a `KnowledgeDocument` per the\n * source's `contentFields` / `metadataFields` config. Pure function.\n */\nexport function recordToDocument(\n source: KnowledgeSource,\n objSource: ObjectKnowledgeSource,\n record: Record<string, unknown>,\n): KnowledgeDocument {\n const recordId = String(record.id ?? (record as any)._id ?? '');\n const contentParts: string[] = [];\n for (const field of objSource.contentFields) {\n if (field === '*') {\n for (const [k, v] of Object.entries(record)) {\n if (typeof v === 'string' && v.length > 0 && k !== 'id') contentParts.push(v);\n }\n } else {\n const v = record[field];\n if (v != null) contentParts.push(String(v));\n }\n }\n const metadata: Record<string, unknown> = {};\n for (const field of objSource.metadataFields ?? []) {\n if (record[field] !== undefined) metadata[field] = record[field];\n }\n return {\n id: documentIdFor(source.id, recordId || `unknown-${Date.now()}`),\n sourceId: source.id,\n sourceRecordId: recordId || undefined,\n content: contentParts.join('\\n\\n'),\n title: typeof record.title === 'string' ? record.title : undefined,\n metadata,\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type {\n IDataEngine,\n IRealtimeService,\n} from '@objectstack/spec/contracts';\nimport type { KnowledgeSource } from '@objectstack/spec/ai';\nimport { KNOWLEDGE_SERVICE } from '@objectstack/spec/contracts';\nimport { KnowledgeService } from './knowledge-service.js';\nimport type { KnowledgeLogger } from './knowledge-service.js';\n\n/**\n * Configuration options for the `KnowledgeServicePlugin`.\n */\nexport interface KnowledgeServicePluginOptions {\n /**\n * Knowledge sources to register at boot. Sources may also be\n * registered programmatically later via `service.registerSource`.\n */\n sources?: KnowledgeSource[];\n /**\n * Subscribe to ObjectQL `record.*` events from `IRealtimeService`\n * for `object` sources. Defaults to `true`. Set to `false` to\n * disable inline event sync (e.g. when an external indexer drives\n * upserts).\n * @default true\n */\n enableEventSync?: boolean;\n /** Default top-K when callers omit it. @default 10 */\n defaultTopK?: number;\n}\n\n/**\n * `KnowledgeServicePlugin` — registers `IKnowledgeService` with the\n * kernel, binds it to `IDataEngine` for RLS-aware permission filtering,\n * and (optionally) subscribes to `IRealtimeService` so ObjectQL record\n * mutations automatically propagate to adapter backends.\n *\n * @example\n * ```ts\n * import { ObjectKernel } from '@objectstack/core';\n * import { KnowledgeServicePlugin } from '@objectstack/service-knowledge';\n *\n * const kernel = new ObjectKernel();\n * kernel.use(new KnowledgeServicePlugin({\n * sources: [{\n * id: 'task_notes', label: 'Task notes', adapter: 'memory',\n * source: { kind: 'object', object: 'task', contentFields: ['notes'] },\n * }],\n * }));\n * await kernel.bootstrap();\n *\n * const knowledge = kernel.getService('knowledge');\n * const hits = await knowledge.search('shopping list', { executionContext });\n * ```\n */\nexport class KnowledgeServicePlugin implements Plugin {\n name = 'com.objectstack.service.knowledge';\n version = '0.1.0';\n type = 'standard';\n\n private service: KnowledgeService | null = null;\n private subscriptionId: string | undefined;\n\n constructor(private readonly options: KnowledgeServicePluginOptions = {}) {}\n\n async init(ctx: PluginContext): Promise<void> {\n let engine: IDataEngine | undefined;\n try {\n engine = ctx.getService<IDataEngine>('objectql');\n } catch {\n // Data engine not wired — service still works in pure-search mode\n // but RLS re-checks will be conservative (drop object-source hits\n // when caller is non-system).\n }\n\n const logger: KnowledgeLogger = {\n info: (msg, ...rest) => {\n (ctx.logger as { info?: (m: string, ...r: unknown[]) => void }).info?.(msg, ...rest);\n },\n warn: (msg, ...rest) => {\n (ctx.logger as { warn?: (m: string, ...r: unknown[]) => void }).warn?.(msg, ...rest);\n },\n error: (msg, ...rest) => {\n (ctx.logger as { error?: (m: string, ...r: unknown[]) => void }).error?.(msg, ...rest);\n },\n debug: (msg, ...rest) => {\n (ctx.logger as { debug?: (m: string, ...r: unknown[]) => void }).debug?.(msg, ...rest);\n },\n };\n\n this.service = new KnowledgeService({\n dataEngine: engine,\n logger,\n defaultTopK: this.options.defaultTopK,\n });\n\n for (const source of this.options.sources ?? []) {\n this.service.registerSource(source);\n }\n\n ctx.registerService(KNOWLEDGE_SERVICE, this.service);\n ctx.logger.info?.(\n `KnowledgeServicePlugin: registered '${KNOWLEDGE_SERVICE}' service (eventSync=${\n this.options.enableEventSync !== false\n }, dataEngine=${engine ? 'yes' : 'no'})`,\n );\n }\n\n async start(ctx: PluginContext): Promise<void> {\n if (this.options.enableEventSync === false) return;\n const service = this.service;\n if (!service) return;\n\n ctx.hook('kernel:ready', async () => {\n let realtime: IRealtimeService | null = null;\n try {\n realtime = ctx.getService<IRealtimeService>('realtime');\n } catch {\n // realtime service not available — sync becomes opt-in via\n // explicit handleRecordUpsert / handleRecordDelete calls.\n ctx.logger.warn?.(\n 'KnowledgeServicePlugin: IRealtimeService unavailable — event sync disabled. ' +\n 'Adapters can still be driven manually via the service API.',\n );\n return;\n }\n\n this.subscriptionId = await realtime.subscribe('knowledge-event-sync', async (event) => {\n const object = event.object;\n if (!object) return;\n const type = event.type;\n const payload = (event.payload ?? {}) as Record<string, unknown>;\n if (\n type === 'record.created' ||\n type === 'record.updated' ||\n type === 'data.record.created' ||\n type === 'data.record.updated'\n ) {\n const record =\n (payload.record as Record<string, unknown> | undefined) ?? payload;\n if (record && typeof record === 'object') {\n await service.handleRecordUpsert(object, record as Record<string, unknown>);\n }\n return;\n }\n if (type === 'record.deleted' || type === 'data.record.deleted') {\n const recordObj = payload.record as Record<string, unknown> | undefined;\n const id =\n (payload.id as string | undefined) ?? (recordObj?.id as string | undefined);\n if (id) await service.handleRecordDelete(object, id);\n }\n });\n ctx.logger.info?.('KnowledgeServicePlugin: event sync subscription active.');\n });\n }\n\n async stop(ctx: PluginContext): Promise<void> {\n if (!this.subscriptionId) return;\n try {\n const realtime = ctx.getService<IRealtimeService>('realtime');\n await realtime.unsubscribe(this.subscriptionId);\n } catch {\n // best-effort\n }\n this.subscriptionId = undefined;\n }\n}\n"],"mappings":";AAwDO,IAAM,mBAAN,MAAoD;AAAA,EAKzD,YAA6B,UAAmC,CAAC,GAAG;AAAvC;AAJ7B,SAAiB,WAAW,oBAAI,IAA+B;AAC/D,SAAiB,UAAU,oBAAI,IAA6B;AAI1D,SAAK,cAAc,QAAQ,eAAe;AAAA,EAC5C;AAAA;AAAA,EAIA,gBAAgB,IAAY,SAAkC;AAC5D,SAAK,SAAS,IAAI,IAAI,OAAO;AAC7B,SAAK,QAAQ,QAAQ,OAAO,mCAAmC,EAAE,EAAE;AAAA,EACrE;AAAA,EAEA,WAAW,IAA+B;AACxC,UAAM,UAAU,KAAK,SAAS,IAAI,EAAE;AACpC,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI;AAAA,QACR,gCAAgC,EAAE,mBAAmB,CAAC,GAAG,KAAK,SAAS,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,MAC3F;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,eAAyB;AACvB,WAAO,CAAC,GAAG,KAAK,SAAS,KAAK,CAAC;AAAA,EACjC;AAAA;AAAA,EAIA,eAAe,QAA+B;AAC5C,QAAI,KAAK,QAAQ,IAAI,OAAO,EAAE,GAAG;AAC/B,WAAK,QAAQ,QAAQ,OAAO,mCAAmC,OAAO,EAAE,EAAE;AAAA,IAC5E;AACA,SAAK,QAAQ,IAAI,OAAO,IAAI,MAAM;AAClC,SAAK,QAAQ,QAAQ;AAAA,MACnB,kCAAkC,OAAO,EAAE,aAAa,OAAO,OAAO,UAAU,OAAO,OAAO,IAAI;AAAA,IACpG;AAAA,EACF;AAAA,EAEA,iBAAiB,UAAwB;AACvC,SAAK,QAAQ,OAAO,QAAQ;AAAA,EAC9B;AAAA,EAEA,cAAiC;AAC/B,WAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC;AAAA,EAClC;AAAA,EAEA,UAAU,UAA+C;AACvD,WAAO,KAAK,QAAQ,IAAI,QAAQ;AAAA,EAClC;AAAA;AAAA,EAIA,MAAM,cAAc,UAAkB,KAAuC;AAC3E,UAAM,SAAS,KAAK,cAAc,QAAQ;AAC1C,UAAM,UAAU,KAAK,WAAW,OAAO,OAAO;AAC9C,UAAM,QAAQ,OAAO,CAAC,GAAG,GAAG,EAAE,QAAQ,QAAQ,SAAS,CAAC;AAAA,EAC1D;AAAA,EAEA,MAAM,eAAe,UAAkB,YAAmC;AACxE,UAAM,SAAS,KAAK,cAAc,QAAQ;AAC1C,UAAM,UAAU,KAAK,WAAW,OAAO,OAAO;AAC9C,UAAM,QAAQ,OAAO,CAAC,UAAU,GAAG,EAAE,QAAQ,QAAQ,SAAS,CAAC;AAAA,EACjE;AAAA;AAAA,EAIA,MAAM,cACJ,UACA,OAAgC,CAAC,GACA;AACjC,UAAM,SAAS,KAAK,cAAc,QAAQ;AAC1C,UAAM,UAAU,KAAK,WAAW,OAAO,OAAO;AAE9C,QAAI,OAAO,OAAO,SAAS,UAAU;AAEnC,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,YAAY;AAAA,QACZ,SAAS;AAAA,QACT,SACE,oBAAoB,OAAO,OAAO,IAAI,+EACI,OAAO,OAAO;AAAA,MAC5D;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,QAAQ,YAAY;AAC5B,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,YAAY;AAAA,QACZ,SAAS;AAAA,QACT,SAAS;AAAA,MACX;AAAA,IACF;AAEA,UAAM,YAAY,OAAO;AAEzB,UAAM,WAA6B,EAAE,OAAO,CAAC,GAAG,aAAa,CAAC,GAAG,mBAAmB,CAAC,GAAG,UAAU,KAAK;AACvG,UAAM,UAAW,MAAM,KAAK,QAAQ,WAAW,KAAK,UAAU,QAAQ;AAAA,MACpE,OAAO,UAAU;AAAA,MACjB,OAAO,KAAK;AAAA,MACZ,SAAS;AAAA,IACX,CAAU;AAEV,QAAI,KAAK,QAAQ;AACf,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ,UAAU;AAAA,QAClB,YAAY,QAAQ;AAAA,QACpB,SAAS;AAAA,QACT,SAAS;AAAA,MACX;AAAA,IACF;AAEA,UAAM,OAAO,QAAQ,IAAI,CAAC,QAAQ,iBAAiB,QAAQ,WAAW,GAAG,CAAC;AAC1E,QAAI,KAAK,SAAS,GAAG;AACnB,YAAM,QAAQ,OAAO,MAAM,EAAE,QAAQ,QAAQ,UAAU,CAAC;AAAA,IAC1D;AACA,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,QAAQ,UAAU;AAAA,MAClB,YAAY,QAAQ;AAAA,MACpB,SAAS,KAAK;AAAA,IAChB;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,OAAO,OAAe,OAA+B,CAAC,GAA4B;AACtF,UAAM,OAAO,KAAK,QAAQ,KAAK;AAC/B,UAAM,UAAU,KAAK,qBAAqB,KAAK,SAAS;AACxD,QAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAElC,UAAM,UAA0B,CAAC;AACjC,eAAW,UAAU,SAAS;AAC5B,YAAM,UAAU,KAAK,SAAS,IAAI,OAAO,OAAO;AAChD,UAAI,CAAC,SAAS;AACZ,aAAK,QAAQ,QAAQ;AAAA,UACnB,uBAAuB,OAAO,EAAE,iCAAiC,OAAO,OAAO;AAAA,QACjF;AACA;AAAA,MACF;AACA,UAAI;AACF,cAAM,OAAO,MAAM,QAAQ,OAAO,OAAO;AAAA,UACvC;AAAA,UACA;AAAA,UACA,QAAQ,KAAK;AAAA,QACf,CAAC;AACD,mBAAW,OAAO,KAAM,SAAQ,KAAK,GAAG;AAAA,MAC1C,SAAS,KAAK;AACZ,aAAK,QAAQ,QAAQ;AAAA,UACnB,wBAAwB,OAAO,OAAO,+BAA+B,OAAO,EAAE,MAAO,IAAc,OAAO;AAAA,QAC5G;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,KAAK,sBAAsB,SAAS,KAAK,gBAAgB;AAChF,aAAS,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AACzC,WAAO,SAAS,MAAM,GAAG,IAAI;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,mBAAmB,QAAgB,QAAgD;AACvF,UAAM,UAAU,KAAK,iBAAiB,MAAM;AAC5C,eAAW,UAAU,SAAS;AAC5B,UAAI;AACF,cAAM,YAAY,OAAO;AACzB,cAAM,MAAM,iBAAiB,QAAQ,WAAW,MAAM;AACtD,cAAM,UAAU,KAAK,WAAW,OAAO,OAAO;AAC9C,cAAM,QAAQ,OAAO,CAAC,GAAG,GAAG,EAAE,QAAQ,QAAQ,aAAa,CAAC;AAAA,MAC9D,SAAS,KAAK;AACZ,aAAK,QAAQ,QAAQ;AAAA,UACnB,oDAAoD,OAAO,EAAE,MAAO,IAAc,OAAO;AAAA,QAC3F;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,mBAAmB,QAAgB,UAAiC;AACxE,UAAM,UAAU,KAAK,iBAAiB,MAAM;AAC5C,eAAW,UAAU,SAAS;AAC5B,UAAI;AACF,cAAM,QAAQ,cAAc,OAAO,IAAI,QAAQ;AAC/C,cAAM,UAAU,KAAK,WAAW,OAAO,OAAO;AAC9C,cAAM,QAAQ,OAAO,CAAC,KAAK,GAAG,EAAE,QAAQ,QAAQ,aAAa,CAAC;AAAA,MAChE,SAAS,KAAK;AACZ,aAAK,QAAQ,QAAQ;AAAA,UACnB,oDAAoD,OAAO,EAAE,MAAO,IAAc,OAAO;AAAA,QAC3F;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAIQ,cAAc,UAAmC;AACvD,UAAM,SAAS,KAAK,QAAQ,IAAI,QAAQ;AACxC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR,+BAA+B,QAAQ,mBAAmB,CAAC,GAAG,KAAK,QAAQ,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA,MAC/F;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,qBAAqB,WAAyC;AACpE,QAAI,CAAC,aAAa,UAAU,WAAW,GAAG;AACxC,aAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,cAAc,KAAK;AAAA,IACvE;AACA,UAAM,MAAyB,CAAC;AAChC,eAAW,MAAM,WAAW;AAC1B,YAAM,IAAI,KAAK,QAAQ,IAAI,EAAE;AAC7B,UAAI,EAAG,KAAI,KAAK,CAAC;AAAA,UACZ,MAAK,QAAQ,QAAQ,OAAO,8BAA8B,EAAE,6BAA6B;AAAA,IAChG;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,iBAAiB,QAAmC;AAC1D,UAAM,MAAyB,CAAC;AAChC,eAAW,UAAU,KAAK,QAAQ,OAAO,GAAG;AAC1C,UACE,OAAO,OAAO,SAAS,YACtB,OAAO,OAAiC,WAAW,WACnD,OAAO,SAAS,kBAAkB,UAAU,OAC7C;AACA,YAAI,KAAK,MAAM;AAAA,MACjB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAc,sBACZ,MACA,KACyB;AACzB,QAAI,CAAC,OAAO,IAAI,SAAU,QAAO;AACjC,QAAI,CAAC,KAAK,QAAQ,YAAY;AAC5B,WAAK,QAAQ,QAAQ;AAAA,QACnB;AAAA,MACF;AACA,aAAO,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,cAAc;AAAA,IAC7C;AAGA,UAAM,WAAW,oBAAI,IAA4B;AACjD,eAAW,OAAO,MAAM;AACtB,UAAI,CAAC,IAAI,eAAgB;AACzB,YAAM,SAAS,KAAK,QAAQ,IAAI,IAAI,QAAQ;AAC5C,UAAI,CAAC,UAAU,OAAO,OAAO,SAAS,SAAU;AAChD,YAAM,UAAW,OAAO,OAAiC;AACzD,YAAM,SAAS,SAAS,IAAI,OAAO,KAAK,CAAC;AACzC,aAAO,KAAK,GAAG;AACf,eAAS,IAAI,SAAS,MAAM;AAAA,IAC9B;AAEA,UAAM,UAAU,oBAAI,IAAY;AAChC,eAAW,CAAC,QAAQ,KAAK,KAAK,UAAU;AACtC,YAAM,MAAM,CAAC,GAAG,IAAI,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,cAAe,EAAE,OAAO,OAAO,CAAC,CAAC;AAC5E,UAAI,IAAI,WAAW,EAAG;AACtB,UAAI;AACF,cAAM,OAAQ,MAAM,KAAK,QAAQ,WAAW,KAAK,QAAQ;AAAA,UACvD,OAAO,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE;AAAA,UAC1B,QAAQ,CAAC,IAAI;AAAA,UACb,SAAS;AAAA,QACX,CAAU;AACV,mBAAW,OAAO,KAAM,KAAI,KAAK,GAAI,SAAQ,IAAI,GAAG,MAAM,IAAI,IAAI,EAAE,EAAE;AAAA,MACxE,SAAS,KAAK;AACZ,aAAK,QAAQ,QAAQ;AAAA,UACnB,6CAA6C,MAAM,MAAO,IAAc,OAAO;AAAA,QAEjF;AAAA,MACF;AAAA,IACF;AAEA,WAAO,KAAK,OAAO,CAAC,QAAQ;AAC1B,UAAI,CAAC,IAAI,eAAgB,QAAO;AAChC,YAAM,SAAS,KAAK,QAAQ,IAAI,IAAI,QAAQ;AAC5C,UAAI,CAAC,UAAU,OAAO,OAAO,SAAS,SAAU,QAAO;AACvD,YAAM,UAAW,OAAO,OAAiC;AACzD,aAAO,QAAQ,IAAI,GAAG,OAAO,IAAI,IAAI,cAAc,EAAE;AAAA,IACvD,CAAC;AAAA,EACH;AACF;AAKO,SAAS,cAAc,UAAkB,UAA0B;AACxE,SAAO,GAAG,QAAQ,IAAI,QAAQ;AAChC;AAMO,SAAS,iBACd,QACA,WACA,QACmB;AACnB,QAAM,WAAW,OAAO,OAAO,MAAO,OAAe,OAAO,EAAE;AAC9D,QAAM,eAAyB,CAAC;AAChC,aAAW,SAAS,UAAU,eAAe;AAC3C,QAAI,UAAU,KAAK;AACjB,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC3C,YAAI,OAAO,MAAM,YAAY,EAAE,SAAS,KAAK,MAAM,KAAM,cAAa,KAAK,CAAC;AAAA,MAC9E;AAAA,IACF,OAAO;AACL,YAAM,IAAI,OAAO,KAAK;AACtB,UAAI,KAAK,KAAM,cAAa,KAAK,OAAO,CAAC,CAAC;AAAA,IAC5C;AAAA,EACF;AACA,QAAM,WAAoC,CAAC;AAC3C,aAAW,SAAS,UAAU,kBAAkB,CAAC,GAAG;AAClD,QAAI,OAAO,KAAK,MAAM,OAAW,UAAS,KAAK,IAAI,OAAO,KAAK;AAAA,EACjE;AACA,SAAO;AAAA,IACL,IAAI,cAAc,OAAO,IAAI,YAAY,WAAW,KAAK,IAAI,CAAC,EAAE;AAAA,IAChE,UAAU,OAAO;AAAA,IACjB,gBAAgB,YAAY;AAAA,IAC5B,SAAS,aAAa,KAAK,MAAM;AAAA,IACjC,OAAO,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAAA,IACzD;AAAA,EACF;AACF;;;ACxYA,SAAS,yBAAyB;AAiD3B,IAAM,yBAAN,MAA+C;AAAA,EAQpD,YAA6B,UAAyC,CAAC,GAAG;AAA7C;AAP7B,gBAAO;AACP,mBAAU;AACV,gBAAO;AAEP,SAAQ,UAAmC;AAAA,EAGgC;AAAA,EAE3E,MAAM,KAAK,KAAmC;AAC5C,QAAI;AACJ,QAAI;AACF,eAAS,IAAI,WAAwB,UAAU;AAAA,IACjD,QAAQ;AAAA,IAIR;AAEA,UAAM,SAA0B;AAAA,MAC9B,MAAM,CAAC,QAAQ,SAAS;AACtB,QAAC,IAAI,OAA2D,OAAO,KAAK,GAAG,IAAI;AAAA,MACrF;AAAA,MACA,MAAM,CAAC,QAAQ,SAAS;AACtB,QAAC,IAAI,OAA2D,OAAO,KAAK,GAAG,IAAI;AAAA,MACrF;AAAA,MACA,OAAO,CAAC,QAAQ,SAAS;AACvB,QAAC,IAAI,OAA4D,QAAQ,KAAK,GAAG,IAAI;AAAA,MACvF;AAAA,MACA,OAAO,CAAC,QAAQ,SAAS;AACvB,QAAC,IAAI,OAA4D,QAAQ,KAAK,GAAG,IAAI;AAAA,MACvF;AAAA,IACF;AAEA,SAAK,UAAU,IAAI,iBAAiB;AAAA,MAClC,YAAY;AAAA,MACZ;AAAA,MACA,aAAa,KAAK,QAAQ;AAAA,IAC5B,CAAC;AAED,eAAW,UAAU,KAAK,QAAQ,WAAW,CAAC,GAAG;AAC/C,WAAK,QAAQ,eAAe,MAAM;AAAA,IACpC;AAEA,QAAI,gBAAgB,mBAAmB,KAAK,OAAO;AACnD,QAAI,OAAO;AAAA,MACT,uCAAuC,iBAAiB,wBACtD,KAAK,QAAQ,oBAAoB,KACnC,gBAAgB,SAAS,QAAQ,IAAI;AAAA,IACvC;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,KAAmC;AAC7C,QAAI,KAAK,QAAQ,oBAAoB,MAAO;AAC5C,UAAM,UAAU,KAAK;AACrB,QAAI,CAAC,QAAS;AAEd,QAAI,KAAK,gBAAgB,YAAY;AACnC,UAAI,WAAoC;AACxC,UAAI;AACF,mBAAW,IAAI,WAA6B,UAAU;AAAA,MACxD,QAAQ;AAGN,YAAI,OAAO;AAAA,UACT;AAAA,QAEF;AACA;AAAA,MACF;AAEA,WAAK,iBAAiB,MAAM,SAAS,UAAU,wBAAwB,OAAO,UAAU;AACtF,cAAM,SAAS,MAAM;AACrB,YAAI,CAAC,OAAQ;AACb,cAAM,OAAO,MAAM;AACnB,cAAM,UAAW,MAAM,WAAW,CAAC;AACnC,YACE,SAAS,oBACT,SAAS,oBACT,SAAS,yBACT,SAAS,uBACT;AACA,gBAAM,SACH,QAAQ,UAAkD;AAC7D,cAAI,UAAU,OAAO,WAAW,UAAU;AACxC,kBAAM,QAAQ,mBAAmB,QAAQ,MAAiC;AAAA,UAC5E;AACA;AAAA,QACF;AACA,YAAI,SAAS,oBAAoB,SAAS,uBAAuB;AAC/D,gBAAM,YAAY,QAAQ;AAC1B,gBAAM,KACH,QAAQ,MAA8B,WAAW;AACpD,cAAI,GAAI,OAAM,QAAQ,mBAAmB,QAAQ,EAAE;AAAA,QACrD;AAAA,MACF,CAAC;AACD,UAAI,OAAO,OAAO,yDAAyD;AAAA,IAC7E,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,KAAK,KAAmC;AAC5C,QAAI,CAAC,KAAK,eAAgB;AAC1B,QAAI;AACF,YAAM,WAAW,IAAI,WAA6B,UAAU;AAC5D,YAAM,SAAS,YAAY,KAAK,cAAc;AAAA,IAChD,QAAQ;AAAA,IAER;AACA,SAAK,iBAAiB;AAAA,EACxB;AACF;","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@objectstack/service-knowledge",
3
- "version": "6.8.0",
4
- "license": "BUSL-1.1",
3
+ "version": "6.8.1",
4
+ "license": "Apache-2.0",
5
5
  "description": "Knowledge Service for ObjectStack — orchestrator implementing IKnowledgeService over pluggable IKnowledgeAdapter backends (RAGFlow, LlamaIndex, Dify, in-memory).",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
@@ -14,8 +14,8 @@
14
14
  }
15
15
  },
16
16
  "dependencies": {
17
- "@objectstack/core": "6.8.0",
18
- "@objectstack/spec": "6.8.0"
17
+ "@objectstack/core": "6.8.1",
18
+ "@objectstack/spec": "6.8.1"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/node": "^25.9.1",