@objectstack/service-knowledge 6.7.1 → 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/LICENSE +93 -202
- package/README.md +4 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/LICENSE
CHANGED
|
@@ -1,202 +1,93 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
(a) You must give any other recipients of the Work or
|
|
95
|
-
Derivative Works a copy of this License; and
|
|
96
|
-
|
|
97
|
-
(b) You must cause any modified files to carry prominent notices
|
|
98
|
-
stating that You changed the files; and
|
|
99
|
-
|
|
100
|
-
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
-
that You distribute, all copyright, patent, trademark, and
|
|
102
|
-
attribution notices from the Source form of the Work,
|
|
103
|
-
excluding those notices that do not pertain to any part of
|
|
104
|
-
the Derivative Works; and
|
|
105
|
-
|
|
106
|
-
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
-
distribution, then any Derivative Works that You distribute
|
|
108
|
-
must include a readable copy of the attribution notices
|
|
109
|
-
contained within such NOTICE file, excluding those notices
|
|
110
|
-
that do not pertain to any part of the Derivative Works,
|
|
111
|
-
in at least one of the following places: within a NOTICE
|
|
112
|
-
text file distributed as part of the Derivative Works; within
|
|
113
|
-
the Source form or documentation, if provided along with
|
|
114
|
-
the Derivative Works; or, within a display generated by the
|
|
115
|
-
Derivative Works, if and wherever such third-party notices
|
|
116
|
-
normally appear. The contents of the NOTICE file are for
|
|
117
|
-
informational purposes only and do not modify the License.
|
|
118
|
-
You may add Your own attribution notices within Derivative
|
|
119
|
-
Works that You distribute, alongside or as an addendum to
|
|
120
|
-
the NOTICE text from the Work, provided that such additional
|
|
121
|
-
attribution notices cannot be construed as modifying the
|
|
122
|
-
License.
|
|
123
|
-
|
|
124
|
-
You may add Your own copyright statement to Your modifications and
|
|
125
|
-
may provide additional or different license terms and conditions
|
|
126
|
-
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
-
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
-
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
-
the conditions stated in this License.
|
|
130
|
-
|
|
131
|
-
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
-
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
-
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
-
this License, without any additional terms or conditions.
|
|
135
|
-
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
-
the terms of any separate license agreement you may have executed
|
|
137
|
-
with Licensor regarding such Contributions.
|
|
138
|
-
|
|
139
|
-
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
-
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
-
except as required for reasonable and customary use in describing the
|
|
142
|
-
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
-
|
|
144
|
-
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
-
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
-
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
-
implied, including, without limitation, any warranties or conditions
|
|
149
|
-
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
-
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
-
appropriateness of using or redistributing the Work and assume any
|
|
152
|
-
risks associated with Your exercise of permissions under this License.
|
|
153
|
-
|
|
154
|
-
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
-
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
-
unless required by applicable law (such as deliberate and grossly
|
|
157
|
-
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
-
liable to You for damages, including any direct, indirect, special,
|
|
159
|
-
incidental, or consequential damages of any character arising as a
|
|
160
|
-
result of this License or out of the use or inability to use the
|
|
161
|
-
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
-
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
-
other commercial damages or losses), even if such Contributor
|
|
164
|
-
has been advised of the possibility of such damages.
|
|
165
|
-
|
|
166
|
-
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
-
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
-
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
-
or other liability obligations and/or rights consistent with this
|
|
170
|
-
License. However, in accepting such obligations, You may act only
|
|
171
|
-
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
-
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
-
defend, and hold each Contributor harmless for any liability
|
|
174
|
-
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
-
of your accepting any such warranty or additional liability.
|
|
176
|
-
|
|
177
|
-
END OF TERMS AND CONDITIONS
|
|
178
|
-
|
|
179
|
-
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
-
|
|
181
|
-
To apply the Apache License to your work, attach the following
|
|
182
|
-
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
-
replaced with your own identifying information. (Don't include
|
|
184
|
-
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
-
comment syntax for the file format. We also recommend that a
|
|
186
|
-
file or class name and description of purpose be included on the
|
|
187
|
-
same "printed page" as the copyright notice for easier
|
|
188
|
-
identification within third-party archives.
|
|
189
|
-
|
|
190
|
-
Copyright 2026 ObjectStack
|
|
191
|
-
|
|
192
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
-
you may not use this file except in compliance with the License.
|
|
194
|
-
You may obtain a copy of the License at
|
|
195
|
-
|
|
196
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
-
|
|
198
|
-
Unless required by applicable law or agreed to in writing, software
|
|
199
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
-
See the License for the specific language governing permissions and
|
|
202
|
-
limitations under the License.
|
|
1
|
+
License text copyright (c) 2020 MariaDB Corporation Ab, All Rights Reserved.
|
|
2
|
+
"Business Source License" is a trademark of MariaDB Corporation Ab.
|
|
3
|
+
|
|
4
|
+
Parameters
|
|
5
|
+
|
|
6
|
+
Licensor: ObjectStack AI LLC
|
|
7
|
+
Licensed Work: ObjectStack Runtime: the BSL-licensed packages
|
|
8
|
+
of the ObjectStack monorepo as listed in LICENSING.md.
|
|
9
|
+
Copyright (c) 2026 ObjectStack AI LLC.
|
|
10
|
+
Additional Use Grant: You may make production use of the Licensed Work, provided
|
|
11
|
+
Your use does not include offering the Licensed Work to third
|
|
12
|
+
parties on a hosted or embedded basis in order to compete with
|
|
13
|
+
ObjectStack AI LLC's paid version(s) of the Licensed Work. For purposes
|
|
14
|
+
of this license:
|
|
15
|
+
|
|
16
|
+
A "competitive offering" is a Product that is offered to third
|
|
17
|
+
parties on a paid basis, including through paid support
|
|
18
|
+
arrangements, that significantly overlaps with the capabilities
|
|
19
|
+
of ObjectStack AI LLC's paid version(s) of the Licensed Work. If Your
|
|
20
|
+
Product is not a competitive offering when You first make it
|
|
21
|
+
generally available, it will not become a competitive offering
|
|
22
|
+
later due to ObjectStack AI LLC releasing a new version of the Licensed
|
|
23
|
+
Work with additional capabilities. In addition, Products that
|
|
24
|
+
are not provided on a paid basis are not competitive.
|
|
25
|
+
|
|
26
|
+
"Product" means software that is offered to end users to manage
|
|
27
|
+
in their own environments or offered as a service on a hosted
|
|
28
|
+
basis.
|
|
29
|
+
|
|
30
|
+
"Embedded" means including the source code or executable code
|
|
31
|
+
from the Licensed Work in a competitive offering. "Embedded"
|
|
32
|
+
also means packaging the competitive offering in such a way
|
|
33
|
+
that the Licensed Work must be accessed or downloaded for the
|
|
34
|
+
competitive offering to operate.
|
|
35
|
+
|
|
36
|
+
Hosting or using the Licensed Work(s) for internal purposes
|
|
37
|
+
within an organization is not considered a competitive
|
|
38
|
+
offering. ObjectStack AI LLC considers your organization to include all
|
|
39
|
+
of your affiliates under common control.
|
|
40
|
+
|
|
41
|
+
For binding interpretive guidance on using ObjectStack AI LLC products
|
|
42
|
+
under the Business Source License, please visit our FAQ.
|
|
43
|
+
(see LICENSING.md in this repository)
|
|
44
|
+
Change Date: Four years from the date the Licensed Work is published.
|
|
45
|
+
Change License: Apache License, Version 2.0
|
|
46
|
+
|
|
47
|
+
For information about alternative licensing arrangements for the Licensed Work,
|
|
48
|
+
please contact licensing@objectstack.dev.
|
|
49
|
+
|
|
50
|
+
Notice
|
|
51
|
+
|
|
52
|
+
Business Source License 1.1
|
|
53
|
+
|
|
54
|
+
Terms
|
|
55
|
+
|
|
56
|
+
The Licensor hereby grants you the right to copy, modify, create derivative
|
|
57
|
+
works, redistribute, and make non-production use of the Licensed Work. The
|
|
58
|
+
Licensor may make an Additional Use Grant, above, permitting limited production use.
|
|
59
|
+
|
|
60
|
+
Effective on the Change Date, or the fourth anniversary of the first publicly
|
|
61
|
+
available distribution of a specific version of the Licensed Work under this
|
|
62
|
+
License, whichever comes first, the Licensor hereby grants you rights under
|
|
63
|
+
the terms of the Change License, and the rights granted in the paragraph
|
|
64
|
+
above terminate.
|
|
65
|
+
|
|
66
|
+
If your use of the Licensed Work does not comply with the requirements
|
|
67
|
+
currently in effect as described in this License, you must purchase a
|
|
68
|
+
commercial license from the Licensor, its affiliated entities, or authorized
|
|
69
|
+
resellers, or you must refrain from using the Licensed Work.
|
|
70
|
+
|
|
71
|
+
All copies of the original and modified Licensed Work, and derivative works
|
|
72
|
+
of the Licensed Work, are subject to this License. This License applies
|
|
73
|
+
separately for each version of the Licensed Work and the Change Date may vary
|
|
74
|
+
for each version of the Licensed Work released by Licensor.
|
|
75
|
+
|
|
76
|
+
You must conspicuously display this License on each original or modified copy
|
|
77
|
+
of the Licensed Work. If you receive the Licensed Work in original or
|
|
78
|
+
modified form from a third party, the terms and conditions set forth in this
|
|
79
|
+
License apply to your use of that work.
|
|
80
|
+
|
|
81
|
+
Any use of the Licensed Work in violation of this License will automatically
|
|
82
|
+
terminate your rights under this License for the current and all other
|
|
83
|
+
versions of the Licensed Work.
|
|
84
|
+
|
|
85
|
+
This License does not grant you any right in any trademark or logo of
|
|
86
|
+
Licensor or its affiliates (provided that you may use a trademark or logo of
|
|
87
|
+
Licensor as expressly required by this License).
|
|
88
|
+
|
|
89
|
+
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
|
|
90
|
+
AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
|
|
91
|
+
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
|
|
92
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
|
|
93
|
+
TITLE.
|
package/README.md
CHANGED
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,
|
package/dist/index.cjs.map
CHANGED
|
@@ -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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACwDO,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,uBAAkC;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,oCAAmB,KAAK,OAAO;AACnD,QAAI,OAAO;AAAA,MACT,uCAAuC,kCAAiB,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/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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACwDO,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,uBAAkC;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,oCAAmB,KAAK,OAAO;AACnD,QAAI,OAAO;AAAA,MACT,uCAAuC,kCAAiB,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/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":"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","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":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/service-knowledge",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.8.1",
|
|
4
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",
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@objectstack/core": "6.
|
|
18
|
-
"@objectstack/spec": "6.
|
|
17
|
+
"@objectstack/core": "6.8.1",
|
|
18
|
+
"@objectstack/spec": "6.8.1"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/node": "^25.9.1",
|