noteconnection 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +257 -1803
- package/dist/src/agent_workspace.contract.parity.test.js +105 -421
- package/dist/src/agent_workspace.frontend.test.js +410 -11
- package/dist/src/android.knowledgebase.picker.contract.test.js +102 -0
- package/dist/src/appimage.patchelf.test.js +113 -0
- package/dist/src/appimage.portability.test.js +156 -0
- package/dist/src/backend/FileLoader.js +18 -4
- package/dist/src/backend/FileLoader.test.js +125 -0
- package/dist/src/backend/GraphBuilder.identity.test.js +86 -0
- package/dist/src/backend/GraphBuilder.js +66 -18
- package/dist/src/backend/GraphBuilder.matching.test.js +127 -0
- package/dist/src/backend/GraphMetrics.js +0 -3
- package/dist/src/backend/ResourceIdentity.js +155 -0
- package/dist/src/backend/ResourceIdentity.test.js +113 -0
- package/dist/src/backend/algorithms/CycleDetection.js +0 -1
- package/dist/src/backend/algorithms/HybridEngine.js +0 -3
- package/dist/src/backend/algorithms/LayoutEngine.js +0 -1
- package/dist/src/backend/algorithms/StatisticalAnalyzer.js +0 -4
- package/dist/src/backend/algorithms/TopologicalSort.js +0 -2
- package/dist/src/backend/build_dag.js +0 -1
- package/dist/src/capacitor.bridge.serialization.contract.test.js +3 -0
- package/dist/src/capacitor.runtime.contract.test.js +11 -2
- package/dist/src/core/Graph.js +138 -42
- package/dist/src/core/Graph.test.js +74 -0
- package/dist/src/core/NoteConnection.js +2 -2
- package/dist/src/core/PathBridge.js +265 -2
- package/dist/src/core/ResourceReference.js +17 -0
- package/dist/src/desktop.godot.pack.test.js +142 -0
- package/dist/src/desktop.runtime.graph.test.js +66 -0
- package/dist/src/export/WorkspaceExportBundle.js +44 -0
- package/dist/src/export/WorkspaceExportBundle.test.js +61 -0
- package/dist/src/foundation.release.evidence.contract.test.js +127 -9
- package/dist/src/foundation.sqlite.runtime.contract.test.js +6 -0
- package/dist/src/frontend/README.md +257 -1803
- package/dist/src/frontend/agent_workspace.js +665 -7
- package/dist/src/frontend/index.html +31 -2
- package/dist/src/frontend/knowledge_projection_contract.js +230 -0
- package/dist/src/frontend/knowledge_projection_store.js +214 -0
- package/dist/src/frontend/libs/path_core.js +158 -42
- package/dist/src/frontend/locales/en.json +32 -0
- package/dist/src/frontend/locales/zh.json +32 -0
- package/dist/src/frontend/mobile_budget_runtime.js +33 -0
- package/dist/src/frontend/mobile_exact_analyzer.js +393 -0
- package/dist/src/frontend/mobile_identity_contract.js +221 -0
- package/dist/src/frontend/mobile_semantic_comparator.js +264 -0
- package/dist/src/frontend/path.html +6 -2
- package/dist/src/frontend/simulationWorker.js +1 -1
- package/dist/src/frontend/source_manager.js +110 -4
- package/dist/src/frontend/storage_provider.js +661 -71
- package/dist/src/frontend/styles.css +107 -13
- package/dist/src/frontend/workspace_panes.js +111 -7
- package/dist/src/frontend.runtime.assets.contract.test.js +81 -0
- package/dist/src/graph.answer.operator.contract.test.js +72 -0
- package/dist/src/index.js +6 -2
- package/dist/src/index.knowledge-root.test.js +118 -0
- package/dist/src/indexing/IndexLifecycle.js +22 -0
- package/dist/src/indexing/IndexLifecycle.test.js +24 -0
- package/dist/src/knowledge.api.contract.test.js +4 -2
- package/dist/src/knowledge_projection.contract.test.js +134 -0
- package/dist/src/knowledge_projection_store.test.js +200 -0
- package/dist/src/learning/AnswerQualityEvaluation.js +153 -0
- package/dist/src/learning/AnswerQualityEvaluation.test.js +118 -0
- package/dist/src/learning/KnowledgeLearningPlatform.js +4687 -3892
- package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +418 -0
- package/dist/src/learning/KnowledgeLearningPlatform.test.js +243 -6
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +25 -1
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +13 -5
- package/dist/src/learning/MeasurementEvidenceConflicts.test.js +78 -0
- package/dist/src/learning/PublicAnswerContract.test.js +85 -0
- package/dist/src/learning/PublicEvidenceSelection.test.js +94 -0
- package/dist/src/learning/PublicMeasurementDisclosure.test.js +54 -0
- package/dist/src/learning/agentConversationExecution.js +211 -0
- package/dist/src/learning/agentConversationExecution.test.js +209 -0
- package/dist/src/learning/agentConversationSerialization.js +266 -0
- package/dist/src/learning/agentConversationSerialization.test.js +165 -0
- package/dist/src/learning/agentResponseBudget.js +179 -0
- package/dist/src/learning/agentResponseBudget.test.js +97 -0
- package/dist/src/learning/agentWorkspaceCapabilityContracts.js +65 -0
- package/dist/src/learning/answerReleaseReview.js +1191 -250
- package/dist/src/learning/answerReleaseReview.test.js +1293 -7
- package/dist/src/learning/answerTaskPlan.js +473 -0
- package/dist/src/learning/conversationComposer.js +627 -386
- package/dist/src/learning/conversationComposer.test.js +1403 -0
- package/dist/src/learning/evidenceContextAssembler.js +312 -120
- package/dist/src/learning/graphAnswerCoverage.js +37 -0
- package/dist/src/learning/graphAnswerCoverage.test.js +93 -0
- package/dist/src/learning/graphAnswerCoverageCalibration.js +59 -0
- package/dist/src/learning/graphAnswerCoverageCalibration.test.js +46 -0
- package/dist/src/learning/graphAnswerFacts.js +94 -0
- package/dist/src/learning/graphAnswerFacts.test.js +39 -0
- package/dist/src/learning/graphAnswerPlan.js +908 -0
- package/dist/src/learning/graphAnswerPlan.test.js +907 -0
- package/dist/src/learning/graphAnswerQualityCalibration.js +255 -0
- package/dist/src/learning/graphAnswerQualityCalibration.test.js +72 -0
- package/dist/src/learning/graphAnswerQualityPolicy.js +30 -0
- package/dist/src/learning/graphClaimMatcher.js +104 -0
- package/dist/src/learning/graphConditionedContext.js +109 -0
- package/dist/src/learning/graphConditionedContext.test.js +99 -0
- package/dist/src/learning/graphContextAssembler.js +3 -13
- package/dist/src/learning/graphContextAssembler.test.js +2 -2
- package/dist/src/learning/graphExpansionPolicy.js +22 -0
- package/dist/src/learning/graphExpansionPolicy.test.js +33 -0
- package/dist/src/learning/identityMetadata.test.js +26 -0
- package/dist/src/learning/index.js +1 -0
- package/dist/src/learning/mobileAnswerProjection.js +104 -0
- package/dist/src/learning/mobileAnswerProjection.test.js +102 -0
- package/dist/src/learning/queryBackend.js +34 -10
- package/dist/src/learning/queryBackend.test.js +21 -0
- package/dist/src/learning/ragContextPack.js +115 -16
- package/dist/src/learning/ragContextPack.test.js +151 -0
- package/dist/src/learning/ragEvidenceQuality.js +96 -0
- package/dist/src/learning/ragEvidenceQuality.test.js +29 -0
- package/dist/src/learning/ragPublicText.js +67 -4
- package/dist/src/learning/ragPublicText.test.js +57 -0
- package/dist/src/learning/ragSufficiencyJudge.js +11 -1
- package/dist/src/learning/ragSufficiencyProviderJudge.js +16 -26
- package/dist/src/learning/ragSufficiencyProviderJudge.test.js +51 -0
- package/dist/src/learning/requestNormalization.js +106 -0
- package/dist/src/learning/requestNormalization.test.js +101 -0
- package/dist/src/learning/store.js +116 -12
- package/dist/src/learning/store.test.js +299 -0
- package/dist/src/learning/vectorAccelerationAdapter.js +22 -6
- package/dist/src/learning/vectorAccelerationAdapter.test.js +30 -0
- package/dist/src/learning/workspaceHydration.js +168 -0
- package/dist/src/learning/workspaceHydration.test.js +30 -0
- package/dist/src/license.policy.contract.test.js +9 -4
- package/dist/src/markdown/MarkdownGateway.js +4 -0
- package/dist/src/markdown/MarkdownGateway.test.js +59 -0
- package/dist/src/middleware/SseResponseWriter.js +122 -0
- package/dist/src/middleware/SseResponseWriter.test.js +54 -0
- package/dist/src/middleware/auth.js +28 -7
- package/dist/src/middleware/auth.test.js +33 -0
- package/dist/src/middleware/index.js +2 -1
- package/dist/src/mobile.android.evidence.contract.test.js +146 -0
- package/dist/src/mobile.android.signing.contract.test.js +66 -0
- package/dist/src/mobile.artifact.contract.test.js +144 -0
- package/dist/src/mobile.budget.contract.test.js +90 -0
- package/dist/src/mobile.canonical-id.readiness.test.js +54 -0
- package/dist/src/mobile.graph.parity.contract.test.js +207 -0
- package/dist/src/mobile.identity.contract.test.js +66 -0
- package/dist/src/mobile.identity.corpus.manifest.test.js +72 -0
- package/dist/src/mobile.identity.corpus.test.js +40 -0
- package/dist/src/mobile.native.recovery.contract.test.js +64 -0
- package/dist/src/mobile.pipeline.test.js +11 -1
- package/dist/src/mobile.profile.contract.test.js +225 -0
- package/dist/src/mobile.semantic.parity.test.js +92 -0
- package/dist/src/mobile_exact_analyzer.test.js +113 -0
- package/dist/src/notemd/ContentGenerator.js +4 -9
- package/dist/src/notemd/FileProcessor.js +4 -9
- package/dist/src/notemd/NotemdService.js +12 -17
- package/dist/src/notemd/Translator.js +4 -9
- package/dist/src/notemd/progressReporter.js +9 -0
- package/dist/src/notemd.server.integration.test.js +119 -4
- package/dist/src/notemd.workflow.pipeline.test.js +1 -1
- package/dist/src/pathbridge.handshake.contract.test.js +29 -0
- package/dist/src/pathbridge.host-adapter.contract.test.js +133 -0
- package/dist/src/pathcore.bundle.contract.test.js +8 -0
- package/dist/src/platform/ExportProfile.js +28 -0
- package/dist/src/platform/MobileBudget.js +11 -0
- package/dist/src/platform/PlatformCapabilities.js +14 -0
- package/dist/src/platform/PlatformCapabilities.test.js +6 -0
- package/dist/src/query_backend.external_http.integration.test.js +2 -2
- package/dist/src/release.godot.mirror.contract.test.js +28 -4
- package/dist/src/resources/ResourceRegistry.js +40 -0
- package/dist/src/resources/ResourceRegistry.test.js +36 -0
- package/dist/src/routes/index.js +0 -4
- package/dist/src/routes/knowledge.js +46 -33
- package/dist/src/routes/knowledgePayload.js +185 -0
- package/dist/src/routes/knowledgePayload.test.js +53 -0
- package/dist/src/routes/markdown.js +8 -0
- package/dist/src/routes/notemd.js +990 -234
- package/dist/src/routes/registry.contract.test.js +5 -2
- package/dist/src/routes/registry.shadow.contract.test.js +22 -0
- package/dist/src/routes/render.js +14 -51
- package/dist/src/runtime.capabilities.test.js +18 -2
- package/dist/src/server.js +566 -1090
- package/dist/src/server.migration.test.js +417 -15
- package/dist/src/sidecar.build.lock.contract.test.js +201 -0
- package/dist/src/sidecar.freshness.contract.test.js +184 -0
- package/dist/src/sidecar.replacement.boundary.contract.test.js +6 -3
- package/dist/src/sidecar.supply.readiness.contract.test.js +11 -0
- package/dist/src/simulation.worker.offline.test.js +104 -0
- package/dist/src/storage.provider.capacitor.content.contract.test.js +4 -1
- package/dist/src/storage.provider.contract.test.js +152 -1
- package/dist/src/tauri.frontend.build.contract.test.js +8 -0
- package/dist/src/tauri.window.evidence.execution.test.js +68 -0
- package/dist/src/utils/RuntimePaths.js +6 -0
- package/dist/src/utils/RuntimePaths.test.js +27 -0
- package/dist/src/windows.installer.build.test.js +109 -0
- package/dist/src/windows.installer.payload.test.js +76 -0
- package/dist/src/windows.installer.runtime.test.js +69 -0
- package/dist/src/workspace/WorkspaceRegistry.js +13 -0
- package/package.json +32 -10
- package/dist/src/routes/data.js +0 -267
- package/dist/src/routes/diagnostics.js +0 -51
|
@@ -58,4 +58,40 @@ describe('ResourceRegistry', () => {
|
|
|
58
58
|
expect(restored.getProjectionByDocumentId('doc_delete')?.status).toBe('deleted');
|
|
59
59
|
expect(restored.listProjectionsByIds([created.projection.projectionId], { includeDeleted: true })).toHaveLength(1);
|
|
60
60
|
});
|
|
61
|
+
test('mirrors a document move without changing resource or projection identity', () => {
|
|
62
|
+
let idCounter = 0;
|
|
63
|
+
const registry = new ResourceRegistry_1.ResourceRegistry((prefix = 'resource') => `${prefix}_${++idCounter}`);
|
|
64
|
+
const created = registry.upsertKnowledgeDocument({
|
|
65
|
+
documentId: 'doc_move',
|
|
66
|
+
sourcePath: 'Knowledge_Base/old.md',
|
|
67
|
+
content: '# Move',
|
|
68
|
+
sourceHash: 'hash_move',
|
|
69
|
+
title: 'old',
|
|
70
|
+
language: 'en',
|
|
71
|
+
version: 1,
|
|
72
|
+
updatedAt: '2026-05-26T04:00:00.000Z',
|
|
73
|
+
metadata: { sourceUri: 'note://workspace/v1/old.md' },
|
|
74
|
+
});
|
|
75
|
+
expect(registry.updateKnowledgeDocumentIdentity({
|
|
76
|
+
documentId: 'doc_move',
|
|
77
|
+
sourcePath: 'Knowledge_Base/new.md',
|
|
78
|
+
sourceUri: 'note://workspace/v1/new.md',
|
|
79
|
+
revision: 'sha256:new',
|
|
80
|
+
identityAliases: ['old.md', 'Knowledge_Base/new.md'],
|
|
81
|
+
updatedAt: '2026-05-26T05:00:00.000Z',
|
|
82
|
+
})).toBe(true);
|
|
83
|
+
expect(registry.getProjectionByDocumentId('doc_move')).toEqual(expect.objectContaining({
|
|
84
|
+
projectionId: created.projection.projectionId,
|
|
85
|
+
sourcePath: 'Knowledge_Base/new.md',
|
|
86
|
+
metadata: expect.objectContaining({
|
|
87
|
+
sourceUri: 'note://workspace/v1/new.md',
|
|
88
|
+
revision: 'sha256:new',
|
|
89
|
+
identityAliases: ['old.md', 'Knowledge_Base/new.md'],
|
|
90
|
+
}),
|
|
91
|
+
}));
|
|
92
|
+
expect(registry.getResourceById(created.resource.resourceId)).toEqual(expect.objectContaining({
|
|
93
|
+
resourceId: created.resource.resourceId,
|
|
94
|
+
sourcePath: 'Knowledge_Base/new.md',
|
|
95
|
+
}));
|
|
96
|
+
});
|
|
61
97
|
});
|
package/dist/src/routes/index.js
CHANGED
|
@@ -6,8 +6,6 @@ const notemd_1 = require("./notemd");
|
|
|
6
6
|
const markdown_1 = require("./markdown");
|
|
7
7
|
const render_1 = require("./render");
|
|
8
8
|
const settings_1 = require("./settings");
|
|
9
|
-
const diagnostics_1 = require("./diagnostics");
|
|
10
|
-
const data_1 = require("./data");
|
|
11
9
|
const agentWorkspaceDiagnostics_1 = require("./agentWorkspaceDiagnostics");
|
|
12
10
|
function registerAllRoutes(ctx) {
|
|
13
11
|
return [
|
|
@@ -16,8 +14,6 @@ function registerAllRoutes(ctx) {
|
|
|
16
14
|
...(0, markdown_1.registerMarkdownRoutes)(ctx),
|
|
17
15
|
...(0, render_1.registerRenderRoutes)(ctx),
|
|
18
16
|
...(0, settings_1.registerSettingsRoutes)(ctx),
|
|
19
|
-
...(0, diagnostics_1.registerDiagnosticsRoutes)(ctx),
|
|
20
|
-
...(0, data_1.registerDataRoutes)(ctx),
|
|
21
17
|
...(0, agentWorkspaceDiagnostics_1.registerAgentWorkspaceDiagnosticsRoutes)(ctx),
|
|
22
18
|
];
|
|
23
19
|
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.registerKnowledgeRoutes = registerKnowledgeRoutes;
|
|
4
4
|
const CrashLogger_1 = require("../backend/utils/CrashLogger");
|
|
5
5
|
const requestNormalization_1 = require("../learning/requestNormalization");
|
|
6
|
+
const knowledgePayload_1 = require("./knowledgePayload");
|
|
6
7
|
function registerKnowledgeRoutes(ctx) {
|
|
7
8
|
const { knowledgeLearningPlatform, knowledgeIngestor, knowledgeQuerier, LOOPBACK_HOST, finalPort, KNOWLEDGE_GRAPH_STORE_BACKEND, KNOWLEDGE_GRAPHDB_ADAPTER_PROVIDER, KNOWLEDGE_GRAPHDB_ADAPTER_ID, KNOWLEDGE_GRAPHDB_FALLBACK_ENABLED, KNOWLEDGE_GRAPHDB_OPERATION_MODE, runtimeRunbookOps, scheduleKnowledgeLearningPlatformWarmup, } = ctx;
|
|
8
9
|
const api = (path) => `/api/knowledge${path}`;
|
|
@@ -14,11 +15,44 @@ function registerKnowledgeRoutes(ctx) {
|
|
|
14
15
|
const fail = (res, error, label) => {
|
|
15
16
|
console.error(error);
|
|
16
17
|
CrashLogger_1.CrashLogger.log(error, label);
|
|
18
|
+
if (error instanceof InvalidJsonPayloadError) {
|
|
19
|
+
res.setHeader?.('X-Error-Code', error.code);
|
|
20
|
+
json(res, 400, { error: error.message, errorCode: error.code, success: false });
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (error instanceof knowledgePayload_1.KnowledgePayloadError) {
|
|
24
|
+
json(res, error.statusCode, { success: false, error: error.message, code: error.code });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
17
27
|
json(res, 500, { success: false, error: String(error) });
|
|
18
28
|
};
|
|
29
|
+
class InvalidJsonPayloadError extends Error {
|
|
30
|
+
constructor() {
|
|
31
|
+
super('Invalid JSON body.');
|
|
32
|
+
this.code = 'invalid_json';
|
|
33
|
+
this.name = 'InvalidJsonPayloadError';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const parseJsonBody = (body) => {
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(body);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
throw new InvalidJsonPayloadError();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
19
44
|
const readBody = (req) => new Promise((resolve, reject) => {
|
|
20
45
|
const chunks = [];
|
|
21
|
-
|
|
46
|
+
let totalBytes = 0;
|
|
47
|
+
req.on('data', (c) => {
|
|
48
|
+
totalBytes += c.length;
|
|
49
|
+
if (totalBytes > 128 * 1024 * 1024) {
|
|
50
|
+
reject(new knowledgePayload_1.KnowledgePayloadError('Request body exceeds 128 MiB.'));
|
|
51
|
+
req.destroy?.();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
chunks.push(c);
|
|
55
|
+
});
|
|
22
56
|
req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
|
|
23
57
|
req.on('error', reject);
|
|
24
58
|
});
|
|
@@ -333,34 +367,6 @@ function registerKnowledgeRoutes(ctx) {
|
|
|
333
367
|
}
|
|
334
368
|
},
|
|
335
369
|
},
|
|
336
|
-
{
|
|
337
|
-
method: 'GET',
|
|
338
|
-
path: api('/conversation/turn-cache/diagnostics'),
|
|
339
|
-
handler: async (req, res) => {
|
|
340
|
-
try {
|
|
341
|
-
const params = parseQuery(req);
|
|
342
|
-
const result = await knowledgeLearningPlatform.getAgentConversationTurnCacheDiagnostics({ format: params.get('format') || 'summary' });
|
|
343
|
-
ok(res, result);
|
|
344
|
-
}
|
|
345
|
-
catch (e) {
|
|
346
|
-
fail(res, e, 'GET /api/knowledge/conversation/turn-cache/diagnostics');
|
|
347
|
-
}
|
|
348
|
-
},
|
|
349
|
-
},
|
|
350
|
-
{
|
|
351
|
-
method: 'GET',
|
|
352
|
-
path: api('/conversation/turn-cache/diagnostics/trend'),
|
|
353
|
-
handler: async (req, res) => {
|
|
354
|
-
try {
|
|
355
|
-
const params = parseQuery(req);
|
|
356
|
-
const result = await knowledgeLearningPlatform.getAgentConversationTurnCacheTrend({ limit: Number(params.get('limit')) || 20, windowSize: Number(params.get('windowSize')) || 6, minSamples: Number(params.get('minSamples')) || 3 });
|
|
357
|
-
ok(res, result);
|
|
358
|
-
}
|
|
359
|
-
catch (e) {
|
|
360
|
-
fail(res, e, 'GET /api/knowledge/conversation/turn-cache/diagnostics/trend');
|
|
361
|
-
}
|
|
362
|
-
},
|
|
363
|
-
},
|
|
364
370
|
{
|
|
365
371
|
method: 'GET',
|
|
366
372
|
path: api('/runtime-capability-matrix'),
|
|
@@ -534,8 +540,9 @@ function registerKnowledgeRoutes(ctx) {
|
|
|
534
540
|
handler: async (req, res) => {
|
|
535
541
|
try {
|
|
536
542
|
const body = await readBody(req);
|
|
537
|
-
const result = await knowledgeIngestor.ingestKnowledge(
|
|
538
|
-
|
|
543
|
+
const result = await knowledgeIngestor.ingestKnowledge((0, knowledgePayload_1.parseKnowledgeIngestBody)(body));
|
|
544
|
+
// Preserve the legacy response shape; diagnostics remain available via runtime metrics.
|
|
545
|
+
ok(res, { result });
|
|
539
546
|
}
|
|
540
547
|
catch (e) {
|
|
541
548
|
fail(res, e, 'POST /api/knowledge/ingest');
|
|
@@ -548,8 +555,10 @@ function registerKnowledgeRoutes(ctx) {
|
|
|
548
555
|
handler: async (req, res) => {
|
|
549
556
|
try {
|
|
550
557
|
const body = await readBody(req);
|
|
551
|
-
|
|
552
|
-
|
|
558
|
+
// Keep the migration boundary on the legacy platform facade so
|
|
559
|
+
// registry dispatch preserves the established response contract.
|
|
560
|
+
const result = await knowledgeLearningPlatform.queryKnowledge((0, requestNormalization_1.normalizeKnowledgeQueryRequestPayload)(parseJsonBody(body)));
|
|
561
|
+
ok(res, { result });
|
|
553
562
|
}
|
|
554
563
|
catch (e) {
|
|
555
564
|
fail(res, e, 'POST /api/knowledge/query');
|
|
@@ -804,6 +813,10 @@ function registerKnowledgeRoutes(ctx) {
|
|
|
804
813
|
handler: async (req, res) => {
|
|
805
814
|
try {
|
|
806
815
|
const body = await readBody(req);
|
|
816
|
+
if (ctx.executeQueryBackendConfigUpdate) {
|
|
817
|
+
ok(res, await ctx.executeQueryBackendConfigUpdate(JSON.parse(body)));
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
807
820
|
const result = await knowledgeLearningPlatform.updateQueryBackendConfig(JSON.parse(body));
|
|
808
821
|
ok(res, { result });
|
|
809
822
|
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KnowledgePayloadError = void 0;
|
|
4
|
+
exports.parseKnowledgeIngestBody = parseKnowledgeIngestBody;
|
|
5
|
+
const MAX_DOCUMENTS_PER_REQUEST = 5000;
|
|
6
|
+
const MAX_ALIASES_PER_DOCUMENT = 64;
|
|
7
|
+
const MAX_ALIAS_LENGTH = 2048;
|
|
8
|
+
const MAX_CONTENT_BYTES = 64 * 1024 * 1024;
|
|
9
|
+
class KnowledgePayloadError extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.statusCode = 400;
|
|
13
|
+
this.code = 'invalid_knowledge_payload';
|
|
14
|
+
this.name = 'KnowledgePayloadError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.KnowledgePayloadError = KnowledgePayloadError;
|
|
18
|
+
function isRecord(value) {
|
|
19
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
20
|
+
}
|
|
21
|
+
function firstString(record, keys) {
|
|
22
|
+
for (const key of keys) {
|
|
23
|
+
const value = record[key];
|
|
24
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
25
|
+
return value.trim();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
function firstValue(record, keys) {
|
|
31
|
+
for (const key of keys) {
|
|
32
|
+
if (Object.prototype.hasOwnProperty.call(record, key)) {
|
|
33
|
+
return record[key];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
function normalizeStringArray(value, field) {
|
|
39
|
+
if (value === undefined || value === null) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
if (!Array.isArray(value)) {
|
|
43
|
+
throw new KnowledgePayloadError(`${field} must be an array of strings.`);
|
|
44
|
+
}
|
|
45
|
+
if (value.length > MAX_ALIASES_PER_DOCUMENT) {
|
|
46
|
+
throw new KnowledgePayloadError(`${field} exceeds ${MAX_ALIASES_PER_DOCUMENT} entries.`);
|
|
47
|
+
}
|
|
48
|
+
const values = Array.from(new Set(value.map((item, index) => {
|
|
49
|
+
if (typeof item !== 'string' || item.trim().length === 0) {
|
|
50
|
+
throw new KnowledgePayloadError(`${field}[${index}] must be a non-empty string.`);
|
|
51
|
+
}
|
|
52
|
+
const normalized = item.trim();
|
|
53
|
+
if (normalized.length > MAX_ALIAS_LENGTH) {
|
|
54
|
+
throw new KnowledgePayloadError(`${field}[${index}] exceeds ${MAX_ALIAS_LENGTH} characters.`);
|
|
55
|
+
}
|
|
56
|
+
return normalized;
|
|
57
|
+
})));
|
|
58
|
+
return values;
|
|
59
|
+
}
|
|
60
|
+
function normalizeDocument(raw) {
|
|
61
|
+
if (!isRecord(raw)) {
|
|
62
|
+
throw new KnowledgePayloadError('Each ingest document must be an object.');
|
|
63
|
+
}
|
|
64
|
+
const contentRaw = firstValue(raw, ['content', 'text', 'body', 'markdown']);
|
|
65
|
+
const content = typeof contentRaw === 'string' ? contentRaw : String(contentRaw ?? '');
|
|
66
|
+
if (Buffer.byteLength(content, 'utf8') > MAX_CONTENT_BYTES) {
|
|
67
|
+
throw new KnowledgePayloadError(`Document content exceeds ${MAX_CONTENT_BYTES} bytes.`);
|
|
68
|
+
}
|
|
69
|
+
if (!content.trim() && !firstString(raw, ['documentId', 'docId', 'id', 'sourcePath', 'source_path', 'path'])) {
|
|
70
|
+
throw new KnowledgePayloadError('Document requires content or an identity.');
|
|
71
|
+
}
|
|
72
|
+
const identityAliases = normalizeStringArray(firstValue(raw, ['identityAliases', 'identity_aliases', 'aliases']), 'identityAliases');
|
|
73
|
+
return {
|
|
74
|
+
documentId: firstString(raw, ['documentId', 'docId', 'id']),
|
|
75
|
+
sourcePath: firstString(raw, ['sourcePath', 'source_path', 'path', 'filePath', 'filepath', 'file']) || '',
|
|
76
|
+
sourceUri: firstString(raw, ['sourceUri', 'source_uri', 'uri']),
|
|
77
|
+
revision: firstString(raw, ['revision', 'sourceRevision', 'source_revision']),
|
|
78
|
+
identityAliases: identityAliases.length > 0 ? identityAliases : undefined,
|
|
79
|
+
content,
|
|
80
|
+
language: firstString(raw, ['language', 'lang', 'locale']),
|
|
81
|
+
updatedAt: firstString(raw, ['updatedAt', 'updated_at', 'timestamp', 'now']),
|
|
82
|
+
workspaceId: firstString(raw, ['workspaceId', 'workspace_id']),
|
|
83
|
+
corpusId: firstString(raw, ['corpusId', 'corpus_id']),
|
|
84
|
+
exportProfileId: firstString(raw, ['exportProfileId', 'export_profile_id']),
|
|
85
|
+
metadata: isRecord(raw.metadata) ? { ...raw.metadata } : undefined,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function normalizeDelete(raw) {
|
|
89
|
+
if (!isRecord(raw)) {
|
|
90
|
+
throw new KnowledgePayloadError('Each deleted document must be an object.');
|
|
91
|
+
}
|
|
92
|
+
const identityAliases = normalizeStringArray(firstValue(raw, ['identityAliases', 'identity_aliases', 'aliases']), 'identityAliases');
|
|
93
|
+
const result = {
|
|
94
|
+
documentId: firstString(raw, ['documentId', 'docId', 'id']),
|
|
95
|
+
sourcePath: firstString(raw, ['sourcePath', 'source_path', 'path', 'filePath', 'filepath', 'file']),
|
|
96
|
+
sourceUri: firstString(raw, ['sourceUri', 'source_uri', 'uri']),
|
|
97
|
+
identityAliases: identityAliases.length > 0 ? identityAliases : undefined,
|
|
98
|
+
};
|
|
99
|
+
if (!result.documentId && !result.sourcePath && !result.sourceUri && identityAliases.length === 0) {
|
|
100
|
+
throw new KnowledgePayloadError('Delete requires documentId, sourcePath, sourceUri, or identityAliases.');
|
|
101
|
+
}
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
function normalizeMove(raw) {
|
|
105
|
+
if (!isRecord(raw)) {
|
|
106
|
+
throw new KnowledgePayloadError('Each move document must be an object.');
|
|
107
|
+
}
|
|
108
|
+
const toSourcePath = firstString(raw, ['toSourcePath', 'to_source_path', 'destinationPath', 'destination_path']);
|
|
109
|
+
if (!toSourcePath) {
|
|
110
|
+
throw new KnowledgePayloadError('Move requires toSourcePath.');
|
|
111
|
+
}
|
|
112
|
+
const aliases = normalizeStringArray(firstValue(raw, ['toIdentityAliases', 'to_identity_aliases', 'aliases']), 'toIdentityAliases');
|
|
113
|
+
const fromAliases = normalizeStringArray(firstValue(raw, ['fromIdentityAliases', 'from_identity_aliases', 'fromAliases']), 'fromIdentityAliases');
|
|
114
|
+
return {
|
|
115
|
+
documentId: firstString(raw, ['documentId', 'docId', 'id']),
|
|
116
|
+
fromSourcePath: firstString(raw, ['fromSourcePath', 'from_source_path', 'sourcePath', 'source_path']),
|
|
117
|
+
fromSourceUri: firstString(raw, ['fromSourceUri', 'from_source_uri', 'sourceUri', 'source_uri']),
|
|
118
|
+
fromIdentityAliases: fromAliases.length > 0 ? fromAliases : undefined,
|
|
119
|
+
toSourcePath,
|
|
120
|
+
toSourceUri: firstString(raw, ['toSourceUri', 'to_source_uri', 'destinationUri', 'destination_uri']),
|
|
121
|
+
toIdentityAliases: aliases.length > 0 ? aliases : undefined,
|
|
122
|
+
revision: firstString(raw, ['revision', 'sourceRevision', 'source_revision']),
|
|
123
|
+
updatedAt: firstString(raw, ['updatedAt', 'updated_at', 'timestamp', 'now']),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function normalizeOperation(raw) {
|
|
127
|
+
if (!isRecord(raw)) {
|
|
128
|
+
throw new KnowledgePayloadError('Each ingest operation must be an object.');
|
|
129
|
+
}
|
|
130
|
+
const operation = (firstString(raw, ['op', 'operation', 'action', 'type']) || 'upsert').toLowerCase();
|
|
131
|
+
const nested = firstValue(raw, ['document', 'payload', 'item']) ?? raw;
|
|
132
|
+
if (operation === 'delete' || operation === 'remove' || operation === 'del') {
|
|
133
|
+
return { op: 'delete', document: normalizeDelete(nested) };
|
|
134
|
+
}
|
|
135
|
+
if (operation === 'move' || operation === 'rename') {
|
|
136
|
+
return { op: 'move', document: normalizeMove(nested) };
|
|
137
|
+
}
|
|
138
|
+
if (operation === 'upsert' || operation === 'insert' || operation === 'update' || operation === 'put') {
|
|
139
|
+
return { op: 'upsert', document: normalizeDocument(nested) };
|
|
140
|
+
}
|
|
141
|
+
throw new KnowledgePayloadError(`Unsupported ingest operation: ${operation}.`);
|
|
142
|
+
}
|
|
143
|
+
function asArray(value) {
|
|
144
|
+
if (value === undefined || value === null)
|
|
145
|
+
return [];
|
|
146
|
+
return Array.isArray(value) ? value : [value];
|
|
147
|
+
}
|
|
148
|
+
function parseKnowledgeIngestBody(body) {
|
|
149
|
+
let payload;
|
|
150
|
+
try {
|
|
151
|
+
payload = body.trim() ? JSON.parse(body) : {};
|
|
152
|
+
}
|
|
153
|
+
catch (_error) {
|
|
154
|
+
throw new KnowledgePayloadError('Request body must be valid JSON.');
|
|
155
|
+
}
|
|
156
|
+
if (!isRecord(payload) && !Array.isArray(payload)) {
|
|
157
|
+
throw new KnowledgePayloadError('Request body must be an object or document array.');
|
|
158
|
+
}
|
|
159
|
+
const record = isRecord(payload) ? payload : {};
|
|
160
|
+
const rawDocuments = firstValue(record, ['documents', 'docs', 'items']);
|
|
161
|
+
const rawDeletes = firstValue(record, ['deletedDocuments', 'deleted', 'deletes']);
|
|
162
|
+
const rawOperations = firstValue(record, ['operations', 'ops']);
|
|
163
|
+
const documents = asArray(rawDocuments ?? (Array.isArray(payload) ? payload : undefined)).map(normalizeDocument);
|
|
164
|
+
const deletedDocuments = asArray(rawDeletes).map(normalizeDelete);
|
|
165
|
+
const operations = asArray(rawOperations).map(normalizeOperation);
|
|
166
|
+
const totalItems = documents.length + deletedDocuments.length + operations.length;
|
|
167
|
+
if (totalItems > MAX_DOCUMENTS_PER_REQUEST) {
|
|
168
|
+
throw new KnowledgePayloadError(`Ingest request exceeds ${MAX_DOCUMENTS_PER_REQUEST} documents/operations.`);
|
|
169
|
+
}
|
|
170
|
+
const incremental = typeof record.incremental === 'boolean' ? record.incremental : undefined;
|
|
171
|
+
const recomputeRelations = typeof record.recomputeRelations === 'boolean' ? record.recomputeRelations : undefined;
|
|
172
|
+
const relationRecomputeMode = firstString(record, ['relationRecomputeMode', 'relation_recompute_mode', 'recomputeMode']);
|
|
173
|
+
if (relationRecomputeMode && !['auto', 'none', 'incremental', 'full'].includes(relationRecomputeMode.toLowerCase())) {
|
|
174
|
+
throw new KnowledgePayloadError('relationRecomputeMode must be one of auto, none, incremental, full.');
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
documents: documents.length > 0 ? documents : undefined,
|
|
178
|
+
deletedDocuments: deletedDocuments.length > 0 ? deletedDocuments : undefined,
|
|
179
|
+
operations: operations.length > 0 ? operations : undefined,
|
|
180
|
+
incremental,
|
|
181
|
+
recomputeRelations,
|
|
182
|
+
relationRecomputeMode: relationRecomputeMode?.toLowerCase(),
|
|
183
|
+
ingestedAt: firstString(record, ['ingestedAt', 'timestamp', 'now']),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const knowledgePayload_1 = require("./knowledgePayload");
|
|
4
|
+
describe('knowledge HTTP payload contract', () => {
|
|
5
|
+
test('normalizes legacy aliases while preserving portable identity fields', () => {
|
|
6
|
+
const request = (0, knowledgePayload_1.parseKnowledgeIngestBody)(JSON.stringify({
|
|
7
|
+
docs: [{
|
|
8
|
+
id: 'legacy-doc',
|
|
9
|
+
path: 'Knowledge_Base/one.md',
|
|
10
|
+
text: '# One',
|
|
11
|
+
uri: 'note://workspace/v1/one.md',
|
|
12
|
+
source_revision: 'sha256:one',
|
|
13
|
+
aliases: ['one', 'notes/one.md'],
|
|
14
|
+
}],
|
|
15
|
+
}));
|
|
16
|
+
expect(request.documents?.[0]).toEqual(expect.objectContaining({
|
|
17
|
+
documentId: 'legacy-doc',
|
|
18
|
+
sourcePath: 'Knowledge_Base/one.md',
|
|
19
|
+
sourceUri: 'note://workspace/v1/one.md',
|
|
20
|
+
revision: 'sha256:one',
|
|
21
|
+
identityAliases: ['one', 'notes/one.md'],
|
|
22
|
+
content: '# One',
|
|
23
|
+
}));
|
|
24
|
+
});
|
|
25
|
+
test('accepts explicit move operations for replay', () => {
|
|
26
|
+
const request = (0, knowledgePayload_1.parseKnowledgeIngestBody)(JSON.stringify({
|
|
27
|
+
operations: [{
|
|
28
|
+
op: 'rename',
|
|
29
|
+
document: {
|
|
30
|
+
documentId: 'doc-1',
|
|
31
|
+
fromSourcePath: 'old.md',
|
|
32
|
+
toSourcePath: 'new.md',
|
|
33
|
+
toSourceUri: 'note://workspace/v1/new.md',
|
|
34
|
+
},
|
|
35
|
+
}],
|
|
36
|
+
}));
|
|
37
|
+
expect(request.operations).toEqual([expect.objectContaining({
|
|
38
|
+
op: 'move',
|
|
39
|
+
document: expect.objectContaining({
|
|
40
|
+
documentId: 'doc-1',
|
|
41
|
+
toSourcePath: 'new.md',
|
|
42
|
+
}),
|
|
43
|
+
})]);
|
|
44
|
+
});
|
|
45
|
+
test('rejects malformed identity arrays and oversized documents at the edge', () => {
|
|
46
|
+
expect(() => (0, knowledgePayload_1.parseKnowledgeIngestBody)(JSON.stringify({
|
|
47
|
+
documents: [{ content: '# invalid', aliases: ['ok', 3] }],
|
|
48
|
+
}))).toThrow(knowledgePayload_1.KnowledgePayloadError);
|
|
49
|
+
expect(() => (0, knowledgePayload_1.parseKnowledgeIngestBody)(JSON.stringify({
|
|
50
|
+
documents: [{ content: 'x'.repeat(64 * 1024 * 1024 + 1) }],
|
|
51
|
+
}))).toThrow(/exceeds 67108864 bytes/);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -161,6 +161,14 @@ function registerMarkdownRoutes(_ctx) {
|
|
|
161
161
|
const body = await readBody(req);
|
|
162
162
|
const payload = JSON.parse(body);
|
|
163
163
|
const filePath = payload.filePath || payload.path || '';
|
|
164
|
+
if (!filePath) {
|
|
165
|
+
json(res, 400, {
|
|
166
|
+
success: false,
|
|
167
|
+
error: 'Missing filePath for /api/markdown/index',
|
|
168
|
+
markdownProtocolVersion: PROTOCOL_VERSION,
|
|
169
|
+
});
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
164
172
|
if (ctx.markdownGateway) {
|
|
165
173
|
const runtimeConfig = await resolveRuntimeConfig();
|
|
166
174
|
const result = await ctx.markdownGateway.buildIndex({
|