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
package/dist/src/server.js
CHANGED
|
@@ -39,12 +39,14 @@ exports.executeNotemdCliCommand = executeNotemdCliCommand;
|
|
|
39
39
|
const http = __importStar(require("http"));
|
|
40
40
|
const fs = __importStar(require("fs"));
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
|
+
const os = __importStar(require("os"));
|
|
42
43
|
const net = __importStar(require("net"));
|
|
43
44
|
const readline = __importStar(require("readline"));
|
|
44
45
|
const events_1 = require("events");
|
|
45
46
|
const child_process_1 = require("child_process");
|
|
46
47
|
const index_1 = require("./index");
|
|
47
48
|
const CrashLogger_1 = require("./backend/utils/CrashLogger");
|
|
49
|
+
const ResourceIdentity_1 = require("./backend/ResourceIdentity");
|
|
48
50
|
const PathBridge_1 = require("./core/PathBridge");
|
|
49
51
|
const GraphMetrics_1 = require("./backend/GraphMetrics");
|
|
50
52
|
const LayoutEngine_1 = require("./backend/algorithms/LayoutEngine");
|
|
@@ -54,11 +56,18 @@ const reader_renderer_1 = require("./reader_renderer");
|
|
|
54
56
|
const native_clipboard_1 = require("./native_clipboard");
|
|
55
57
|
const notemd_1 = require("./notemd");
|
|
56
58
|
const AppConfigToml_1 = require("./notemd/AppConfigToml");
|
|
57
|
-
const providerTemplates_1 = require("./notemd/providerTemplates");
|
|
58
59
|
const MarkdownGateway_1 = require("./markdown/MarkdownGateway");
|
|
59
60
|
const learning_1 = require("./learning");
|
|
61
|
+
const requestNormalization_1 = require("./learning/requestNormalization");
|
|
62
|
+
const agentConversationSerialization_1 = require("./learning/agentConversationSerialization");
|
|
63
|
+
const agentConversationExecution_1 = require("./learning/agentConversationExecution");
|
|
64
|
+
const agentResponseBudget_1 = require("./learning/agentResponseBudget");
|
|
65
|
+
const SseResponseWriter_1 = require("./middleware/SseResponseWriter");
|
|
66
|
+
const mobileAnswerProjection_1 = require("./learning/mobileAnswerProjection");
|
|
67
|
+
const workspaceHydration_1 = require("./learning/workspaceHydration");
|
|
60
68
|
const routes_1 = require("./routes");
|
|
61
69
|
const runtimeRunbookRouteOps_1 = require("./routes/runtimeRunbookRouteOps");
|
|
70
|
+
const auth_1 = require("./middleware/auth");
|
|
62
71
|
const domains_1 = require("./learning/domains");
|
|
63
72
|
function installBrokenPipeGuard(stream) {
|
|
64
73
|
if (!stream || stream.__noteConnectionBrokenPipeGuardInstalled) {
|
|
@@ -184,6 +193,9 @@ const AGENT_WORKSPACE_DIAGNOSTICS_TRIAGE_TOP_LIMIT = 5;
|
|
|
184
193
|
const AGENT_WORKSPACE_DIAGNOSTICS_TRIAGE_TOP_LIMIT_MIN = 1;
|
|
185
194
|
const AGENT_WORKSPACE_DIAGNOSTICS_TRIAGE_TOP_LIMIT_MAX = 10;
|
|
186
195
|
const REQUEST_BODY_SPOOL_DIR = path.join(runtimePaths.projectRoot, 'tmp', 'request-bodies');
|
|
196
|
+
const KNOWLEDGE_WORKSPACE_TITLE_PREVIEW_BYTES = 16 * 1024;
|
|
197
|
+
const KNOWLEDGE_WORKSPACE_LARGE_TARGET_FILE_THRESHOLD = 100;
|
|
198
|
+
const KNOWLEDGE_WORKSPACE_MAX_SELECTIVE_HYDRATION_FILES = 32;
|
|
187
199
|
let KB_ROOT = runtimePaths.kbRoot;
|
|
188
200
|
let activeBuildKey = null;
|
|
189
201
|
let activeBuildPromise = null;
|
|
@@ -191,7 +203,7 @@ let ACTIVE_GRAPH_TARGET = 'ALL_FOLDERS';
|
|
|
191
203
|
let lastRestoreKey = null;
|
|
192
204
|
let lastRestoreTs = 0;
|
|
193
205
|
const activeKnowledgeWorkspaceSyncs = new Map();
|
|
194
|
-
const SIDECAR_RUNTIME_MANIFEST =
|
|
206
|
+
const SIDECAR_RUNTIME_MANIFEST = runtimePaths.runtimeManifestPath;
|
|
195
207
|
const notemdService = new notemd_1.NotemdService();
|
|
196
208
|
const notemdLlmClient = new notemd_1.LlmProviderClient();
|
|
197
209
|
const API_REQUEST_TRACE_MAX_RECORDS = 400;
|
|
@@ -4808,12 +4820,22 @@ const knowledgeGraphStore = (0, learning_1.createKnowledgeGraphStore)({
|
|
|
4808
4820
|
backend: KNOWLEDGE_GRAPH_STORE_BACKEND,
|
|
4809
4821
|
filePath: KNOWLEDGE_GRAPH_STORE_PATH,
|
|
4810
4822
|
graphDbAdapter: KNOWLEDGE_GRAPHDB_ADAPTER,
|
|
4823
|
+
graphDbRequestedProvider: KNOWLEDGE_GRAPHDB_ADAPTER_PROVIDER,
|
|
4811
4824
|
graphDbFallbackEnabled: KNOWLEDGE_GRAPHDB_FALLBACK_ENABLED,
|
|
4812
4825
|
graphDbOperationMode: KNOWLEDGE_GRAPHDB_OPERATION_MODE,
|
|
4813
4826
|
});
|
|
4814
4827
|
const defaultLocalTutorAdapter = createNotemdTutorAdapter('local');
|
|
4815
4828
|
const defaultCloudTutorAdapter = createNotemdTutorAdapter('cloud');
|
|
4829
|
+
const SERVER_RESPONSE_LOW_MEMORY_BYTES = 4 * 1024 ** 3;
|
|
4830
|
+
const SERVER_RESPONSE_HIGH_MEMORY_BYTES = 8 * 1024 ** 3;
|
|
4831
|
+
const serverResponseMemoryBytes = os.totalmem();
|
|
4832
|
+
const SERVER_RESPONSE_BUDGET_CAPABILITY = {
|
|
4833
|
+
memoryClass: serverResponseMemoryBytes < SERVER_RESPONSE_LOW_MEMORY_BYTES ? 'low'
|
|
4834
|
+
: serverResponseMemoryBytes >= SERVER_RESPONSE_HIGH_MEMORY_BYTES ? 'high' : 'standard',
|
|
4835
|
+
workload: 'max',
|
|
4836
|
+
};
|
|
4816
4837
|
const knowledgeLearningPlatform = (0, learning_1.createKnowledgeLearningPlatform)({
|
|
4838
|
+
responseBudgetHostCapability: SERVER_RESPONSE_BUDGET_CAPABILITY,
|
|
4817
4839
|
store: knowledgeGraphStore,
|
|
4818
4840
|
learningQualityThresholds: LEARNING_QUALITY_THRESHOLDS,
|
|
4819
4841
|
studySessionPlanQualityAdaptiveThresholdsEnabled: STUDY_SESSION_PLAN_QUALITY_ADAPTIVE_THRESHOLDS_ENABLED,
|
|
@@ -4964,7 +4986,6 @@ const markdownGateway = new MarkdownGateway_1.MarkdownGateway({
|
|
|
4964
4986
|
warn: (...args) => warnDiagnostic(...args),
|
|
4965
4987
|
},
|
|
4966
4988
|
});
|
|
4967
|
-
const NOTEMD_ACTIVE_OPERATIONS = new Map();
|
|
4968
4989
|
const AGENT_CONVERSATION_TURN_CACHE = new Map();
|
|
4969
4990
|
const AGENT_CONVERSATION_TURN_CACHE_COUNTERS = {
|
|
4970
4991
|
cacheHitCount: 0,
|
|
@@ -6557,19 +6578,6 @@ function getRawRequestPathname(rawUrl) {
|
|
|
6557
6578
|
}
|
|
6558
6579
|
return requestTarget || '/';
|
|
6559
6580
|
}
|
|
6560
|
-
function extractRequestToken(req) {
|
|
6561
|
-
const headerToken = typeof req.headers['x-noteconnection-token'] === 'string'
|
|
6562
|
-
? req.headers['x-noteconnection-token'].trim()
|
|
6563
|
-
: '';
|
|
6564
|
-
if (headerToken) {
|
|
6565
|
-
return headerToken;
|
|
6566
|
-
}
|
|
6567
|
-
const authHeader = typeof req.headers.authorization === 'string' ? req.headers.authorization.trim() : '';
|
|
6568
|
-
if (authHeader.toLowerCase().startsWith('bearer ')) {
|
|
6569
|
-
return authHeader.slice(7).trim();
|
|
6570
|
-
}
|
|
6571
|
-
return '';
|
|
6572
|
-
}
|
|
6573
6581
|
function createRequestId() {
|
|
6574
6582
|
return `req-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
6575
6583
|
}
|
|
@@ -6597,7 +6605,7 @@ function isAuthorizedRequest(req) {
|
|
|
6597
6605
|
if (!AUTH_TOKEN || !isProtectedRequest(req)) {
|
|
6598
6606
|
return true;
|
|
6599
6607
|
}
|
|
6600
|
-
return
|
|
6608
|
+
return (0, auth_1.isRequestTokenAuthorized)(req, AUTH_TOKEN);
|
|
6601
6609
|
}
|
|
6602
6610
|
function isGeneratedGraphAsset(filename) {
|
|
6603
6611
|
return (filename === 'data.js' ||
|
|
@@ -6907,6 +6915,18 @@ function classifyApiInputValidationError(error) {
|
|
|
6907
6915
|
return null;
|
|
6908
6916
|
}
|
|
6909
6917
|
function writeApiErrorResponse(res, error, options) {
|
|
6918
|
+
if (res.destroyed || res.writableEnded)
|
|
6919
|
+
return;
|
|
6920
|
+
if (res.headersSent) {
|
|
6921
|
+
res.destroy();
|
|
6922
|
+
return;
|
|
6923
|
+
}
|
|
6924
|
+
if (error instanceof agentConversationExecution_1.AgentConversationExecutionError || error instanceof agentConversationSerialization_1.AgentConversationSerializationError) {
|
|
6925
|
+
setApiErrorCodeHeader(res, error.code);
|
|
6926
|
+
res.writeHead(error.statusCode, { 'Content-Type': 'application/json' });
|
|
6927
|
+
res.end(JSON.stringify(createApiErrorEnvelope({ error: error.message, errorCode: error.code, requestId: options.requestId })));
|
|
6928
|
+
return;
|
|
6929
|
+
}
|
|
6910
6930
|
if (writeBodyParseErrorResponse(res, error, { requestId: options.requestId })) {
|
|
6911
6931
|
return;
|
|
6912
6932
|
}
|
|
@@ -7139,22 +7159,6 @@ function normalizeKnowledgeQueryRequestPayload(payload) {
|
|
|
7139
7159
|
: undefined,
|
|
7140
7160
|
};
|
|
7141
7161
|
}
|
|
7142
|
-
function normalizeAgentConversationRequestPayload(payload) {
|
|
7143
|
-
const record = isObjectRecord(payload) ? payload : {};
|
|
7144
|
-
const topKValue = parsePositiveIntegerValue(readFirstPresentValue(record, ['topK', 'k', 'limit']));
|
|
7145
|
-
const normalizedQueryRequest = normalizeKnowledgeQueryRequestPayload(payload);
|
|
7146
|
-
return {
|
|
7147
|
-
userId: String(readFirstPresentValue(record, ['userId', 'user_id', 'learnerId']) || '').trim(),
|
|
7148
|
-
sessionId: readFirstNonEmptyString(record, ['sessionId', 'session_id']),
|
|
7149
|
-
activeTarget: readFirstNonEmptyString(record, ['activeTarget', 'active_target', 'target']),
|
|
7150
|
-
message: readFirstNonEmptyString(record, ['message', 'prompt', 'query', 'q', 'text']) || '',
|
|
7151
|
-
topK: topKValue > 0 ? topKValue : undefined,
|
|
7152
|
-
asOf: readFirstNonEmptyString(record, ['asOf', 'as_of', 'timestamp', 'now']),
|
|
7153
|
-
persistMemory: readFirstPresentValue(record, ['persistMemory', 'persist_memory']) !== false,
|
|
7154
|
-
memoryNamespace: readFirstNonEmptyString(record, ['memoryNamespace', 'memory_namespace']),
|
|
7155
|
-
scope: normalizedQueryRequest.scope,
|
|
7156
|
-
};
|
|
7157
|
-
}
|
|
7158
7162
|
function requestAcceptsEventStream(req) {
|
|
7159
7163
|
const acceptHeader = String(req.headers.accept || '').toLowerCase();
|
|
7160
7164
|
return acceptHeader.includes('text/event-stream');
|
|
@@ -7199,11 +7203,43 @@ function normalizeAgentConversationTopK(value) {
|
|
|
7199
7203
|
return Math.max(1, Math.floor(numeric));
|
|
7200
7204
|
}
|
|
7201
7205
|
function buildAgentConversationRequestFingerprint(requestPayload) {
|
|
7206
|
+
const scope = requestPayload.scope;
|
|
7207
|
+
const normalizedScope = scope
|
|
7208
|
+
? {
|
|
7209
|
+
workspaceId: String(scope.workspaceId || '').trim(),
|
|
7210
|
+
corpusId: String(scope.corpusId || '').trim(),
|
|
7211
|
+
documentIds: Array.from(new Set(scope.documentIds || [])).sort(),
|
|
7212
|
+
atomIds: Array.from(new Set(scope.atomIds || [])).sort(),
|
|
7213
|
+
sourcePathPrefixes: Array.from(new Set(scope.sourcePathPrefixes || [])).sort(),
|
|
7214
|
+
languages: Array.from(new Set(scope.languages || [])).sort(),
|
|
7215
|
+
}
|
|
7216
|
+
: null;
|
|
7202
7217
|
return JSON.stringify({
|
|
7203
7218
|
userId: String(requestPayload.userId || '').trim(),
|
|
7219
|
+
sessionId: String(requestPayload.sessionId || '').trim(),
|
|
7220
|
+
activeTarget: String(requestPayload.activeTarget || '').trim(),
|
|
7204
7221
|
message: String(requestPayload.message || '').trim(),
|
|
7222
|
+
answerLanguage: String(requestPayload.answerLanguage || 'auto').trim(),
|
|
7223
|
+
responseMode: String(requestPayload.responseMode || 'slim').trim(),
|
|
7224
|
+
responseBudgetMode: String(requestPayload.responseBudgetMode || 'adaptive').trim(),
|
|
7225
|
+
responseBudgetCapability: requestPayload.responseBudgetCapability
|
|
7226
|
+
? {
|
|
7227
|
+
memoryClass: String(requestPayload.responseBudgetCapability.memoryClass || '').trim(),
|
|
7228
|
+
workload: String(requestPayload.responseBudgetCapability.workload || '').trim(),
|
|
7229
|
+
maxReportCharsHint: Number.isFinite(Number(requestPayload.responseBudgetCapability.maxReportCharsHint))
|
|
7230
|
+
? Math.floor(Number(requestPayload.responseBudgetCapability.maxReportCharsHint))
|
|
7231
|
+
: null,
|
|
7232
|
+
maxSerializedBytesHint: Number.isFinite(Number(requestPayload.responseBudgetCapability.maxSerializedBytesHint))
|
|
7233
|
+
? Math.floor(Number(requestPayload.responseBudgetCapability.maxSerializedBytesHint))
|
|
7234
|
+
: null,
|
|
7235
|
+
}
|
|
7236
|
+
: null,
|
|
7237
|
+
responseProfile: String(requestPayload.responseProfile || 'default').trim(),
|
|
7205
7238
|
topK: normalizeAgentConversationTopK(requestPayload.topK),
|
|
7206
7239
|
asOf: String(requestPayload.asOf || '').trim(),
|
|
7240
|
+
persistMemory: requestPayload.persistMemory !== false,
|
|
7241
|
+
memoryNamespace: String(requestPayload.memoryNamespace || '').trim(),
|
|
7242
|
+
scope: normalizedScope,
|
|
7207
7243
|
});
|
|
7208
7244
|
}
|
|
7209
7245
|
function pruneAgentConversationTurnCache(nowMs = Date.now()) {
|
|
@@ -7253,8 +7289,13 @@ function getOrCreateAgentConversationTurnCacheRecord(turnId, requestFingerprint)
|
|
|
7253
7289
|
return existing;
|
|
7254
7290
|
}
|
|
7255
7291
|
AGENT_CONVERSATION_TURN_CACHE_COUNTERS.cacheMissCount += 1;
|
|
7292
|
+
const runningTurns = Array.from(AGENT_CONVERSATION_TURN_CACHE.values()).filter(record => record.status === 'running').length;
|
|
7293
|
+
if (runningTurns >= agentConversationExecution_1.DEFAULT_AGENT_CONVERSATION_EXECUTION_POLICY.maxPendingTurns)
|
|
7294
|
+
throw new agentConversationExecution_1.AgentConversationExecutionError('runtime_turn_capacity');
|
|
7256
7295
|
const nowMs = Date.now();
|
|
7257
7296
|
const record = {
|
|
7297
|
+
controller: new AbortController(),
|
|
7298
|
+
consumers: 0,
|
|
7258
7299
|
turnId,
|
|
7259
7300
|
requestFingerprint,
|
|
7260
7301
|
createdAtMs: nowMs,
|
|
@@ -7266,6 +7307,31 @@ function getOrCreateAgentConversationTurnCacheRecord(turnId, requestFingerprint)
|
|
|
7266
7307
|
pruneAgentConversationTurnCache(nowMs);
|
|
7267
7308
|
return record;
|
|
7268
7309
|
}
|
|
7310
|
+
function attachAgentConversationClient(record, req, res) {
|
|
7311
|
+
record.consumers++;
|
|
7312
|
+
let attached = true;
|
|
7313
|
+
const release = () => {
|
|
7314
|
+
if (!attached)
|
|
7315
|
+
return;
|
|
7316
|
+
attached = false;
|
|
7317
|
+
record.consumers--;
|
|
7318
|
+
req.removeListener('aborted', release);
|
|
7319
|
+
res.removeListener('close', release);
|
|
7320
|
+
if (record.status === 'running' && record.consumers === 0) {
|
|
7321
|
+
record.controller.abort(new agentConversationExecution_1.AgentConversationExecutionError('runtime_cancelled'));
|
|
7322
|
+
if (!record.inFlight) {
|
|
7323
|
+
record.status = 'failed';
|
|
7324
|
+
record.failure = { error: 'runtime_cancelled', errorCode: 'runtime_cancelled' };
|
|
7325
|
+
appendAgentConversationTurnCacheEvent(record, { type: 'turn_failed', turnId: record.turnId, emittedAt: new Date().toISOString(), ...record.failure });
|
|
7326
|
+
}
|
|
7327
|
+
}
|
|
7328
|
+
};
|
|
7329
|
+
req.once('aborted', release);
|
|
7330
|
+
res.once('close', release);
|
|
7331
|
+
if (req.aborted || res.destroyed)
|
|
7332
|
+
release();
|
|
7333
|
+
return release;
|
|
7334
|
+
}
|
|
7269
7335
|
function appendAgentConversationTurnCacheEvent(record, event) {
|
|
7270
7336
|
if (record.events.length >= AGENT_CONVERSATION_TURN_CACHE_MAX_EVENTS_PER_TURN) {
|
|
7271
7337
|
record.events.shift();
|
|
@@ -7273,12 +7339,58 @@ function appendAgentConversationTurnCacheEvent(record, event) {
|
|
|
7273
7339
|
record.events.push(event);
|
|
7274
7340
|
record.updatedAtMs = Date.now();
|
|
7275
7341
|
}
|
|
7276
|
-
function replayAgentConversationTurnEvents(res, record) {
|
|
7342
|
+
function replayAgentConversationTurnEvents(res, record, requestPayload) {
|
|
7277
7343
|
AGENT_CONVERSATION_TURN_CACHE_COUNTERS.replayResponseCount += 1;
|
|
7278
7344
|
AGENT_CONVERSATION_TURN_CACHE_COUNTERS.replayedEventCount += record.events.length;
|
|
7279
7345
|
for (const event of record.events) {
|
|
7280
|
-
writeSseEvent(res, event.type, event);
|
|
7346
|
+
writeSseEvent(res, event.type, projectAgentConversationTurnEvent(event, requestPayload));
|
|
7347
|
+
}
|
|
7348
|
+
}
|
|
7349
|
+
function buildMobileAgentConversationResponse(result) {
|
|
7350
|
+
const projection = result.mobileProjection || (0, mobileAnswerProjection_1.projectAnswerForMobile)(result);
|
|
7351
|
+
const summary = result.summary || {
|
|
7352
|
+
generatedAt: new Date().toISOString(),
|
|
7353
|
+
topK: 0,
|
|
7354
|
+
queryEvidenceCoverageRatioPct: 0,
|
|
7355
|
+
};
|
|
7356
|
+
return {
|
|
7357
|
+
userId: result.userId,
|
|
7358
|
+
sessionId: result.sessionId,
|
|
7359
|
+
assistantMessage: projection.directAnswer,
|
|
7360
|
+
answer: projection.directAnswer,
|
|
7361
|
+
message: projection.directAnswer,
|
|
7362
|
+
responseMode: 'slim',
|
|
7363
|
+
responseProfile: 'mobile_compact',
|
|
7364
|
+
mobileProjection: projection,
|
|
7365
|
+
assistantBlocks: [],
|
|
7366
|
+
knowledgePoints: [],
|
|
7367
|
+
citations: [],
|
|
7368
|
+
recalledMemories: [],
|
|
7369
|
+
memoryActions: [],
|
|
7370
|
+
summary: {
|
|
7371
|
+
generatedAt: summary.generatedAt,
|
|
7372
|
+
topK: summary.topK,
|
|
7373
|
+
returnedKnowledgePoints: projection.route.length,
|
|
7374
|
+
returnedCitations: projection.citations.length,
|
|
7375
|
+
recalledMemoryCount: 0,
|
|
7376
|
+
appliedMemoryCount: 0,
|
|
7377
|
+
queryEvidenceCoverageRatioPct: summary.queryEvidenceCoverageRatioPct,
|
|
7378
|
+
responseBudgetMode: 'adaptive',
|
|
7379
|
+
responseBudgetTier: 'standard',
|
|
7380
|
+
responseTruncated: false,
|
|
7381
|
+
},
|
|
7382
|
+
};
|
|
7383
|
+
}
|
|
7384
|
+
function projectAgentConversationTurnEvent(event, requestPayload) {
|
|
7385
|
+
if (requestPayload?.responseProfile !== 'mobile_compact'
|
|
7386
|
+
|| event.type !== 'turn_completed'
|
|
7387
|
+
|| !event.result) {
|
|
7388
|
+
return event;
|
|
7281
7389
|
}
|
|
7390
|
+
return {
|
|
7391
|
+
...event,
|
|
7392
|
+
result: buildMobileAgentConversationResponse(event.result),
|
|
7393
|
+
};
|
|
7282
7394
|
}
|
|
7283
7395
|
function buildAgentConversationInitialTurnEvents(params) {
|
|
7284
7396
|
return [
|
|
@@ -7289,6 +7401,10 @@ function buildAgentConversationInitialTurnEvents(params) {
|
|
|
7289
7401
|
request: {
|
|
7290
7402
|
userId: params.requestPayload.userId,
|
|
7291
7403
|
topK: params.topK,
|
|
7404
|
+
responseMode: params.requestPayload.responseMode === 'full' ? 'full' : 'slim',
|
|
7405
|
+
responseBudgetMode: params.requestPayload.responseBudgetMode === 'unbounded'
|
|
7406
|
+
? 'unbounded'
|
|
7407
|
+
: 'adaptive',
|
|
7292
7408
|
},
|
|
7293
7409
|
},
|
|
7294
7410
|
{
|
|
@@ -7341,7 +7457,12 @@ async function ensureAgentConversationTurnExecution(record, requestPayload, opti
|
|
|
7341
7457
|
const emit = (event) => {
|
|
7342
7458
|
appendAgentConversationTurnCacheEvent(record, event);
|
|
7343
7459
|
if (emitLiveEvent) {
|
|
7344
|
-
|
|
7460
|
+
try {
|
|
7461
|
+
emitLiveEvent(event);
|
|
7462
|
+
}
|
|
7463
|
+
catch (error) {
|
|
7464
|
+
console.warn('[SSE] Live delivery ended:', error.message);
|
|
7465
|
+
}
|
|
7345
7466
|
}
|
|
7346
7467
|
};
|
|
7347
7468
|
const nowIso = () => new Date().toISOString();
|
|
@@ -7355,51 +7476,64 @@ async function ensureAgentConversationTurnExecution(record, requestPayload, opti
|
|
|
7355
7476
|
writeLiveEvent: emitLiveEvent,
|
|
7356
7477
|
});
|
|
7357
7478
|
try {
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7479
|
+
await agentConversationExecution_1.AgentConversationExecution.run({
|
|
7480
|
+
budget: (0, agentResponseBudget_1.resolveHostedAgentResponseBudget)({
|
|
7481
|
+
responseMode: requestPayload.responseMode,
|
|
7482
|
+
responseBudgetMode: requestPayload.responseBudgetMode,
|
|
7483
|
+
capability: requestPayload.responseBudgetCapability,
|
|
7484
|
+
mobile: requestPayload.responseProfile === 'mobile_compact',
|
|
7485
|
+
}, SERVER_RESPONSE_BUDGET_CAPABILITY),
|
|
7486
|
+
policy: agentConversationExecution_1.DEFAULT_AGENT_CONVERSATION_EXECUTION_POLICY,
|
|
7487
|
+
signal: record.controller.signal,
|
|
7488
|
+
}, async () => {
|
|
7489
|
+
const hydration = await ensureLearningWorkspaceHydratedForConversationRequest(requestPayload);
|
|
7490
|
+
emit({
|
|
7491
|
+
type: 'capability_result',
|
|
7492
|
+
turnId: record.turnId,
|
|
7493
|
+
emittedAt: nowIso(),
|
|
7494
|
+
stage: 'workspace_readiness_gate',
|
|
7495
|
+
summary: {
|
|
7496
|
+
hydrated: hydration.hydrated,
|
|
7497
|
+
target: hydration.target,
|
|
7498
|
+
reason: hydration.reason,
|
|
7499
|
+
workspaceId: hydration.scope?.workspaceId || null,
|
|
7500
|
+
corpusId: hydration.scope?.corpusId || null,
|
|
7501
|
+
},
|
|
7502
|
+
});
|
|
7503
|
+
const rawResult = await knowledgeLearningPlatform.runAgentConversation(requestPayload, record.controller.signal);
|
|
7504
|
+
const result = (0, agentConversationSerialization_1.serializeAgentConversationResponse)(rawResult).result;
|
|
7505
|
+
record.status = 'completed';
|
|
7506
|
+
record.result = result;
|
|
7507
|
+
record.failure = undefined;
|
|
7508
|
+
AGENT_CONVERSATION_TURN_CACHE_COUNTERS.executionSuccessCount += 1;
|
|
7509
|
+
emit({
|
|
7510
|
+
type: 'capability_result',
|
|
7511
|
+
turnId: record.turnId,
|
|
7512
|
+
emittedAt: nowIso(),
|
|
7513
|
+
stage: 'query_local_knowledge',
|
|
7514
|
+
summary: {
|
|
7515
|
+
returnedKnowledgePoints: Number(result.summary.returnedKnowledgePoints || 0),
|
|
7516
|
+
queryEvidenceCoverageRatioPct: Number(result.summary.queryEvidenceCoverageRatioPct || 0),
|
|
7517
|
+
},
|
|
7518
|
+
});
|
|
7519
|
+
emit({
|
|
7520
|
+
type: 'turn_completed',
|
|
7521
|
+
turnId: record.turnId,
|
|
7522
|
+
emittedAt: nowIso(),
|
|
7523
|
+
result,
|
|
7524
|
+
});
|
|
7392
7525
|
});
|
|
7393
7526
|
}
|
|
7394
7527
|
catch (executionError) {
|
|
7395
7528
|
const validation = classifyApiInputValidationError(executionError);
|
|
7529
|
+
const runtimeError = executionError instanceof agentConversationExecution_1.AgentConversationExecutionError || executionError instanceof agentConversationSerialization_1.AgentConversationSerializationError;
|
|
7396
7530
|
const failure = {
|
|
7397
7531
|
error: validation?.error || String(executionError?.message
|
|
7398
7532
|
|| executionError
|
|
7399
7533
|
|| 'unknown_error'),
|
|
7400
|
-
errorCode: validation?.errorCode || 'internal_error',
|
|
7534
|
+
errorCode: runtimeError ? executionError.code : validation?.errorCode || 'internal_error',
|
|
7401
7535
|
};
|
|
7402
|
-
if (!validation) {
|
|
7536
|
+
if (!validation && !runtimeError) {
|
|
7403
7537
|
console.error(executionError);
|
|
7404
7538
|
CrashLogger_1.CrashLogger.log(executionError, 'API:POST /api/knowledge/conversation [turn-cache]');
|
|
7405
7539
|
}
|
|
@@ -7436,6 +7570,10 @@ function throwAgentConversationCachedFailure(failureLike) {
|
|
|
7436
7570
|
error: 'conversation_turn_failed',
|
|
7437
7571
|
errorCode: 'internal_error',
|
|
7438
7572
|
};
|
|
7573
|
+
if (failure.errorCode === 'runtime_serialized_bytes_limit')
|
|
7574
|
+
throw new agentConversationSerialization_1.AgentConversationSerializationError();
|
|
7575
|
+
if (failure.errorCode?.startsWith('runtime_'))
|
|
7576
|
+
throw new agentConversationExecution_1.AgentConversationExecutionError(failure.errorCode);
|
|
7439
7577
|
if (failure.errorCode && failure.errorCode !== 'internal_error') {
|
|
7440
7578
|
throw new learning_1.InvalidRequestError(failure.error, {
|
|
7441
7579
|
errorCode: failure.errorCode,
|
|
@@ -7620,12 +7758,18 @@ function normalizeKnowledgeDocumentInputPayload(payload) {
|
|
|
7620
7758
|
: String(contentRaw ?? '');
|
|
7621
7759
|
const language = readFirstNonEmptyString(record, ['language', 'lang', 'locale']);
|
|
7622
7760
|
const updatedAt = readFirstNonEmptyString(record, ['updatedAt', 'updated_at', 'timestamp', 'now']);
|
|
7761
|
+
const sourceUri = readFirstNonEmptyString(record, ['sourceUri', 'source_uri', 'uri']);
|
|
7762
|
+
const revision = readFirstNonEmptyString(record, ['revision', 'sourceRevision', 'source_revision']);
|
|
7763
|
+
const identityAliases = normalizeStringArrayValue(readFirstPresentValue(record, ['identityAliases', 'identity_aliases', 'aliases']));
|
|
7623
7764
|
if (!documentId && !sourcePath && !content.trim()) {
|
|
7624
7765
|
return null;
|
|
7625
7766
|
}
|
|
7626
7767
|
return {
|
|
7627
7768
|
documentId,
|
|
7628
7769
|
sourcePath: sourcePath || '',
|
|
7770
|
+
sourceUri,
|
|
7771
|
+
revision,
|
|
7772
|
+
identityAliases: identityAliases.length > 0 ? identityAliases : undefined,
|
|
7629
7773
|
content,
|
|
7630
7774
|
language,
|
|
7631
7775
|
updatedAt,
|
|
@@ -7642,12 +7786,16 @@ function normalizeKnowledgeDocumentDeletePayload(payload) {
|
|
|
7642
7786
|
'filepath',
|
|
7643
7787
|
'file',
|
|
7644
7788
|
]);
|
|
7645
|
-
|
|
7789
|
+
const sourceUri = readFirstNonEmptyString(record, ['sourceUri', 'source_uri', 'uri']);
|
|
7790
|
+
const identityAliases = normalizeStringArrayValue(readFirstPresentValue(record, ['identityAliases', 'identity_aliases', 'aliases']));
|
|
7791
|
+
if (!documentId && !sourcePath && !sourceUri && identityAliases.length === 0) {
|
|
7646
7792
|
return null;
|
|
7647
7793
|
}
|
|
7648
7794
|
return {
|
|
7649
7795
|
documentId,
|
|
7650
7796
|
sourcePath,
|
|
7797
|
+
sourceUri,
|
|
7798
|
+
identityAliases: identityAliases.length > 0 ? identityAliases : undefined,
|
|
7651
7799
|
};
|
|
7652
7800
|
}
|
|
7653
7801
|
function normalizeKnowledgeIngestOperationPayload(payload) {
|
|
@@ -7656,6 +7804,33 @@ function normalizeKnowledgeIngestOperationPayload(payload) {
|
|
|
7656
7804
|
const normalizedOp = String(opRaw || '').trim().toLowerCase();
|
|
7657
7805
|
const documentRaw = readFirstPresentValue(record, ['document', 'payload', 'item']);
|
|
7658
7806
|
const operationPayload = typeof documentRaw !== 'undefined' ? documentRaw : payload;
|
|
7807
|
+
if (normalizedOp === 'move'
|
|
7808
|
+
|| normalizedOp === 'rename') {
|
|
7809
|
+
const moveRecord = isObjectRecord(operationPayload) ? operationPayload : {};
|
|
7810
|
+
const toSourcePath = readFirstNonEmptyString(moveRecord, [
|
|
7811
|
+
'toSourcePath',
|
|
7812
|
+
'to_source_path',
|
|
7813
|
+
'destinationPath',
|
|
7814
|
+
'destination_path',
|
|
7815
|
+
]);
|
|
7816
|
+
if (!toSourcePath) {
|
|
7817
|
+
return null;
|
|
7818
|
+
}
|
|
7819
|
+
return {
|
|
7820
|
+
op: 'move',
|
|
7821
|
+
document: {
|
|
7822
|
+
documentId: readFirstNonEmptyString(moveRecord, ['documentId', 'docId', 'id']),
|
|
7823
|
+
fromSourcePath: readFirstNonEmptyString(moveRecord, ['fromSourcePath', 'from_source_path', 'sourcePath', 'source_path']),
|
|
7824
|
+
fromSourceUri: readFirstNonEmptyString(moveRecord, ['fromSourceUri', 'from_source_uri', 'sourceUri', 'source_uri']),
|
|
7825
|
+
fromIdentityAliases: normalizeStringArrayValue(readFirstPresentValue(moveRecord, ['fromIdentityAliases', 'from_identity_aliases', 'fromAliases'])),
|
|
7826
|
+
toSourcePath,
|
|
7827
|
+
toSourceUri: readFirstNonEmptyString(moveRecord, ['toSourceUri', 'to_source_uri', 'destinationUri', 'destination_uri']),
|
|
7828
|
+
toIdentityAliases: normalizeStringArrayValue(readFirstPresentValue(moveRecord, ['toIdentityAliases', 'to_identity_aliases', 'aliases'])),
|
|
7829
|
+
revision: readFirstNonEmptyString(moveRecord, ['revision', 'sourceRevision', 'source_revision']),
|
|
7830
|
+
updatedAt: readFirstNonEmptyString(moveRecord, ['updatedAt', 'updated_at', 'timestamp', 'now']),
|
|
7831
|
+
},
|
|
7832
|
+
};
|
|
7833
|
+
}
|
|
7659
7834
|
if (normalizedOp === 'delete'
|
|
7660
7835
|
|| normalizedOp === 'remove'
|
|
7661
7836
|
|| normalizedOp === 'del') {
|
|
@@ -8848,56 +9023,6 @@ async function persistNotemdSettings(settingsLike) {
|
|
|
8848
9023
|
cachedNotemdSettings = cloneNotemdSettings(normalized);
|
|
8849
9024
|
return cloneNotemdSettings(normalized);
|
|
8850
9025
|
}
|
|
8851
|
-
function extractNotemdWorkspaceState(settings) {
|
|
8852
|
-
return {
|
|
8853
|
-
filePath: String(settings.workspaceFilePath || '').trim(),
|
|
8854
|
-
folderPath: String(settings.workspaceFolderPath || '').trim(),
|
|
8855
|
-
outputFilePath: String(settings.workspaceOutputFilePath || '').trim(),
|
|
8856
|
-
outputFolderPath: String(settings.workspaceOutputFolderPath || '').trim(),
|
|
8857
|
-
};
|
|
8858
|
-
}
|
|
8859
|
-
function normalizeWorkspaceField(source, keys, fallback) {
|
|
8860
|
-
for (const key of keys) {
|
|
8861
|
-
if (!Object.prototype.hasOwnProperty.call(source, key)) {
|
|
8862
|
-
continue;
|
|
8863
|
-
}
|
|
8864
|
-
return String(source[key] || '').trim();
|
|
8865
|
-
}
|
|
8866
|
-
return fallback;
|
|
8867
|
-
}
|
|
8868
|
-
function applyWorkspacePatchToSettings(settings, workspacePatch) {
|
|
8869
|
-
const next = cloneNotemdSettings(settings);
|
|
8870
|
-
if (!isObjectRecord(workspacePatch)) {
|
|
8871
|
-
return next;
|
|
8872
|
-
}
|
|
8873
|
-
next.workspaceFilePath = normalizeWorkspaceField(workspacePatch, ['filePath', 'file_path', 'workspaceFilePath', 'workspace_file_path'], next.workspaceFilePath);
|
|
8874
|
-
next.workspaceFolderPath = normalizeWorkspaceField(workspacePatch, ['folderPath', 'folder_path', 'workspaceFolderPath', 'workspace_folder_path'], next.workspaceFolderPath);
|
|
8875
|
-
next.workspaceOutputFilePath = normalizeWorkspaceField(workspacePatch, ['outputFilePath', 'output_file_path', 'workspaceOutputFilePath', 'workspace_output_file_path'], next.workspaceOutputFilePath);
|
|
8876
|
-
next.workspaceOutputFolderPath = normalizeWorkspaceField(workspacePatch, ['outputFolderPath', 'output_folder_path', 'workspaceOutputFolderPath', 'workspace_output_folder_path'], next.workspaceOutputFolderPath);
|
|
8877
|
-
return next;
|
|
8878
|
-
}
|
|
8879
|
-
async function persistNotemdWorkspacePatch(workspacePatch) {
|
|
8880
|
-
const settings = await loadNotemdSettings();
|
|
8881
|
-
const nextSettings = applyWorkspacePatchToSettings(settings, workspacePatch);
|
|
8882
|
-
const persisted = await persistNotemdSettings(nextSettings);
|
|
8883
|
-
return extractNotemdWorkspaceState(persisted);
|
|
8884
|
-
}
|
|
8885
|
-
async function ensureNotemdProviderTemplatesPersisted() {
|
|
8886
|
-
const appConfig = await (0, AppConfigToml_1.loadAppConfigToml)();
|
|
8887
|
-
const currentNotemdSection = isObjectRecord(appConfig.notemd) ? appConfig.notemd : {};
|
|
8888
|
-
const nextNotemdSection = (0, providerTemplates_1.mergeProviderTemplatesIntoNotemdSection)(currentNotemdSection);
|
|
8889
|
-
const persisted = JSON.stringify(currentNotemdSection) !== JSON.stringify(nextNotemdSection);
|
|
8890
|
-
if (persisted) {
|
|
8891
|
-
await (0, AppConfigToml_1.saveAppConfigToml)({
|
|
8892
|
-
...appConfig,
|
|
8893
|
-
notemd: nextNotemdSection,
|
|
8894
|
-
});
|
|
8895
|
-
}
|
|
8896
|
-
return {
|
|
8897
|
-
configPath: (0, AppConfigToml_1.resolveAppConfigPath)(),
|
|
8898
|
-
persisted,
|
|
8899
|
-
};
|
|
8900
|
-
}
|
|
8901
9026
|
function clonePathModeSettings(settings) {
|
|
8902
9027
|
return JSON.parse(JSON.stringify(settings));
|
|
8903
9028
|
}
|
|
@@ -8942,74 +9067,25 @@ async function persistFrontendSettings(settingsLike) {
|
|
|
8942
9067
|
cachedFrontendSettings = cloneFrontendSettings(persisted);
|
|
8943
9068
|
return cloneFrontendSettings(persisted);
|
|
8944
9069
|
}
|
|
8945
|
-
|
|
8946
|
-
return `notemd-${Date.now()}-${Math.random().toString(16).slice(2, 10)}`;
|
|
8947
|
-
}
|
|
8948
|
-
function createNotemdOperation(operationIdCandidate) {
|
|
8949
|
-
const requestedId = String(operationIdCandidate || '').trim();
|
|
8950
|
-
const operationId = requestedId || generateNotemdOperationId();
|
|
8951
|
-
const state = {
|
|
8952
|
-
id: operationId,
|
|
8953
|
-
controller: new AbortController(),
|
|
8954
|
-
status: 'running',
|
|
8955
|
-
createdAt: Date.now(),
|
|
8956
|
-
updatedAt: Date.now(),
|
|
8957
|
-
logs: [],
|
|
8958
|
-
};
|
|
8959
|
-
NOTEMD_ACTIVE_OPERATIONS.set(operationId, state);
|
|
8960
|
-
return state;
|
|
8961
|
-
}
|
|
8962
|
-
function finalizeNotemdOperation(state, status) {
|
|
8963
|
-
state.status = status;
|
|
8964
|
-
state.updatedAt = Date.now();
|
|
8965
|
-
setTimeout(() => {
|
|
8966
|
-
const current = NOTEMD_ACTIVE_OPERATIONS.get(state.id);
|
|
8967
|
-
if (current === state && current.status !== 'running') {
|
|
8968
|
-
NOTEMD_ACTIVE_OPERATIONS.delete(state.id);
|
|
8969
|
-
}
|
|
8970
|
-
}, 60000);
|
|
8971
|
-
}
|
|
9070
|
+
const sseResponseWriters = new WeakMap();
|
|
8972
9071
|
function writeSseEvent(res, eventType, payload) {
|
|
8973
9072
|
if (res.writableEnded || res.destroyed) {
|
|
8974
9073
|
return;
|
|
8975
9074
|
}
|
|
8976
|
-
|
|
8977
|
-
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
catch (_error) {
|
|
8981
|
-
// Ignore stream write failures when client disconnected mid-stream.
|
|
9075
|
+
let writer = sseResponseWriters.get(res);
|
|
9076
|
+
if (!writer) {
|
|
9077
|
+
writer = new SseResponseWriter_1.SseResponseWriter(res);
|
|
9078
|
+
sseResponseWriters.set(res, writer);
|
|
8982
9079
|
}
|
|
9080
|
+
const serialized = (0, agentConversationSerialization_1.serializeAgentConversationTurnEvent)(eventType, payload);
|
|
9081
|
+
writer.write(`event: ${eventType}\ndata: ${serialized.json}\n\n`);
|
|
8983
9082
|
}
|
|
8984
|
-
function
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
timestamp: Date.now(),
|
|
8991
|
-
};
|
|
8992
|
-
state.logs.push(event);
|
|
8993
|
-
state.updatedAt = event.timestamp;
|
|
8994
|
-
if (res) {
|
|
8995
|
-
writeSseEvent(res, event.type, event);
|
|
8996
|
-
}
|
|
8997
|
-
},
|
|
8998
|
-
isCancelled: () => state.controller.signal.aborted,
|
|
8999
|
-
};
|
|
9000
|
-
}
|
|
9001
|
-
function shouldStreamNotemdResponse(req) {
|
|
9002
|
-
const acceptHeader = typeof req.headers.accept === 'string' ? req.headers.accept : '';
|
|
9003
|
-
if (acceptHeader.includes('text/event-stream')) {
|
|
9004
|
-
return true;
|
|
9005
|
-
}
|
|
9006
|
-
try {
|
|
9007
|
-
const urlObj = new URL(req.url || '/', `http://${LOOPBACK_HOST}:${PORT}`);
|
|
9008
|
-
return urlObj.searchParams.get('stream') === '1';
|
|
9009
|
-
}
|
|
9010
|
-
catch (_error) {
|
|
9011
|
-
return false;
|
|
9012
|
-
}
|
|
9083
|
+
async function finishSseResponse(res) {
|
|
9084
|
+
const writer = sseResponseWriters.get(res);
|
|
9085
|
+
if (writer)
|
|
9086
|
+
await writer.finish();
|
|
9087
|
+
else
|
|
9088
|
+
res.end();
|
|
9013
9089
|
}
|
|
9014
9090
|
async function resolveNearestExistingAncestor(candidatePath) {
|
|
9015
9091
|
let current = path.resolve(candidatePath);
|
|
@@ -9317,7 +9393,8 @@ async function writeSidecarRuntimeManifest(finalPort) {
|
|
|
9317
9393
|
runtimeDataDir: RUNTIME_DATA_DIR,
|
|
9318
9394
|
generatedAt: new Date().toISOString(),
|
|
9319
9395
|
pid: process.pid,
|
|
9320
|
-
}, null, 2), 'utf8');
|
|
9396
|
+
}, null, 2), { encoding: 'utf8', mode: 0o600 });
|
|
9397
|
+
await fs.promises.chmod(SIDECAR_RUNTIME_MANIFEST, 0o600);
|
|
9321
9398
|
}
|
|
9322
9399
|
catch (error) {
|
|
9323
9400
|
warnDiagnostic('[Sidecar] Failed to write runtime manifest:', error);
|
|
@@ -9518,15 +9595,21 @@ function resolveKnowledgeWorkspaceTargetFromConversationRequest(requestPayload)
|
|
|
9518
9595
|
return activeTarget;
|
|
9519
9596
|
}
|
|
9520
9597
|
function buildKnowledgeDocumentPayloadFromFile(file) {
|
|
9521
|
-
const
|
|
9522
|
-
const sourcePath = `Knowledge_Base/${relativePath}`.replace(/\/{2,}/g, '/');
|
|
9598
|
+
const sourcePath = buildKnowledgeSourcePathFromFilePath(file.filepath);
|
|
9523
9599
|
const language = /[\u4e00-\u9fff]/.test(file.content) ? 'zh' : 'en';
|
|
9524
9600
|
return {
|
|
9525
9601
|
sourcePath,
|
|
9602
|
+
sourceUri: file.sourceUri,
|
|
9603
|
+
revision: file.revision,
|
|
9604
|
+
identityAliases: file.identityAliases,
|
|
9526
9605
|
content: file.content,
|
|
9527
9606
|
language,
|
|
9528
9607
|
};
|
|
9529
9608
|
}
|
|
9609
|
+
function buildKnowledgeSourcePathFromFilePath(filePath) {
|
|
9610
|
+
const relativePath = path.relative(KB_ROOT, filePath).replace(/\\/g, '/');
|
|
9611
|
+
return `Knowledge_Base/${relativePath}`.replace(/\/{2,}/g, '/');
|
|
9612
|
+
}
|
|
9530
9613
|
async function collectMarkdownFilePaths(targetPath) {
|
|
9531
9614
|
const collected = [];
|
|
9532
9615
|
const scan = async (currentPath) => {
|
|
@@ -9554,26 +9637,7 @@ function normalizeKnowledgeTargetLookupQuery(value) {
|
|
|
9554
9637
|
.toLowerCase();
|
|
9555
9638
|
}
|
|
9556
9639
|
function deriveKnowledgeTargetLookupQueries(query) {
|
|
9557
|
-
|
|
9558
|
-
if (!normalized) {
|
|
9559
|
-
return [];
|
|
9560
|
-
}
|
|
9561
|
-
const candidates = new Set([normalized]);
|
|
9562
|
-
const stripped = normalized
|
|
9563
|
-
.replace(/^(what is|what are|define|explain|tell me about|what's)\s+/i, '')
|
|
9564
|
-
.replace(/^(什么是|解释一下|介绍一下|请解释|请介绍)\s*/i, '')
|
|
9565
|
-
.trim();
|
|
9566
|
-
if (stripped) {
|
|
9567
|
-
candidates.add(stripped);
|
|
9568
|
-
candidates.add(stripped.replace(/\s+/g, ''));
|
|
9569
|
-
}
|
|
9570
|
-
if (normalized.includes('water glass') || stripped === 'waterglass') {
|
|
9571
|
-
candidates.add('water glass');
|
|
9572
|
-
candidates.add('waterglass');
|
|
9573
|
-
candidates.add('水玻璃');
|
|
9574
|
-
candidates.add('水杯');
|
|
9575
|
-
}
|
|
9576
|
-
return Array.from(candidates.values()).filter(Boolean);
|
|
9640
|
+
return (0, workspaceHydration_1.deriveKnowledgeTargetLookupQueries)(query);
|
|
9577
9641
|
}
|
|
9578
9642
|
async function findKnowledgeFilesByTitleLikeQueries(targetPath, titleLikeQueries) {
|
|
9579
9643
|
if (titleLikeQueries.length <= 0) {
|
|
@@ -9588,16 +9652,50 @@ async function findKnowledgeFilesByTitleLikeQueries(targetPath, titleLikeQueries
|
|
|
9588
9652
|
if (exactMatches.length > 0) {
|
|
9589
9653
|
return exactMatches;
|
|
9590
9654
|
}
|
|
9591
|
-
|
|
9655
|
+
const fuzzyBasenameMatches = markdownFiles.filter((filePath) => {
|
|
9592
9656
|
const normalizedBaseName = normalizeKnowledgeTargetLookupQuery(path.basename(filePath, path.extname(filePath)));
|
|
9593
9657
|
return normalizedQueries.some((query) => normalizedBaseName.includes(query));
|
|
9594
9658
|
});
|
|
9659
|
+
if (fuzzyBasenameMatches.length > 0) {
|
|
9660
|
+
return fuzzyBasenameMatches;
|
|
9661
|
+
}
|
|
9662
|
+
const previewMatches = [];
|
|
9663
|
+
for (const filePath of markdownFiles) {
|
|
9664
|
+
const preview = await readMarkdownTitlePreview(filePath);
|
|
9665
|
+
if ((0, workspaceHydration_1.markdownPreviewMatchesTitleLikeQueries)({
|
|
9666
|
+
sourcePath: buildKnowledgeSourcePathFromFilePath(filePath),
|
|
9667
|
+
preview,
|
|
9668
|
+
titleLikeQueries: normalizedQueries,
|
|
9669
|
+
})) {
|
|
9670
|
+
previewMatches.push(filePath);
|
|
9671
|
+
}
|
|
9672
|
+
}
|
|
9673
|
+
return previewMatches;
|
|
9674
|
+
}
|
|
9675
|
+
async function readMarkdownTitlePreview(filePath) {
|
|
9676
|
+
const handle = await fs.promises.open(filePath, 'r');
|
|
9677
|
+
try {
|
|
9678
|
+
const buffer = Buffer.allocUnsafe(KNOWLEDGE_WORKSPACE_TITLE_PREVIEW_BYTES);
|
|
9679
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
9680
|
+
return buffer.subarray(0, bytesRead).toString('utf8');
|
|
9681
|
+
}
|
|
9682
|
+
finally {
|
|
9683
|
+
await handle.close();
|
|
9684
|
+
}
|
|
9595
9685
|
}
|
|
9596
9686
|
async function buildKnowledgeDocumentPayloadsFromPaths(filePaths) {
|
|
9597
9687
|
const documents = [];
|
|
9598
9688
|
for (const filePath of filePaths) {
|
|
9599
|
-
const
|
|
9600
|
-
|
|
9689
|
+
const execution = agentConversationExecution_1.AgentConversationExecution.current();
|
|
9690
|
+
const content = execution ? await execution.readSourceFile(filePath) : await fs.promises.readFile(filePath, 'utf8');
|
|
9691
|
+
if (content === null)
|
|
9692
|
+
continue;
|
|
9693
|
+
const identity = (0, ResourceIdentity_1.createResourceIdentity)(path.relative(KB_ROOT, filePath).replace(/\\/g, '/'), path.basename(filePath, path.extname(filePath)), content);
|
|
9694
|
+
documents.push(buildKnowledgeDocumentPayloadFromFile({
|
|
9695
|
+
filepath: filePath,
|
|
9696
|
+
content,
|
|
9697
|
+
...identity,
|
|
9698
|
+
}));
|
|
9601
9699
|
}
|
|
9602
9700
|
return documents;
|
|
9603
9701
|
}
|
|
@@ -9606,6 +9704,7 @@ async function syncLearningWorkspaceForDocumentPaths(params) {
|
|
|
9606
9704
|
const result = await knowledgeLearningPlatform.ingestKnowledge({
|
|
9607
9705
|
incremental: true,
|
|
9608
9706
|
documents,
|
|
9707
|
+
deletedDocuments: params.deletedDocuments,
|
|
9609
9708
|
ingestedAt: new Date().toISOString(),
|
|
9610
9709
|
relationRecomputeMode: 'incremental',
|
|
9611
9710
|
});
|
|
@@ -9613,6 +9712,7 @@ async function syncLearningWorkspaceForDocumentPaths(params) {
|
|
|
9613
9712
|
target: params.target,
|
|
9614
9713
|
reason: params.reason,
|
|
9615
9714
|
documentCount: documents.length,
|
|
9715
|
+
deletedDocumentCount: params.deletedDocuments?.length || 0,
|
|
9616
9716
|
changedDocuments: result.summary.changedDocuments,
|
|
9617
9717
|
activeAtoms: result.summary.activeAtoms,
|
|
9618
9718
|
});
|
|
@@ -9666,15 +9766,8 @@ async function ensureLearningWorkspaceHydratedForConversationRequest(requestPayl
|
|
|
9666
9766
|
const readinessBeforeSync = await knowledgeLearningPlatform.inspectKnowledgeWorkspaceRequest({
|
|
9667
9767
|
query: requestPayload.message,
|
|
9668
9768
|
scope: derivedScope,
|
|
9769
|
+
includeSourceInventory: true,
|
|
9669
9770
|
});
|
|
9670
|
-
if (readinessBeforeSync.readiness.status === 'ready') {
|
|
9671
|
-
return {
|
|
9672
|
-
hydrated: false,
|
|
9673
|
-
target,
|
|
9674
|
-
scope: derivedScope,
|
|
9675
|
-
reason: 'existing_store_ready',
|
|
9676
|
-
};
|
|
9677
|
-
}
|
|
9678
9771
|
if (!target || target === 'ALL_FOLDERS') {
|
|
9679
9772
|
const explicitAllFoldersTarget = String(requestPayload.activeTarget || '').trim().toUpperCase() === 'ALL_FOLDERS';
|
|
9680
9773
|
if (explicitAllFoldersTarget) {
|
|
@@ -9693,26 +9786,91 @@ async function ensureLearningWorkspaceHydratedForConversationRequest(requestPayl
|
|
|
9693
9786
|
reason: readinessBeforeSync.readiness.status,
|
|
9694
9787
|
};
|
|
9695
9788
|
}
|
|
9696
|
-
const
|
|
9789
|
+
const availableTargets = await collectAvailableTargetsFromPath(KB_ROOT);
|
|
9790
|
+
const resolvedTarget = availableTargets.find((entry) => entry.toLowerCase() === target.toLowerCase()) || target;
|
|
9791
|
+
const targetPath = path.join(KB_ROOT, resolvedTarget);
|
|
9792
|
+
const targetPathExists = await pathExists(targetPath);
|
|
9793
|
+
const targetFiles = await collectMarkdownFilePaths(targetPath);
|
|
9794
|
+
const diskSourcePaths = targetFiles.map(buildKnowledgeSourcePathFromFilePath);
|
|
9795
|
+
const indexedSourcePaths = readinessBeforeSync.sourceInventory?.sourcePaths || [];
|
|
9796
|
+
const inventoryDiff = (0, workspaceHydration_1.buildKnowledgeSourceInventoryDiff)({
|
|
9797
|
+
diskSourcePaths,
|
|
9798
|
+
indexedSourcePaths,
|
|
9799
|
+
});
|
|
9800
|
+
const indexedItemsByPath = new Map((readinessBeforeSync.sourceInventory?.items || []).map((item) => [
|
|
9801
|
+
(0, workspaceHydration_1.normalizeKnowledgeSourcePath)(item.sourcePath),
|
|
9802
|
+
item,
|
|
9803
|
+
]));
|
|
9804
|
+
// A missing target directory is an unavailable filesystem boundary, not an
|
|
9805
|
+
// authoritative empty corpus. API/mobile callers may legitimately ingest a
|
|
9806
|
+
// document before its host materializes the directory, and deleting those
|
|
9807
|
+
// indexed projections would make the next conversation observe an empty store.
|
|
9808
|
+
const deletedDocuments = targetPathExists
|
|
9809
|
+
? inventoryDiff.removedSourcePaths
|
|
9810
|
+
.map((sourcePath) => indexedItemsByPath.get((0, workspaceHydration_1.normalizeKnowledgeSourcePath)(sourcePath)))
|
|
9811
|
+
.filter((item) => Boolean(item))
|
|
9812
|
+
.map((item) => ({
|
|
9813
|
+
documentId: item.documentId,
|
|
9814
|
+
sourcePath: item.sourcePath,
|
|
9815
|
+
}))
|
|
9816
|
+
: [];
|
|
9817
|
+
if (readinessBeforeSync.readiness.status === 'ready'
|
|
9818
|
+
&& inventoryDiff.addedSourcePaths.length <= 0
|
|
9819
|
+
&& deletedDocuments.length <= 0) {
|
|
9820
|
+
return {
|
|
9821
|
+
hydrated: false,
|
|
9822
|
+
target: resolvedTarget,
|
|
9823
|
+
scope: derivedScope,
|
|
9824
|
+
reason: 'existing_store_ready',
|
|
9825
|
+
};
|
|
9826
|
+
}
|
|
9697
9827
|
const titleLikeQueries = deriveKnowledgeTargetLookupQueries(String(requestPayload.message || ''));
|
|
9698
9828
|
const candidateFiles = await findKnowledgeFilesByTitleLikeQueries(targetPath, titleLikeQueries);
|
|
9699
9829
|
if (candidateFiles.length > 0) {
|
|
9700
9830
|
await syncLearningWorkspaceForDocumentPaths({
|
|
9701
|
-
target,
|
|
9702
|
-
filePaths: candidateFiles,
|
|
9831
|
+
target: resolvedTarget,
|
|
9832
|
+
filePaths: candidateFiles.slice(0, KNOWLEDGE_WORKSPACE_MAX_SELECTIVE_HYDRATION_FILES),
|
|
9833
|
+
deletedDocuments,
|
|
9703
9834
|
reason: 'conversation_selective_title_hydration',
|
|
9704
9835
|
});
|
|
9705
9836
|
return {
|
|
9706
9837
|
hydrated: true,
|
|
9707
|
-
target,
|
|
9838
|
+
target: resolvedTarget,
|
|
9708
9839
|
scope: derivedScope,
|
|
9709
9840
|
reason: 'conversation_selective_title_hydration',
|
|
9710
9841
|
};
|
|
9711
9842
|
}
|
|
9712
|
-
|
|
9843
|
+
if (deletedDocuments.length > 0 && readinessBeforeSync.readiness.status === 'ready') {
|
|
9844
|
+
await syncLearningWorkspaceForDocumentPaths({
|
|
9845
|
+
target: resolvedTarget,
|
|
9846
|
+
filePaths: [],
|
|
9847
|
+
deletedDocuments,
|
|
9848
|
+
reason: 'conversation_stale_source_reconciliation',
|
|
9849
|
+
});
|
|
9850
|
+
return {
|
|
9851
|
+
hydrated: true,
|
|
9852
|
+
target: resolvedTarget,
|
|
9853
|
+
scope: derivedScope,
|
|
9854
|
+
reason: 'conversation_stale_source_reconciliation',
|
|
9855
|
+
};
|
|
9856
|
+
}
|
|
9857
|
+
if (targetFiles.length > KNOWLEDGE_WORKSPACE_LARGE_TARGET_FILE_THRESHOLD) {
|
|
9858
|
+
return {
|
|
9859
|
+
hydrated: false,
|
|
9860
|
+
target: resolvedTarget,
|
|
9861
|
+
scope: derivedScope,
|
|
9862
|
+
reason: 'conversation_hydration_deferred_large_target',
|
|
9863
|
+
};
|
|
9864
|
+
}
|
|
9865
|
+
await syncLearningWorkspaceForDocumentPaths({
|
|
9866
|
+
target: resolvedTarget,
|
|
9867
|
+
filePaths: targetFiles,
|
|
9868
|
+
deletedDocuments,
|
|
9869
|
+
reason: 'conversation_auto_hydration',
|
|
9870
|
+
});
|
|
9713
9871
|
return {
|
|
9714
9872
|
hydrated: true,
|
|
9715
|
-
target,
|
|
9873
|
+
target: resolvedTarget,
|
|
9716
9874
|
scope: derivedScope,
|
|
9717
9875
|
reason: 'conversation_auto_hydration',
|
|
9718
9876
|
};
|
|
@@ -10130,15 +10288,12 @@ const startServer = async (options = {}) => {
|
|
|
10130
10288
|
}
|
|
10131
10289
|
}
|
|
10132
10290
|
}
|
|
10133
|
-
|
|
10134
|
-
|
|
10135
|
-
|
|
10136
|
-
|
|
10137
|
-
|
|
10138
|
-
|
|
10139
|
-
// full registry init and depend on the inline fallback. Once all tests use
|
|
10140
|
-
// the registry, this becomes default and inline handlers can be deleted.
|
|
10141
|
-
const STRICT_REGISTRY = process.env.NOTE_CONNECTION_STRICT_REGISTRY === '1';
|
|
10291
|
+
const ROUTE_DISPATCH_MODE = String(process.env.NOTE_CONNECTION_ROUTE_DISPATCH_MODE || 'registry')
|
|
10292
|
+
.trim()
|
|
10293
|
+
.toLowerCase() === 'legacy'
|
|
10294
|
+
? 'legacy'
|
|
10295
|
+
: 'registry';
|
|
10296
|
+
const USE_REGISTRY_DISPATCH = ROUTE_DISPATCH_MODE === 'registry';
|
|
10142
10297
|
// --- Route Registry (modular dispatch for extracted route groups) ---
|
|
10143
10298
|
const runtimeRunbookOps = (0, runtimeRunbookRouteOps_1.createRuntimeRunbookRouteOps)({
|
|
10144
10299
|
buildRuntimePayload: buildKnowledgeRuntimePayload,
|
|
@@ -10184,6 +10339,24 @@ const startServer = async (options = {}) => {
|
|
|
10184
10339
|
memoryPolicyManager,
|
|
10185
10340
|
notemdService,
|
|
10186
10341
|
loadNotemdSettings,
|
|
10342
|
+
notemdHttp: { readJsonBody, writeBodyParseErrorResponse, writeApiErrorResponse, resolvePathWithinKnowledgeBase, writeSseEvent, finishSseResponse, llmClient: notemdLlmClient },
|
|
10343
|
+
executeQueryBackendConfigUpdate: async (payload) => {
|
|
10344
|
+
const requestPayload = normalizeQueryBackendConfigRequestPayload(payload);
|
|
10345
|
+
const result = await knowledgeLearningPlatform.updateQueryBackendConfig(requestPayload);
|
|
10346
|
+
ACTIVE_KNOWLEDGE_QUERY_BACKEND = result.configuredBackend;
|
|
10347
|
+
const baseDiagnostics = knowledgeLearningPlatform.getQueryBackendDiagnostics();
|
|
10348
|
+
const graphvizRuntimeAvailability = await getGraphvizDotRuntimeAvailability();
|
|
10349
|
+
const diagnostics = enrichQueryBackendDiagnosticsWithRendererRuntime(baseDiagnostics, graphvizRuntimeAvailability);
|
|
10350
|
+
return {
|
|
10351
|
+
result,
|
|
10352
|
+
diagnostics,
|
|
10353
|
+
configuredVectorAccelerationProvider: QUERY_VECTOR_ACCELERATION_PROVIDER,
|
|
10354
|
+
configuredVectorAccelerationFailureMode: QUERY_VECTOR_ACCELERATION_FAILURE_MODE,
|
|
10355
|
+
configuredVectorAccelerationRepresentationStrict: QUERY_VECTOR_ACCELERATION_REPRESENTATION_STRICT_ENABLED,
|
|
10356
|
+
queryVectorAnnPrefilterEnabled: QUERY_VECTOR_ANN_PREFILTER_ENABLED,
|
|
10357
|
+
rolloutProfile: buildKnowledgeRuntimeRolloutProfile(),
|
|
10358
|
+
};
|
|
10359
|
+
},
|
|
10187
10360
|
persistNotemdSettings,
|
|
10188
10361
|
loadFrontendSettings,
|
|
10189
10362
|
markdownGateway,
|
|
@@ -10199,6 +10372,63 @@ const startServer = async (options = {}) => {
|
|
|
10199
10372
|
runtimeRunbookOps,
|
|
10200
10373
|
getPathBridge: () => pathBridge,
|
|
10201
10374
|
};
|
|
10375
|
+
// These routes keep their complete state/I/O operation in this server.
|
|
10376
|
+
const serverOwnedApiPaths = new Set([
|
|
10377
|
+
'/api/available-targets',
|
|
10378
|
+
'/api/build',
|
|
10379
|
+
'/api/check-cache',
|
|
10380
|
+
'/api/clipboard/image',
|
|
10381
|
+
'/api/clipboard/image-binary',
|
|
10382
|
+
'/api/content',
|
|
10383
|
+
'/api/folders',
|
|
10384
|
+
'/api/frontend/settings',
|
|
10385
|
+
'/api/kb-path',
|
|
10386
|
+
'/api/knowledge/conversation',
|
|
10387
|
+
'/api/knowledge/conversation-memory/add',
|
|
10388
|
+
'/api/knowledge/conversation-memory/delete',
|
|
10389
|
+
'/api/knowledge/conversation-memory/feedback',
|
|
10390
|
+
'/api/knowledge/conversation-memory/search',
|
|
10391
|
+
'/api/knowledge/conversation/turn-cache/diagnostics',
|
|
10392
|
+
'/api/knowledge/conversation/turn-cache/diagnostics/trend',
|
|
10393
|
+
'/api/knowledge/conversation/turn-cache/diagnostics/trend/export',
|
|
10394
|
+
'/api/knowledge/conversation/turn-cache/diagnostics/trend/index',
|
|
10395
|
+
'/api/knowledge/ingest/guardrails/evaluate',
|
|
10396
|
+
'/api/knowledge/mastery/diagnose',
|
|
10397
|
+
'/api/knowledge/mastery/misconceptions',
|
|
10398
|
+
'/api/knowledge/memory/policy',
|
|
10399
|
+
'/api/knowledge/path',
|
|
10400
|
+
'/api/knowledge/quality/evaluate',
|
|
10401
|
+
'/api/knowledge/quality/snapshot',
|
|
10402
|
+
'/api/knowledge/query',
|
|
10403
|
+
'/api/knowledge/query-backend-config',
|
|
10404
|
+
'/api/knowledge/query/compare-backends',
|
|
10405
|
+
'/api/knowledge/runtime-capability-runbook/remediation-event',
|
|
10406
|
+
'/api/knowledge/runtime-capability-runbook/remediation-event/replay',
|
|
10407
|
+
'/api/knowledge/runtime-capability-runbook/remediation-event/replay-schedule',
|
|
10408
|
+
'/api/knowledge/runtime-capability-runbook/remediation-event/replay-schedule/tick',
|
|
10409
|
+
'/api/knowledge/session/action',
|
|
10410
|
+
'/api/knowledge/session/execute',
|
|
10411
|
+
'/api/knowledge/session/history',
|
|
10412
|
+
'/api/knowledge/session/orchestration/config',
|
|
10413
|
+
'/api/knowledge/session/plan',
|
|
10414
|
+
'/api/knowledge/session/plan/evaluate',
|
|
10415
|
+
'/api/knowledge/staleness/rebuild',
|
|
10416
|
+
'/api/knowledge/store/reload',
|
|
10417
|
+
'/api/knowledge/tutor/action',
|
|
10418
|
+
'/api/markdown/chunk',
|
|
10419
|
+
'/api/markdown/index',
|
|
10420
|
+
'/api/markdown/resolve-node',
|
|
10421
|
+
'/api/markdown/resolve-wiki',
|
|
10422
|
+
'/api/path-mode/settings',
|
|
10423
|
+
'/api/render/graphviz',
|
|
10424
|
+
'/api/render/math',
|
|
10425
|
+
'/api/render/mermaid',
|
|
10426
|
+
'/api/restore-cache',
|
|
10427
|
+
'/api/runtime-diagnostics',
|
|
10428
|
+
'/api/runtime-request-trace',
|
|
10429
|
+
]);
|
|
10430
|
+
let intentionalInlineHits = 0;
|
|
10431
|
+
const inlineFallbacksByFamily = {};
|
|
10202
10432
|
// Route migration tracking
|
|
10203
10433
|
let routeRegistryHits = 0;
|
|
10204
10434
|
let routeInlineFallbacks = 0;
|
|
@@ -10217,9 +10447,8 @@ const startServer = async (options = {}) => {
|
|
|
10217
10447
|
}
|
|
10218
10448
|
const routeMigrationStats = {
|
|
10219
10449
|
totalModularRoutes: allRoutes.length,
|
|
10220
|
-
totalInlineRoutes:
|
|
10221
|
-
|
|
10222
|
-
// Pending safe deletion after integration test; set NOTE_CONNECTION_STRICT_REGISTRY=1 to skip inline.
|
|
10450
|
+
totalInlineRoutes: [...serverOwnedApiPaths].filter(pathname => !allRoutes.some(route => route.path === pathname)).length,
|
|
10451
|
+
dispatchMode: ROUTE_DISPATCH_MODE,
|
|
10223
10452
|
registryHits: () => routeRegistryHits,
|
|
10224
10453
|
inlineFallbacks: () => routeInlineFallbacks,
|
|
10225
10454
|
registryHitRate: () => {
|
|
@@ -10227,9 +10456,9 @@ const startServer = async (options = {}) => {
|
|
|
10227
10456
|
return total === 0 ? '0.0%' : (routeRegistryHits / total * 100).toFixed(1) + '%';
|
|
10228
10457
|
},
|
|
10229
10458
|
migrationProgress: () => {
|
|
10230
|
-
const
|
|
10231
|
-
const
|
|
10232
|
-
return (
|
|
10459
|
+
const registeredPaths = new Set(allRoutes.map(route => route.path));
|
|
10460
|
+
const supportedPaths = new Set([...registeredPaths, ...serverOwnedApiPaths]);
|
|
10461
|
+
return (registeredPaths.size / supportedPaths.size * 100).toFixed(1) + '%';
|
|
10233
10462
|
},
|
|
10234
10463
|
};
|
|
10235
10464
|
const server = http.createServer(async (req, res) => {
|
|
@@ -10285,113 +10514,57 @@ const startServer = async (options = {}) => {
|
|
|
10285
10514
|
res.end(JSON.stringify({ error: 'Unauthorized sidecar request.' }));
|
|
10286
10515
|
return;
|
|
10287
10516
|
}
|
|
10288
|
-
|
|
10289
|
-
const
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
await
|
|
10295
|
-
return;
|
|
10517
|
+
const methodMap = routeMap.get(requestMethod);
|
|
10518
|
+
const registeredRoute = methodMap?.get(requestPath)
|
|
10519
|
+
|| prefixRoutes.find(route => requestPath.startsWith(route.path) && route.method === requestMethod);
|
|
10520
|
+
if (registeredRoute && (USE_REGISTRY_DISPATCH || requestPath.startsWith('/api/notemd/'))) {
|
|
10521
|
+
routeRegistryHits++;
|
|
10522
|
+
try {
|
|
10523
|
+
await registeredRoute.handler(req, res, routeContext);
|
|
10296
10524
|
}
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
routeRegistryHits++;
|
|
10300
|
-
await prefixRoute.handler(req, res, routeContext);
|
|
10301
|
-
return;
|
|
10302
|
-
}
|
|
10525
|
+
catch (error) {
|
|
10526
|
+
writeApiErrorResponse(res, error, { context: requestMethod + ' ' + requestPath, requestId });
|
|
10303
10527
|
}
|
|
10528
|
+
return;
|
|
10529
|
+
}
|
|
10530
|
+
if (requestPath.startsWith('/api/notemd/')) {
|
|
10531
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
10532
|
+
res.end(JSON.stringify({ success: false, error: 'NoteMD route not found' }));
|
|
10533
|
+
return;
|
|
10304
10534
|
}
|
|
10305
|
-
if (
|
|
10535
|
+
if (serverOwnedApiPaths.has(requestPath))
|
|
10536
|
+
intentionalInlineHits++;
|
|
10537
|
+
else if (requestPath.startsWith('/api/')) {
|
|
10306
10538
|
routeInlineFallbacks++;
|
|
10539
|
+
const family = requestPath.split('/')[2] || 'unknown';
|
|
10540
|
+
inlineFallbacksByFamily[family] = (inlineFallbacksByFamily[family] || 0) + 1;
|
|
10307
10541
|
}
|
|
10308
|
-
//
|
|
10309
|
-
// Route distribution:
|
|
10310
|
-
// Registry-covered: ~80 routes (knowledge:36G+28P, notemd:2G+16P, + data/render)
|
|
10311
|
-
// Terminal inline: ~7 routes intentionally kept inline:
|
|
10312
|
-
// - runtime-diagnostics, runtime-request-trace (meta: report server state)
|
|
10313
|
-
// - static file serving (requires FRONTEND_DIR + MIME resolution)
|
|
10314
|
-
// - graph asset serving (requires cliOptions + generated asset paths)
|
|
10315
|
-
// - generated asset resolution (requires CLI build output paths)
|
|
10316
|
-
// These are terminal — they require deep server state that can't be
|
|
10317
|
-
// cleanly exposed through ServerContext without circular dependencies.
|
|
10318
|
-
// Total: ~105 route patterns
|
|
10319
|
-
//
|
|
10320
|
-
// The registry dispatch (above) intercepts covered routes first.
|
|
10321
|
-
// Tag [REGISTRY_COVERED] marks routes already handled by registry.
|
|
10322
|
-
// Migration metrics: GET /api/runtime-diagnostics → routeMigration
|
|
10542
|
+
// Server-owned operations retain their validation, state and native I/O boundaries.
|
|
10323
10543
|
if (req.method === 'GET') {
|
|
10324
10544
|
const getPathname = getRawRequestPathname(req.url);
|
|
10325
|
-
|
|
10326
|
-
|
|
10327
|
-
|
|
10328
|
-
res.writeHead(501, { 'Content-Type': 'application/json' });
|
|
10329
|
-
res.end(JSON.stringify({ error: 'strict_registry: route must be handled by modular registry', path: getPathname }));
|
|
10330
|
-
routeInlineFallbacks++;
|
|
10545
|
+
if (getPathname === '/api/knowledge/conversation/turn-cache/diagnostics') {
|
|
10546
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
10547
|
+
res.end(JSON.stringify({ success: true, result: getAgentConversationTurnCacheDiagnostics() }));
|
|
10331
10548
|
return;
|
|
10332
10549
|
}
|
|
10333
|
-
if (getPathname === '/api/
|
|
10334
|
-
|
|
10335
|
-
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
settings,
|
|
10341
|
-
operationSummary: {
|
|
10342
|
-
total: NOTEMD_ACTIVE_OPERATIONS.size,
|
|
10343
|
-
running: activeOperationCount,
|
|
10344
|
-
},
|
|
10345
|
-
}));
|
|
10346
|
-
}
|
|
10347
|
-
catch (error) {
|
|
10348
|
-
writeApiErrorResponse(res, error, {
|
|
10349
|
-
context: 'API:GET /api/notemd/settings',
|
|
10350
|
-
requestId,
|
|
10351
|
-
});
|
|
10352
|
-
}
|
|
10550
|
+
if (getPathname === '/api/knowledge/conversation/turn-cache/diagnostics/trend') {
|
|
10551
|
+
getAgentConversationTurnCacheDiagnostics();
|
|
10552
|
+
const request = normalizeAgentConversationTurnCacheAlertTrendRequestFromQuery(new URL(req.url, `http://${LOOPBACK_HOST}`).searchParams);
|
|
10553
|
+
const result = queryAgentConversationTurnCacheAlertTrend(request);
|
|
10554
|
+
appendRuntimeRunbookVerificationHistoryFromConversationTurnCacheAlertTrend(result);
|
|
10555
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
10556
|
+
res.end(JSON.stringify({ success: true, result }));
|
|
10353
10557
|
return;
|
|
10354
10558
|
}
|
|
10355
|
-
if (getPathname === '/api/
|
|
10356
|
-
|
|
10357
|
-
|
|
10358
|
-
const persistTemplates = ['1', 'true', 'yes'].includes(String(urlObj.searchParams.get('persist') || '').trim().toLowerCase());
|
|
10359
|
-
let persistence = { configPath: (0, AppConfigToml_1.resolveAppConfigPath)(), persisted: false };
|
|
10360
|
-
if (persistTemplates) {
|
|
10361
|
-
persistence = await ensureNotemdProviderTemplatesPersisted();
|
|
10362
|
-
}
|
|
10363
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
10364
|
-
res.end(JSON.stringify({
|
|
10365
|
-
success: true,
|
|
10366
|
-
templates: providerTemplates_1.NOTEMD_PROVIDER_TEMPLATES,
|
|
10367
|
-
configPath: persistence.configPath,
|
|
10368
|
-
persisted: persistence.persisted,
|
|
10369
|
-
}));
|
|
10370
|
-
}
|
|
10371
|
-
catch (error) {
|
|
10372
|
-
writeApiErrorResponse(res, error, {
|
|
10373
|
-
context: 'API:GET /api/notemd/provider-templates',
|
|
10374
|
-
requestId,
|
|
10375
|
-
});
|
|
10376
|
-
}
|
|
10559
|
+
if (getPathname === '/api/knowledge/conversation/turn-cache/diagnostics/trend/index') {
|
|
10560
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
10561
|
+
res.end(JSON.stringify({ success: true, result: snapshotAgentConversationTurnCacheAlertHistoryStorageIndex() }));
|
|
10377
10562
|
return;
|
|
10378
10563
|
}
|
|
10379
|
-
if (getPathname === '/api/
|
|
10380
|
-
|
|
10381
|
-
|
|
10382
|
-
|
|
10383
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
10384
|
-
res.end(JSON.stringify({
|
|
10385
|
-
success: true,
|
|
10386
|
-
workspace,
|
|
10387
|
-
}));
|
|
10388
|
-
}
|
|
10389
|
-
catch (error) {
|
|
10390
|
-
writeApiErrorResponse(res, error, {
|
|
10391
|
-
context: 'API:GET /api/notemd/workspace',
|
|
10392
|
-
requestId,
|
|
10393
|
-
});
|
|
10394
|
-
}
|
|
10564
|
+
if (getPathname === '/api/knowledge/conversation/turn-cache/diagnostics/trend/export') {
|
|
10565
|
+
const request = normalizeAgentConversationTurnCacheAlertTrendRequestFromQuery(new URL(req.url, `http://${LOOPBACK_HOST}`).searchParams);
|
|
10566
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
10567
|
+
res.end(JSON.stringify({ success: true, result: queryAgentConversationTurnCacheAlertTrendExport(request) }));
|
|
10395
10568
|
return;
|
|
10396
10569
|
}
|
|
10397
10570
|
if (getPathname === '/api/path-mode/settings') {
|
|
@@ -10467,6 +10640,9 @@ const startServer = async (options = {}) => {
|
|
|
10467
10640
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
10468
10641
|
res.end(JSON.stringify({
|
|
10469
10642
|
runtime: {
|
|
10643
|
+
nodeVersion: process.version,
|
|
10644
|
+
platform: process.platform,
|
|
10645
|
+
arch: process.arch,
|
|
10470
10646
|
host: LOOPBACK_HOST,
|
|
10471
10647
|
port: runtimePort,
|
|
10472
10648
|
bridgePort: effectivePathBridgePort,
|
|
@@ -10515,10 +10691,13 @@ const startServer = async (options = {}) => {
|
|
|
10515
10691
|
totalModularRoutes: routeMigrationStats.totalModularRoutes,
|
|
10516
10692
|
registryHits: routeMigrationStats.registryHits(),
|
|
10517
10693
|
inlineFallbacks: routeMigrationStats.inlineFallbacks(),
|
|
10694
|
+
intentionalInlineHits,
|
|
10695
|
+
inlineFallbacksByFamily: { ...inlineFallbacksByFamily },
|
|
10518
10696
|
registryHitRate: routeMigrationStats.registryHitRate(),
|
|
10519
10697
|
migrationProgress: routeMigrationStats.migrationProgress(),
|
|
10520
10698
|
modularRoutes: routeMigrationStats.totalModularRoutes,
|
|
10521
10699
|
inlineOnlyRoutes: routeMigrationStats.totalInlineRoutes,
|
|
10700
|
+
dispatchMode: routeMigrationStats.dispatchMode,
|
|
10522
10701
|
},
|
|
10523
10702
|
domains: {
|
|
10524
10703
|
ingest: knowledgeIngestor.getDiagnostics(),
|
|
@@ -11006,24 +11185,12 @@ const startServer = async (options = {}) => {
|
|
|
11006
11185
|
if (postPathname === '/api/knowledge/query-backend-config') {
|
|
11007
11186
|
try {
|
|
11008
11187
|
const payload = await readJsonBody(req);
|
|
11009
|
-
const
|
|
11010
|
-
|
|
11011
|
-
|
|
11012
|
-
|
|
11013
|
-
const graphvizRuntimeAvailability = await getGraphvizDotRuntimeAvailability();
|
|
11014
|
-
const diagnostics = enrichQueryBackendDiagnosticsWithRendererRuntime(baseDiagnostics, graphvizRuntimeAvailability);
|
|
11015
|
-
const rolloutProfile = buildKnowledgeRuntimeRolloutProfile();
|
|
11188
|
+
const responsePayload = await routeContext.executeQueryBackendConfigUpdate?.(payload);
|
|
11189
|
+
if (!responsePayload) {
|
|
11190
|
+
throw new Error('Query backend config operation is unavailable.');
|
|
11191
|
+
}
|
|
11016
11192
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
11017
|
-
res.end(JSON.stringify({
|
|
11018
|
-
success: true,
|
|
11019
|
-
result,
|
|
11020
|
-
diagnostics,
|
|
11021
|
-
configuredVectorAccelerationProvider: QUERY_VECTOR_ACCELERATION_PROVIDER,
|
|
11022
|
-
configuredVectorAccelerationFailureMode: QUERY_VECTOR_ACCELERATION_FAILURE_MODE,
|
|
11023
|
-
configuredVectorAccelerationRepresentationStrict: QUERY_VECTOR_ACCELERATION_REPRESENTATION_STRICT_ENABLED,
|
|
11024
|
-
queryVectorAnnPrefilterEnabled: QUERY_VECTOR_ANN_PREFILTER_ENABLED,
|
|
11025
|
-
rolloutProfile,
|
|
11026
|
-
}));
|
|
11193
|
+
res.end(JSON.stringify({ success: true, ...responsePayload }));
|
|
11027
11194
|
}
|
|
11028
11195
|
catch (error) {
|
|
11029
11196
|
writeApiErrorResponse(res, error, {
|
|
@@ -11069,13 +11236,15 @@ const startServer = async (options = {}) => {
|
|
|
11069
11236
|
return;
|
|
11070
11237
|
}
|
|
11071
11238
|
if (postPathname === '/api/knowledge/conversation') {
|
|
11239
|
+
let releaseClient;
|
|
11072
11240
|
try {
|
|
11073
11241
|
const payload = await readJsonBody(req);
|
|
11074
|
-
const requestPayload = normalizeAgentConversationRequestPayload(payload);
|
|
11242
|
+
const requestPayload = (0, requestNormalization_1.normalizeAgentConversationRequestPayload)(payload);
|
|
11075
11243
|
const streamRequested = requestAcceptsEventStream(req);
|
|
11076
11244
|
const turnId = resolveAgentConversationTurnId(req, requestId);
|
|
11077
11245
|
const requestFingerprint = buildAgentConversationRequestFingerprint(requestPayload);
|
|
11078
11246
|
const turnRecord = getOrCreateAgentConversationTurnCacheRecord(turnId, requestFingerprint);
|
|
11247
|
+
releaseClient = attachAgentConversationClient(turnRecord, req, res);
|
|
11079
11248
|
if (!streamRequested) {
|
|
11080
11249
|
const wasCompletedBeforeExecution = turnRecord.status === 'completed';
|
|
11081
11250
|
await ensureAgentConversationTurnExecution(turnRecord, requestPayload);
|
|
@@ -11086,7 +11255,10 @@ const startServer = async (options = {}) => {
|
|
|
11086
11255
|
res.setHeader('X-Agent-Conversation-Turn-Id', turnId);
|
|
11087
11256
|
res.setHeader('X-Agent-Conversation-Replay', wasCompletedBeforeExecution ? 'hit' : 'miss');
|
|
11088
11257
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
11089
|
-
|
|
11258
|
+
const responseResult = requestPayload.responseProfile === 'mobile_compact'
|
|
11259
|
+
? buildMobileAgentConversationResponse(turnRecord.result)
|
|
11260
|
+
: turnRecord.result;
|
|
11261
|
+
res.end((0, agentConversationSerialization_1.serializeAgentConversationHttpResponse)(responseResult).json);
|
|
11090
11262
|
return;
|
|
11091
11263
|
}
|
|
11092
11264
|
throwAgentConversationCachedFailure(turnRecord.failure);
|
|
@@ -11118,15 +11290,15 @@ const startServer = async (options = {}) => {
|
|
|
11118
11290
|
await new Promise((resolve) => setImmediate(resolve));
|
|
11119
11291
|
await ensureAgentConversationTurnExecution(turnRecord, requestPayload, {
|
|
11120
11292
|
emitLiveEvent: (event) => {
|
|
11121
|
-
writeSseEvent(res, event.type, event);
|
|
11293
|
+
writeSseEvent(res, event.type, projectAgentConversationTurnEvent(event, requestPayload));
|
|
11122
11294
|
},
|
|
11123
11295
|
});
|
|
11124
11296
|
}
|
|
11125
11297
|
else {
|
|
11126
11298
|
await ensureAgentConversationTurnExecution(turnRecord, requestPayload);
|
|
11127
|
-
replayAgentConversationTurnEvents(res, turnRecord);
|
|
11299
|
+
replayAgentConversationTurnEvents(res, turnRecord, requestPayload);
|
|
11128
11300
|
}
|
|
11129
|
-
res
|
|
11301
|
+
await finishSseResponse(res);
|
|
11130
11302
|
}
|
|
11131
11303
|
catch (error) {
|
|
11132
11304
|
writeApiErrorResponse(res, error, {
|
|
@@ -11135,6 +11307,9 @@ const startServer = async (options = {}) => {
|
|
|
11135
11307
|
enableValidationStatus: true,
|
|
11136
11308
|
});
|
|
11137
11309
|
}
|
|
11310
|
+
finally {
|
|
11311
|
+
releaseClient?.();
|
|
11312
|
+
}
|
|
11138
11313
|
return;
|
|
11139
11314
|
}
|
|
11140
11315
|
if (postPathname === '/api/knowledge/mastery/diagnose') {
|
|
@@ -11453,125 +11628,43 @@ const startServer = async (options = {}) => {
|
|
|
11453
11628
|
}
|
|
11454
11629
|
return;
|
|
11455
11630
|
}
|
|
11456
|
-
|
|
11457
|
-
// [REGISTRY_COVERED: routes/notemd.ts]
|
|
11458
|
-
if (STRICT_REGISTRY && postPathname.startsWith('/api/notemd/')) {
|
|
11459
|
-
res.writeHead(501, { 'Content-Type': 'application/json' });
|
|
11460
|
-
res.end(JSON.stringify({ error: 'strict_registry: route must be handled by modular registry', path: postPathname }));
|
|
11461
|
-
routeInlineFallbacks++;
|
|
11462
|
-
return;
|
|
11463
|
-
}
|
|
11464
|
-
if (postPathname === '/api/notemd/settings') {
|
|
11631
|
+
if (postPathname === '/api/path-mode/settings') {
|
|
11465
11632
|
try {
|
|
11466
11633
|
const payload = await readJsonBody(req);
|
|
11467
11634
|
const settingsCandidate = isObjectRecord(payload) && payload.settings !== undefined
|
|
11468
11635
|
? payload.settings
|
|
11469
11636
|
: payload;
|
|
11470
|
-
const settings = await
|
|
11637
|
+
const settings = await persistPathModeSettings(settingsCandidate);
|
|
11471
11638
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
11472
11639
|
res.end(JSON.stringify({ success: true, settings }));
|
|
11473
11640
|
}
|
|
11474
11641
|
catch (error) {
|
|
11475
11642
|
writeApiErrorResponse(res, error, {
|
|
11476
|
-
context: 'API:POST /api/
|
|
11643
|
+
context: 'API:POST /api/path-mode/settings',
|
|
11477
11644
|
requestId,
|
|
11478
11645
|
});
|
|
11479
11646
|
}
|
|
11480
11647
|
return;
|
|
11481
11648
|
}
|
|
11482
|
-
if (postPathname === '/api/
|
|
11649
|
+
if (postPathname === '/api/frontend/settings') {
|
|
11483
11650
|
try {
|
|
11484
11651
|
const payload = await readJsonBody(req);
|
|
11485
|
-
const
|
|
11486
|
-
?
|
|
11487
|
-
:
|
|
11488
|
-
|
|
11489
|
-
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
11490
|
-
res.end(JSON.stringify({ success: false, error: 'Missing templateId.' }));
|
|
11491
|
-
return;
|
|
11492
|
-
}
|
|
11493
|
-
const template = (0, providerTemplates_1.getNotemdProviderTemplate)(rawTemplateId);
|
|
11494
|
-
if (!template) {
|
|
11495
|
-
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
11496
|
-
res.end(JSON.stringify({ success: false, error: `Unknown provider template: ${rawTemplateId}` }));
|
|
11497
|
-
return;
|
|
11498
|
-
}
|
|
11499
|
-
const settings = await loadNotemdSettings();
|
|
11500
|
-
const updatedSettings = (0, providerTemplates_1.applyProviderTemplateToSettings)(settings, rawTemplateId);
|
|
11501
|
-
const persistedSettings = await persistNotemdSettings(updatedSettings);
|
|
11502
|
-
const persistence = await ensureNotemdProviderTemplatesPersisted();
|
|
11652
|
+
const settingsCandidate = isObjectRecord(payload) && payload.settings !== undefined
|
|
11653
|
+
? payload.settings
|
|
11654
|
+
: payload;
|
|
11655
|
+
const settings = await persistFrontendSettings(settingsCandidate);
|
|
11503
11656
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
11504
|
-
res.end(JSON.stringify({
|
|
11505
|
-
success: true,
|
|
11506
|
-
template,
|
|
11507
|
-
settings: persistedSettings,
|
|
11508
|
-
configPath: persistence.configPath,
|
|
11509
|
-
persistedTemplates: persistence.persisted,
|
|
11510
|
-
}));
|
|
11657
|
+
res.end(JSON.stringify({ success: true, settings }));
|
|
11511
11658
|
}
|
|
11512
11659
|
catch (error) {
|
|
11513
11660
|
writeApiErrorResponse(res, error, {
|
|
11514
|
-
context: 'API:POST /api/
|
|
11661
|
+
context: 'API:POST /api/frontend/settings',
|
|
11515
11662
|
requestId,
|
|
11516
11663
|
});
|
|
11517
11664
|
}
|
|
11518
11665
|
return;
|
|
11519
11666
|
}
|
|
11520
|
-
if (postPathname === '/api/
|
|
11521
|
-
try {
|
|
11522
|
-
const payload = await readJsonBody(req);
|
|
11523
|
-
const workspaceCandidate = isObjectRecord(payload) && payload.workspace !== undefined
|
|
11524
|
-
? payload.workspace
|
|
11525
|
-
: payload;
|
|
11526
|
-
const workspace = await persistNotemdWorkspacePatch(workspaceCandidate);
|
|
11527
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
11528
|
-
res.end(JSON.stringify({ success: true, workspace }));
|
|
11529
|
-
}
|
|
11530
|
-
catch (error) {
|
|
11531
|
-
writeApiErrorResponse(res, error, {
|
|
11532
|
-
context: 'API:POST /api/notemd/workspace',
|
|
11533
|
-
requestId,
|
|
11534
|
-
});
|
|
11535
|
-
}
|
|
11536
|
-
return;
|
|
11537
|
-
}
|
|
11538
|
-
if (postPathname === '/api/path-mode/settings') {
|
|
11539
|
-
try {
|
|
11540
|
-
const payload = await readJsonBody(req);
|
|
11541
|
-
const settingsCandidate = isObjectRecord(payload) && payload.settings !== undefined
|
|
11542
|
-
? payload.settings
|
|
11543
|
-
: payload;
|
|
11544
|
-
const settings = await persistPathModeSettings(settingsCandidate);
|
|
11545
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
11546
|
-
res.end(JSON.stringify({ success: true, settings }));
|
|
11547
|
-
}
|
|
11548
|
-
catch (error) {
|
|
11549
|
-
writeApiErrorResponse(res, error, {
|
|
11550
|
-
context: 'API:POST /api/path-mode/settings',
|
|
11551
|
-
requestId,
|
|
11552
|
-
});
|
|
11553
|
-
}
|
|
11554
|
-
return;
|
|
11555
|
-
}
|
|
11556
|
-
if (postPathname === '/api/frontend/settings') {
|
|
11557
|
-
try {
|
|
11558
|
-
const payload = await readJsonBody(req);
|
|
11559
|
-
const settingsCandidate = isObjectRecord(payload) && payload.settings !== undefined
|
|
11560
|
-
? payload.settings
|
|
11561
|
-
: payload;
|
|
11562
|
-
const settings = await persistFrontendSettings(settingsCandidate);
|
|
11563
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
11564
|
-
res.end(JSON.stringify({ success: true, settings }));
|
|
11565
|
-
}
|
|
11566
|
-
catch (error) {
|
|
11567
|
-
writeApiErrorResponse(res, error, {
|
|
11568
|
-
context: 'API:POST /api/frontend/settings',
|
|
11569
|
-
requestId,
|
|
11570
|
-
});
|
|
11571
|
-
}
|
|
11572
|
-
return;
|
|
11573
|
-
}
|
|
11574
|
-
if (postPathname === '/api/markdown/index') {
|
|
11667
|
+
if (postPathname === '/api/markdown/index') {
|
|
11575
11668
|
try {
|
|
11576
11669
|
const payload = await readJsonBody(req);
|
|
11577
11670
|
const requestBody = isObjectRecord(payload) ? payload : {};
|
|
@@ -11789,645 +11882,6 @@ const startServer = async (options = {}) => {
|
|
|
11789
11882
|
}
|
|
11790
11883
|
return;
|
|
11791
11884
|
}
|
|
11792
|
-
if (postPathname === '/api/notemd/cancel') {
|
|
11793
|
-
try {
|
|
11794
|
-
const payload = await readJsonBody(req);
|
|
11795
|
-
const operationId = String(payload.operationId || '').trim();
|
|
11796
|
-
if (!operationId) {
|
|
11797
|
-
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
11798
|
-
res.end(JSON.stringify({ success: false, error: 'Missing operationId' }));
|
|
11799
|
-
return;
|
|
11800
|
-
}
|
|
11801
|
-
const operation = NOTEMD_ACTIVE_OPERATIONS.get(operationId);
|
|
11802
|
-
if (!operation) {
|
|
11803
|
-
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
11804
|
-
res.end(JSON.stringify({ success: false, error: 'Operation not found' }));
|
|
11805
|
-
return;
|
|
11806
|
-
}
|
|
11807
|
-
if (operation.status !== 'running') {
|
|
11808
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
11809
|
-
res.end(JSON.stringify({
|
|
11810
|
-
success: false,
|
|
11811
|
-
operationId,
|
|
11812
|
-
status: operation.status,
|
|
11813
|
-
message: 'Operation is not running.',
|
|
11814
|
-
}));
|
|
11815
|
-
return;
|
|
11816
|
-
}
|
|
11817
|
-
operation.controller.abort();
|
|
11818
|
-
finalizeNotemdOperation(operation, 'cancelled');
|
|
11819
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
11820
|
-
res.end(JSON.stringify({ success: true, operationId, status: 'cancelled' }));
|
|
11821
|
-
}
|
|
11822
|
-
catch (error) {
|
|
11823
|
-
if (writeBodyParseErrorResponse(res, error)) {
|
|
11824
|
-
return;
|
|
11825
|
-
}
|
|
11826
|
-
console.error(error);
|
|
11827
|
-
CrashLogger_1.CrashLogger.log(error, 'API:POST /api/notemd/cancel');
|
|
11828
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
11829
|
-
res.end(JSON.stringify({ success: false, error: String(error) }));
|
|
11830
|
-
}
|
|
11831
|
-
return;
|
|
11832
|
-
}
|
|
11833
|
-
if (postPathname === '/api/notemd/test-llm') {
|
|
11834
|
-
try {
|
|
11835
|
-
const payload = await readJsonBody(req);
|
|
11836
|
-
const settings = await loadNotemdSettings();
|
|
11837
|
-
const providerName = String(payload.providerName || settings.activeProvider).trim();
|
|
11838
|
-
const provider = settings.providers.find((item) => item.name === providerName);
|
|
11839
|
-
if (!provider) {
|
|
11840
|
-
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
11841
|
-
res.end(JSON.stringify({ success: false, error: `Unknown provider: ${providerName}` }));
|
|
11842
|
-
return;
|
|
11843
|
-
}
|
|
11844
|
-
const result = await notemdLlmClient.testConnection(provider);
|
|
11845
|
-
const statusCode = result.success ? 200 : 400;
|
|
11846
|
-
res.writeHead(statusCode, { 'Content-Type': 'application/json' });
|
|
11847
|
-
res.end(JSON.stringify(result));
|
|
11848
|
-
}
|
|
11849
|
-
catch (error) {
|
|
11850
|
-
if (writeBodyParseErrorResponse(res, error)) {
|
|
11851
|
-
return;
|
|
11852
|
-
}
|
|
11853
|
-
console.error(error);
|
|
11854
|
-
CrashLogger_1.CrashLogger.log(error, 'API:POST /api/notemd/test-llm');
|
|
11855
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
11856
|
-
res.end(JSON.stringify({ success: false, error: String(error) }));
|
|
11857
|
-
}
|
|
11858
|
-
return;
|
|
11859
|
-
}
|
|
11860
|
-
if (postPathname === '/api/notemd/process-file') {
|
|
11861
|
-
const streamEnabled = shouldStreamNotemdResponse(req);
|
|
11862
|
-
let operation = null;
|
|
11863
|
-
try {
|
|
11864
|
-
const payload = await readJsonBody(req);
|
|
11865
|
-
const settings = await loadNotemdSettings();
|
|
11866
|
-
operation = createNotemdOperation(payload.operationId);
|
|
11867
|
-
const reporter = createNotemdReporter(operation, streamEnabled ? res : undefined);
|
|
11868
|
-
if (streamEnabled) {
|
|
11869
|
-
res.writeHead(200, {
|
|
11870
|
-
'Content-Type': 'text/event-stream',
|
|
11871
|
-
'Cache-Control': 'no-cache',
|
|
11872
|
-
Connection: 'keep-alive',
|
|
11873
|
-
});
|
|
11874
|
-
writeSseEvent(res, 'operation', {
|
|
11875
|
-
operationId: operation.id,
|
|
11876
|
-
status: operation.status,
|
|
11877
|
-
});
|
|
11878
|
-
}
|
|
11879
|
-
const resolvedFilePath = await resolvePathWithinKnowledgeBase(payload.filePath, {
|
|
11880
|
-
expectedType: 'file',
|
|
11881
|
-
});
|
|
11882
|
-
const resolvedOutputPath = payload.outputPath
|
|
11883
|
-
? await resolvePathWithinKnowledgeBase(payload.outputPath, {
|
|
11884
|
-
expectedType: 'any',
|
|
11885
|
-
allowMissing: true,
|
|
11886
|
-
})
|
|
11887
|
-
: undefined;
|
|
11888
|
-
const result = await notemdService.processFile({
|
|
11889
|
-
filePath: resolvedFilePath,
|
|
11890
|
-
outputPath: resolvedOutputPath,
|
|
11891
|
-
createConceptNotes: payload.createConceptNotes === true,
|
|
11892
|
-
dryRun: payload.dryRun === true,
|
|
11893
|
-
}, settings, reporter, operation.controller.signal);
|
|
11894
|
-
void persistNotemdWorkspacePatch({
|
|
11895
|
-
filePath: resolvedFilePath,
|
|
11896
|
-
folderPath: path.dirname(resolvedFilePath),
|
|
11897
|
-
outputFilePath: resolvedOutputPath || result.outputPath || '',
|
|
11898
|
-
outputFolderPath: path.dirname(resolvedOutputPath || result.outputPath || resolvedFilePath),
|
|
11899
|
-
}).catch((workspaceError) => {
|
|
11900
|
-
warnDiagnostic('[NoteMD] Failed to persist workspace state after process-file.', workspaceError);
|
|
11901
|
-
});
|
|
11902
|
-
finalizeNotemdOperation(operation, 'done');
|
|
11903
|
-
if (streamEnabled) {
|
|
11904
|
-
writeSseEvent(res, 'done', {
|
|
11905
|
-
success: true,
|
|
11906
|
-
operationId: operation.id,
|
|
11907
|
-
result,
|
|
11908
|
-
});
|
|
11909
|
-
res.end();
|
|
11910
|
-
}
|
|
11911
|
-
else {
|
|
11912
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
11913
|
-
res.end(JSON.stringify({
|
|
11914
|
-
success: true,
|
|
11915
|
-
operationId: operation.id,
|
|
11916
|
-
result,
|
|
11917
|
-
logs: operation.logs,
|
|
11918
|
-
}));
|
|
11919
|
-
}
|
|
11920
|
-
}
|
|
11921
|
-
catch (error) {
|
|
11922
|
-
if (operation) {
|
|
11923
|
-
finalizeNotemdOperation(operation, operation.controller.signal.aborted ? 'cancelled' : 'error');
|
|
11924
|
-
}
|
|
11925
|
-
if (writeBodyParseErrorResponse(res, error)) {
|
|
11926
|
-
return;
|
|
11927
|
-
}
|
|
11928
|
-
if (isAccessDeniedError(error)) {
|
|
11929
|
-
const statusCode = operation?.controller.signal.aborted ? 499 : 403;
|
|
11930
|
-
const payload = { success: false, error: String(error.message || 'Access denied') };
|
|
11931
|
-
if (streamEnabled) {
|
|
11932
|
-
writeSseEvent(res, 'error', payload);
|
|
11933
|
-
res.end();
|
|
11934
|
-
}
|
|
11935
|
-
else {
|
|
11936
|
-
res.writeHead(statusCode, { 'Content-Type': 'application/json' });
|
|
11937
|
-
res.end(JSON.stringify(payload));
|
|
11938
|
-
}
|
|
11939
|
-
return;
|
|
11940
|
-
}
|
|
11941
|
-
console.error(error);
|
|
11942
|
-
CrashLogger_1.CrashLogger.log(error, 'API:POST /api/notemd/process-file');
|
|
11943
|
-
const payload = { success: false, error: String(error) };
|
|
11944
|
-
if (streamEnabled) {
|
|
11945
|
-
writeSseEvent(res, 'error', payload);
|
|
11946
|
-
res.end();
|
|
11947
|
-
}
|
|
11948
|
-
else {
|
|
11949
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
11950
|
-
res.end(JSON.stringify(payload));
|
|
11951
|
-
}
|
|
11952
|
-
}
|
|
11953
|
-
return;
|
|
11954
|
-
}
|
|
11955
|
-
if (postPathname === '/api/notemd/process-folder') {
|
|
11956
|
-
const streamEnabled = shouldStreamNotemdResponse(req);
|
|
11957
|
-
let operation = null;
|
|
11958
|
-
try {
|
|
11959
|
-
const payload = await readJsonBody(req);
|
|
11960
|
-
const settings = await loadNotemdSettings();
|
|
11961
|
-
operation = createNotemdOperation(payload.operationId);
|
|
11962
|
-
const reporter = createNotemdReporter(operation, streamEnabled ? res : undefined);
|
|
11963
|
-
if (streamEnabled) {
|
|
11964
|
-
res.writeHead(200, {
|
|
11965
|
-
'Content-Type': 'text/event-stream',
|
|
11966
|
-
'Cache-Control': 'no-cache',
|
|
11967
|
-
Connection: 'keep-alive',
|
|
11968
|
-
});
|
|
11969
|
-
writeSseEvent(res, 'operation', {
|
|
11970
|
-
operationId: operation.id,
|
|
11971
|
-
status: operation.status,
|
|
11972
|
-
});
|
|
11973
|
-
}
|
|
11974
|
-
const resolvedFolderPath = await resolvePathWithinKnowledgeBase(payload.folderPath, {
|
|
11975
|
-
expectedType: 'directory',
|
|
11976
|
-
});
|
|
11977
|
-
const resolvedOutputFolderPath = payload.outputFolderPath
|
|
11978
|
-
? await resolvePathWithinKnowledgeBase(payload.outputFolderPath, {
|
|
11979
|
-
expectedType: 'any',
|
|
11980
|
-
allowMissing: true,
|
|
11981
|
-
})
|
|
11982
|
-
: undefined;
|
|
11983
|
-
const result = await notemdService.processFolder({
|
|
11984
|
-
folderPath: resolvedFolderPath,
|
|
11985
|
-
outputFolderPath: resolvedOutputFolderPath,
|
|
11986
|
-
createConceptNotes: payload.createConceptNotes === true,
|
|
11987
|
-
dryRun: payload.dryRun === true,
|
|
11988
|
-
}, settings, reporter, operation.controller.signal);
|
|
11989
|
-
void persistNotemdWorkspacePatch({
|
|
11990
|
-
folderPath: resolvedFolderPath,
|
|
11991
|
-
outputFolderPath: resolvedOutputFolderPath || '',
|
|
11992
|
-
}).catch((workspaceError) => {
|
|
11993
|
-
warnDiagnostic('[NoteMD] Failed to persist workspace state after process-folder.', workspaceError);
|
|
11994
|
-
});
|
|
11995
|
-
finalizeNotemdOperation(operation, 'done');
|
|
11996
|
-
if (streamEnabled) {
|
|
11997
|
-
writeSseEvent(res, 'done', {
|
|
11998
|
-
success: true,
|
|
11999
|
-
operationId: operation.id,
|
|
12000
|
-
result,
|
|
12001
|
-
});
|
|
12002
|
-
res.end();
|
|
12003
|
-
}
|
|
12004
|
-
else {
|
|
12005
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
12006
|
-
res.end(JSON.stringify({
|
|
12007
|
-
success: true,
|
|
12008
|
-
operationId: operation.id,
|
|
12009
|
-
result,
|
|
12010
|
-
logs: operation.logs,
|
|
12011
|
-
}));
|
|
12012
|
-
}
|
|
12013
|
-
}
|
|
12014
|
-
catch (error) {
|
|
12015
|
-
if (operation) {
|
|
12016
|
-
finalizeNotemdOperation(operation, operation.controller.signal.aborted ? 'cancelled' : 'error');
|
|
12017
|
-
}
|
|
12018
|
-
if (writeBodyParseErrorResponse(res, error)) {
|
|
12019
|
-
return;
|
|
12020
|
-
}
|
|
12021
|
-
if (isAccessDeniedError(error)) {
|
|
12022
|
-
const statusCode = operation?.controller.signal.aborted ? 499 : 403;
|
|
12023
|
-
const payload = { success: false, error: String(error.message || 'Access denied') };
|
|
12024
|
-
if (streamEnabled) {
|
|
12025
|
-
writeSseEvent(res, 'error', payload);
|
|
12026
|
-
res.end();
|
|
12027
|
-
}
|
|
12028
|
-
else {
|
|
12029
|
-
res.writeHead(statusCode, { 'Content-Type': 'application/json' });
|
|
12030
|
-
res.end(JSON.stringify(payload));
|
|
12031
|
-
}
|
|
12032
|
-
return;
|
|
12033
|
-
}
|
|
12034
|
-
console.error(error);
|
|
12035
|
-
CrashLogger_1.CrashLogger.log(error, 'API:POST /api/notemd/process-folder');
|
|
12036
|
-
const payload = { success: false, error: String(error) };
|
|
12037
|
-
if (streamEnabled) {
|
|
12038
|
-
writeSseEvent(res, 'error', payload);
|
|
12039
|
-
res.end();
|
|
12040
|
-
}
|
|
12041
|
-
else {
|
|
12042
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
12043
|
-
res.end(JSON.stringify(payload));
|
|
12044
|
-
}
|
|
12045
|
-
}
|
|
12046
|
-
return;
|
|
12047
|
-
}
|
|
12048
|
-
if (postPathname === '/api/notemd/generate-content') {
|
|
12049
|
-
try {
|
|
12050
|
-
const payload = await readJsonBody(req);
|
|
12051
|
-
const settings = await loadNotemdSettings();
|
|
12052
|
-
let title = String(payload.title || '').trim();
|
|
12053
|
-
const filePathCandidate = String(payload.filePath || '').trim();
|
|
12054
|
-
if (!title && filePathCandidate) {
|
|
12055
|
-
title = path.basename(filePathCandidate, path.extname(filePathCandidate));
|
|
12056
|
-
}
|
|
12057
|
-
if (!title) {
|
|
12058
|
-
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
12059
|
-
res.end(JSON.stringify({ success: false, error: 'Missing title or filePath' }));
|
|
12060
|
-
return;
|
|
12061
|
-
}
|
|
12062
|
-
const content = await notemdService.generateContent(title, typeof payload.context === 'string' ? payload.context : undefined, settings);
|
|
12063
|
-
let outputPath = null;
|
|
12064
|
-
if (payload.outputPath) {
|
|
12065
|
-
outputPath = await resolvePathWithinKnowledgeBase(payload.outputPath, {
|
|
12066
|
-
expectedType: 'any',
|
|
12067
|
-
allowMissing: true,
|
|
12068
|
-
});
|
|
12069
|
-
}
|
|
12070
|
-
else if (filePathCandidate) {
|
|
12071
|
-
outputPath = await resolvePathWithinKnowledgeBase(filePathCandidate, {
|
|
12072
|
-
expectedType: 'any',
|
|
12073
|
-
allowMissing: true,
|
|
12074
|
-
});
|
|
12075
|
-
}
|
|
12076
|
-
if (outputPath) {
|
|
12077
|
-
await fs.promises.mkdir(path.dirname(outputPath), { recursive: true });
|
|
12078
|
-
await fs.promises.writeFile(outputPath, content, 'utf8');
|
|
12079
|
-
}
|
|
12080
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
12081
|
-
res.end(JSON.stringify({
|
|
12082
|
-
success: true,
|
|
12083
|
-
title,
|
|
12084
|
-
outputPath,
|
|
12085
|
-
content,
|
|
12086
|
-
}));
|
|
12087
|
-
}
|
|
12088
|
-
catch (error) {
|
|
12089
|
-
if (writeBodyParseErrorResponse(res, error)) {
|
|
12090
|
-
return;
|
|
12091
|
-
}
|
|
12092
|
-
if (isAccessDeniedError(error)) {
|
|
12093
|
-
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
12094
|
-
res.end(JSON.stringify({ success: false, error: String(error.message || 'Access denied') }));
|
|
12095
|
-
return;
|
|
12096
|
-
}
|
|
12097
|
-
console.error(error);
|
|
12098
|
-
CrashLogger_1.CrashLogger.log(error, 'API:POST /api/notemd/generate-content');
|
|
12099
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
12100
|
-
res.end(JSON.stringify({ success: false, error: String(error) }));
|
|
12101
|
-
}
|
|
12102
|
-
return;
|
|
12103
|
-
}
|
|
12104
|
-
if (postPathname === '/api/notemd/translate-file') {
|
|
12105
|
-
try {
|
|
12106
|
-
const payload = await readJsonBody(req);
|
|
12107
|
-
const settings = await loadNotemdSettings();
|
|
12108
|
-
const resolvedFilePath = await resolvePathWithinKnowledgeBase(payload.filePath, {
|
|
12109
|
-
expectedType: 'file',
|
|
12110
|
-
});
|
|
12111
|
-
const resolvedOutputPath = payload.outputPath
|
|
12112
|
-
? await resolvePathWithinKnowledgeBase(payload.outputPath, {
|
|
12113
|
-
expectedType: 'any',
|
|
12114
|
-
allowMissing: true,
|
|
12115
|
-
})
|
|
12116
|
-
: undefined;
|
|
12117
|
-
const targetLanguage = String(payload.targetLanguage || settings.translateLanguage || settings.language).trim();
|
|
12118
|
-
if (!targetLanguage) {
|
|
12119
|
-
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
12120
|
-
res.end(JSON.stringify({ success: false, error: 'Missing targetLanguage' }));
|
|
12121
|
-
return;
|
|
12122
|
-
}
|
|
12123
|
-
const result = await notemdService.translateFile({
|
|
12124
|
-
filePath: resolvedFilePath,
|
|
12125
|
-
outputPath: resolvedOutputPath,
|
|
12126
|
-
targetLanguage,
|
|
12127
|
-
}, settings);
|
|
12128
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
12129
|
-
res.end(JSON.stringify({ success: true, result }));
|
|
12130
|
-
}
|
|
12131
|
-
catch (error) {
|
|
12132
|
-
if (writeBodyParseErrorResponse(res, error)) {
|
|
12133
|
-
return;
|
|
12134
|
-
}
|
|
12135
|
-
if (isAccessDeniedError(error)) {
|
|
12136
|
-
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
12137
|
-
res.end(JSON.stringify({ success: false, error: String(error.message || 'Access denied') }));
|
|
12138
|
-
return;
|
|
12139
|
-
}
|
|
12140
|
-
console.error(error);
|
|
12141
|
-
CrashLogger_1.CrashLogger.log(error, 'API:POST /api/notemd/translate-file');
|
|
12142
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
12143
|
-
res.end(JSON.stringify({ success: false, error: String(error) }));
|
|
12144
|
-
}
|
|
12145
|
-
return;
|
|
12146
|
-
}
|
|
12147
|
-
if (postPathname === '/api/notemd/translate-folder') {
|
|
12148
|
-
try {
|
|
12149
|
-
const payload = await readJsonBody(req);
|
|
12150
|
-
const settings = await loadNotemdSettings();
|
|
12151
|
-
const resolvedFolderPath = await resolvePathWithinKnowledgeBase(payload.folderPath, {
|
|
12152
|
-
expectedType: 'directory',
|
|
12153
|
-
});
|
|
12154
|
-
const targetLanguage = String(payload.targetLanguage || settings.translateLanguage || settings.language).trim();
|
|
12155
|
-
if (!targetLanguage) {
|
|
12156
|
-
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
12157
|
-
res.end(JSON.stringify({ success: false, error: 'Missing targetLanguage' }));
|
|
12158
|
-
return;
|
|
12159
|
-
}
|
|
12160
|
-
const result = await notemdService.translateFolder(resolvedFolderPath, targetLanguage, settings);
|
|
12161
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
12162
|
-
res.end(JSON.stringify({ success: true, result }));
|
|
12163
|
-
}
|
|
12164
|
-
catch (error) {
|
|
12165
|
-
if (writeBodyParseErrorResponse(res, error)) {
|
|
12166
|
-
return;
|
|
12167
|
-
}
|
|
12168
|
-
if (isAccessDeniedError(error)) {
|
|
12169
|
-
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
12170
|
-
res.end(JSON.stringify({ success: false, error: String(error.message || 'Access denied') }));
|
|
12171
|
-
return;
|
|
12172
|
-
}
|
|
12173
|
-
console.error(error);
|
|
12174
|
-
CrashLogger_1.CrashLogger.log(error, 'API:POST /api/notemd/translate-folder');
|
|
12175
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
12176
|
-
res.end(JSON.stringify({ success: false, error: String(error) }));
|
|
12177
|
-
}
|
|
12178
|
-
return;
|
|
12179
|
-
}
|
|
12180
|
-
if (postPathname === '/api/notemd/fix-mermaid') {
|
|
12181
|
-
try {
|
|
12182
|
-
const payload = await readJsonBody(req);
|
|
12183
|
-
const resolvedFilePath = await resolvePathWithinKnowledgeBase(payload.filePath, {
|
|
12184
|
-
expectedType: 'file',
|
|
12185
|
-
});
|
|
12186
|
-
const inPlace = payload.inPlace !== false;
|
|
12187
|
-
const result = await notemdService.fixMermaid(resolvedFilePath, inPlace);
|
|
12188
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
12189
|
-
res.end(JSON.stringify({ success: true, result }));
|
|
12190
|
-
}
|
|
12191
|
-
catch (error) {
|
|
12192
|
-
if (writeBodyParseErrorResponse(res, error)) {
|
|
12193
|
-
return;
|
|
12194
|
-
}
|
|
12195
|
-
if (isAccessDeniedError(error)) {
|
|
12196
|
-
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
12197
|
-
res.end(JSON.stringify({ success: false, error: String(error.message || 'Access denied') }));
|
|
12198
|
-
return;
|
|
12199
|
-
}
|
|
12200
|
-
console.error(error);
|
|
12201
|
-
CrashLogger_1.CrashLogger.log(error, 'API:POST /api/notemd/fix-mermaid');
|
|
12202
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
12203
|
-
res.end(JSON.stringify({ success: false, error: String(error) }));
|
|
12204
|
-
}
|
|
12205
|
-
return;
|
|
12206
|
-
}
|
|
12207
|
-
if (postPathname === '/api/notemd/fix-formulas') {
|
|
12208
|
-
try {
|
|
12209
|
-
const payload = await readJsonBody(req);
|
|
12210
|
-
const resolvedFilePath = await resolvePathWithinKnowledgeBase(payload.filePath, {
|
|
12211
|
-
expectedType: 'file',
|
|
12212
|
-
});
|
|
12213
|
-
const inPlace = payload.inPlace !== false;
|
|
12214
|
-
const result = await notemdService.fixFormulas(resolvedFilePath, inPlace);
|
|
12215
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
12216
|
-
res.end(JSON.stringify({ success: true, result }));
|
|
12217
|
-
}
|
|
12218
|
-
catch (error) {
|
|
12219
|
-
if (writeBodyParseErrorResponse(res, error)) {
|
|
12220
|
-
return;
|
|
12221
|
-
}
|
|
12222
|
-
if (isAccessDeniedError(error)) {
|
|
12223
|
-
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
12224
|
-
res.end(JSON.stringify({ success: false, error: String(error.message || 'Access denied') }));
|
|
12225
|
-
return;
|
|
12226
|
-
}
|
|
12227
|
-
console.error(error);
|
|
12228
|
-
CrashLogger_1.CrashLogger.log(error, 'API:POST /api/notemd/fix-formulas');
|
|
12229
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
12230
|
-
res.end(JSON.stringify({ success: false, error: String(error) }));
|
|
12231
|
-
}
|
|
12232
|
-
return;
|
|
12233
|
-
}
|
|
12234
|
-
if (postPathname === '/api/notemd/check-duplicates') {
|
|
12235
|
-
try {
|
|
12236
|
-
const payload = await readJsonBody(req);
|
|
12237
|
-
const resolvedFilePath = await resolvePathWithinKnowledgeBase(payload.filePath, {
|
|
12238
|
-
expectedType: 'file',
|
|
12239
|
-
});
|
|
12240
|
-
const result = await notemdService.checkDuplicates(resolvedFilePath);
|
|
12241
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
12242
|
-
res.end(JSON.stringify({ success: true, result }));
|
|
12243
|
-
}
|
|
12244
|
-
catch (error) {
|
|
12245
|
-
if (writeBodyParseErrorResponse(res, error)) {
|
|
12246
|
-
return;
|
|
12247
|
-
}
|
|
12248
|
-
if (isAccessDeniedError(error)) {
|
|
12249
|
-
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
12250
|
-
res.end(JSON.stringify({ success: false, error: String(error.message || 'Access denied') }));
|
|
12251
|
-
return;
|
|
12252
|
-
}
|
|
12253
|
-
console.error(error);
|
|
12254
|
-
CrashLogger_1.CrashLogger.log(error, 'API:POST /api/notemd/check-duplicates');
|
|
12255
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
12256
|
-
res.end(JSON.stringify({ success: false, error: String(error) }));
|
|
12257
|
-
}
|
|
12258
|
-
return;
|
|
12259
|
-
}
|
|
12260
|
-
if (postPathname === '/api/notemd/extract-concepts') {
|
|
12261
|
-
const streamEnabled = shouldStreamNotemdResponse(req);
|
|
12262
|
-
let operation = null;
|
|
12263
|
-
try {
|
|
12264
|
-
const payload = await readJsonBody(req);
|
|
12265
|
-
const settings = await loadNotemdSettings();
|
|
12266
|
-
operation = createNotemdOperation(payload.operationId);
|
|
12267
|
-
const reporter = createNotemdReporter(operation, streamEnabled ? res : undefined);
|
|
12268
|
-
if (streamEnabled) {
|
|
12269
|
-
res.writeHead(200, {
|
|
12270
|
-
'Content-Type': 'text/event-stream',
|
|
12271
|
-
'Cache-Control': 'no-cache',
|
|
12272
|
-
Connection: 'keep-alive',
|
|
12273
|
-
});
|
|
12274
|
-
writeSseEvent(res, 'operation', {
|
|
12275
|
-
operationId: operation.id,
|
|
12276
|
-
status: operation.status,
|
|
12277
|
-
});
|
|
12278
|
-
}
|
|
12279
|
-
const resolvedFilePath = await resolvePathWithinKnowledgeBase(payload.filePath, {
|
|
12280
|
-
expectedType: 'file',
|
|
12281
|
-
});
|
|
12282
|
-
const result = await notemdService.extractConcepts(resolvedFilePath, settings, reporter, operation.controller.signal);
|
|
12283
|
-
void persistNotemdWorkspacePatch({
|
|
12284
|
-
filePath: resolvedFilePath,
|
|
12285
|
-
folderPath: path.dirname(resolvedFilePath),
|
|
12286
|
-
}).catch((workspaceError) => {
|
|
12287
|
-
warnDiagnostic('[NoteMD] Failed to persist workspace state after extract-concepts.', workspaceError);
|
|
12288
|
-
});
|
|
12289
|
-
finalizeNotemdOperation(operation, 'done');
|
|
12290
|
-
if (streamEnabled) {
|
|
12291
|
-
writeSseEvent(res, 'done', {
|
|
12292
|
-
success: true,
|
|
12293
|
-
operationId: operation.id,
|
|
12294
|
-
result,
|
|
12295
|
-
});
|
|
12296
|
-
res.end();
|
|
12297
|
-
}
|
|
12298
|
-
else {
|
|
12299
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
12300
|
-
res.end(JSON.stringify({ success: true, operationId: operation.id, result, logs: operation.logs }));
|
|
12301
|
-
}
|
|
12302
|
-
}
|
|
12303
|
-
catch (error) {
|
|
12304
|
-
if (operation) {
|
|
12305
|
-
finalizeNotemdOperation(operation, operation.controller.signal.aborted ? 'cancelled' : 'error');
|
|
12306
|
-
}
|
|
12307
|
-
if (writeBodyParseErrorResponse(res, error)) {
|
|
12308
|
-
return;
|
|
12309
|
-
}
|
|
12310
|
-
if (isAccessDeniedError(error)) {
|
|
12311
|
-
const statusCode = operation?.controller.signal.aborted ? 499 : 403;
|
|
12312
|
-
const payload = { success: false, error: String(error.message || 'Access denied') };
|
|
12313
|
-
if (streamEnabled) {
|
|
12314
|
-
writeSseEvent(res, 'error', payload);
|
|
12315
|
-
res.end();
|
|
12316
|
-
}
|
|
12317
|
-
else {
|
|
12318
|
-
res.writeHead(statusCode, { 'Content-Type': 'application/json' });
|
|
12319
|
-
res.end(JSON.stringify(payload));
|
|
12320
|
-
}
|
|
12321
|
-
return;
|
|
12322
|
-
}
|
|
12323
|
-
console.error(error);
|
|
12324
|
-
CrashLogger_1.CrashLogger.log(error, 'API:POST /api/notemd/extract-concepts');
|
|
12325
|
-
const payload = { success: false, error: String(error) };
|
|
12326
|
-
if (streamEnabled) {
|
|
12327
|
-
writeSseEvent(res, 'error', payload);
|
|
12328
|
-
res.end();
|
|
12329
|
-
}
|
|
12330
|
-
else {
|
|
12331
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
12332
|
-
res.end(JSON.stringify(payload));
|
|
12333
|
-
}
|
|
12334
|
-
}
|
|
12335
|
-
return;
|
|
12336
|
-
}
|
|
12337
|
-
if (postPathname === '/api/notemd/one-click-extract') {
|
|
12338
|
-
let operation = null;
|
|
12339
|
-
try {
|
|
12340
|
-
const payload = await readJsonBody(req);
|
|
12341
|
-
const settings = await loadNotemdSettings();
|
|
12342
|
-
operation = createNotemdOperation(payload.operationId);
|
|
12343
|
-
const reporter = createNotemdReporter(operation);
|
|
12344
|
-
const resolvedFilePath = await resolvePathWithinKnowledgeBase(payload.filePath, {
|
|
12345
|
-
expectedType: 'file',
|
|
12346
|
-
});
|
|
12347
|
-
const result = await notemdService.oneClickExtract(resolvedFilePath, settings, reporter, operation.controller.signal);
|
|
12348
|
-
void persistNotemdWorkspacePatch({
|
|
12349
|
-
filePath: resolvedFilePath,
|
|
12350
|
-
folderPath: result.outputFolderPath,
|
|
12351
|
-
outputFolderPath: result.outputFolderPath,
|
|
12352
|
-
}).catch((workspaceError) => {
|
|
12353
|
-
warnDiagnostic('[NoteMD] Failed to persist workspace state after one-click-extract.', workspaceError);
|
|
12354
|
-
});
|
|
12355
|
-
finalizeNotemdOperation(operation, 'done');
|
|
12356
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
12357
|
-
res.end(JSON.stringify({ success: true, operationId: operation.id, result, logs: operation.logs }));
|
|
12358
|
-
}
|
|
12359
|
-
catch (error) {
|
|
12360
|
-
if (operation) {
|
|
12361
|
-
finalizeNotemdOperation(operation, operation.controller.signal.aborted ? 'cancelled' : 'error');
|
|
12362
|
-
}
|
|
12363
|
-
if (writeBodyParseErrorResponse(res, error)) {
|
|
12364
|
-
return;
|
|
12365
|
-
}
|
|
12366
|
-
if (isAccessDeniedError(error)) {
|
|
12367
|
-
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
12368
|
-
res.end(JSON.stringify({ success: false, error: String(error.message || 'Access denied') }));
|
|
12369
|
-
return;
|
|
12370
|
-
}
|
|
12371
|
-
console.error(error);
|
|
12372
|
-
CrashLogger_1.CrashLogger.log(error, 'API:POST /api/notemd/one-click-extract');
|
|
12373
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
12374
|
-
res.end(JSON.stringify({ success: false, error: String(error) }));
|
|
12375
|
-
}
|
|
12376
|
-
return;
|
|
12377
|
-
}
|
|
12378
|
-
if (postPathname === '/api/notemd/batch-fix-mermaid') {
|
|
12379
|
-
try {
|
|
12380
|
-
const payload = await readJsonBody(req);
|
|
12381
|
-
const resolvedFolderPath = await resolvePathWithinKnowledgeBase(payload.folderPath, {
|
|
12382
|
-
expectedType: 'directory',
|
|
12383
|
-
});
|
|
12384
|
-
const result = await notemdService.batchFixMermaid(resolvedFolderPath, payload.inPlace !== false);
|
|
12385
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
12386
|
-
res.end(JSON.stringify({ success: true, result }));
|
|
12387
|
-
}
|
|
12388
|
-
catch (error) {
|
|
12389
|
-
if (writeBodyParseErrorResponse(res, error)) {
|
|
12390
|
-
return;
|
|
12391
|
-
}
|
|
12392
|
-
if (isAccessDeniedError(error)) {
|
|
12393
|
-
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
12394
|
-
res.end(JSON.stringify({ success: false, error: String(error.message || 'Access denied') }));
|
|
12395
|
-
return;
|
|
12396
|
-
}
|
|
12397
|
-
console.error(error);
|
|
12398
|
-
CrashLogger_1.CrashLogger.log(error, 'API:POST /api/notemd/batch-fix-mermaid');
|
|
12399
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
12400
|
-
res.end(JSON.stringify({ success: false, error: String(error) }));
|
|
12401
|
-
}
|
|
12402
|
-
return;
|
|
12403
|
-
}
|
|
12404
|
-
if (postPathname === '/api/notemd/generate-folder-content') {
|
|
12405
|
-
try {
|
|
12406
|
-
const payload = await readJsonBody(req);
|
|
12407
|
-
const settings = await loadNotemdSettings();
|
|
12408
|
-
const resolvedFolderPath = await resolvePathWithinKnowledgeBase(payload.folderPath, {
|
|
12409
|
-
expectedType: 'directory',
|
|
12410
|
-
});
|
|
12411
|
-
const result = await notemdService.generateFolderContent(resolvedFolderPath, settings);
|
|
12412
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
12413
|
-
res.end(JSON.stringify({ success: true, result }));
|
|
12414
|
-
}
|
|
12415
|
-
catch (error) {
|
|
12416
|
-
if (writeBodyParseErrorResponse(res, error)) {
|
|
12417
|
-
return;
|
|
12418
|
-
}
|
|
12419
|
-
if (isAccessDeniedError(error)) {
|
|
12420
|
-
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
12421
|
-
res.end(JSON.stringify({ success: false, error: String(error.message || 'Access denied') }));
|
|
12422
|
-
return;
|
|
12423
|
-
}
|
|
12424
|
-
console.error(error);
|
|
12425
|
-
CrashLogger_1.CrashLogger.log(error, 'API:POST /api/notemd/generate-folder-content');
|
|
12426
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
12427
|
-
res.end(JSON.stringify({ success: false, error: String(error) }));
|
|
12428
|
-
}
|
|
12429
|
-
return;
|
|
12430
|
-
}
|
|
12431
11885
|
if (req.url === '/api/render/math') {
|
|
12432
11886
|
try {
|
|
12433
11887
|
const payload = await readJsonBody(req);
|
|
@@ -12590,6 +12044,17 @@ const startServer = async (options = {}) => {
|
|
|
12590
12044
|
try {
|
|
12591
12045
|
const payload = await readJsonBody(req);
|
|
12592
12046
|
const { target, maxWorkers, enableGPU, enableGPULayout, memorySavingMode, deepDebug } = payload;
|
|
12047
|
+
const requestedRelationRecomputeMode = payload?.relationRecomputeMode;
|
|
12048
|
+
const relationRecomputeMode = normalizeRelationRecomputeModeValue(requestedRelationRecomputeMode);
|
|
12049
|
+
if (requestedRelationRecomputeMode !== undefined
|
|
12050
|
+
&& (!relationRecomputeMode || relationRecomputeMode === 'auto')) {
|
|
12051
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
12052
|
+
res.end(JSON.stringify({
|
|
12053
|
+
success: false,
|
|
12054
|
+
error: 'relationRecomputeMode must be one of: none, incremental, full',
|
|
12055
|
+
}));
|
|
12056
|
+
return;
|
|
12057
|
+
}
|
|
12593
12058
|
logDiagnostic('Received build request for:', target, 'maxWorkers:', maxWorkers, 'enableGPU:', enableGPU, 'enableGPULayout:', enableGPULayout, 'memorySavingMode:', memorySavingMode, 'deepDebug:', deepDebug);
|
|
12594
12059
|
const buildKey = JSON.stringify({
|
|
12595
12060
|
target,
|
|
@@ -12597,7 +12062,8 @@ const startServer = async (options = {}) => {
|
|
|
12597
12062
|
enableGPU,
|
|
12598
12063
|
enableGPULayout,
|
|
12599
12064
|
memorySavingMode,
|
|
12600
|
-
deepDebug
|
|
12065
|
+
deepDebug,
|
|
12066
|
+
relationRecomputeMode: relationRecomputeMode || 'incremental',
|
|
12601
12067
|
});
|
|
12602
12068
|
// De-duplicate accidental double-submit from frontend.
|
|
12603
12069
|
if (activeBuildPromise) {
|
|
@@ -12640,7 +12106,13 @@ const startServer = async (options = {}) => {
|
|
|
12640
12106
|
deepDebug
|
|
12641
12107
|
});
|
|
12642
12108
|
ACTIVE_GRAPH_TARGET = normalizedRuntimeTarget;
|
|
12643
|
-
|
|
12109
|
+
const files = await collectMarkdownFilePaths(targetToBuild);
|
|
12110
|
+
const documents = await buildKnowledgeDocumentPayloadsFromPaths(files);
|
|
12111
|
+
const ingest = await knowledgeLearningPlatform.ingestKnowledge({
|
|
12112
|
+
incremental: true, documents, ingestedAt: new Date().toISOString(),
|
|
12113
|
+
relationRecomputeMode: relationRecomputeMode || 'incremental',
|
|
12114
|
+
});
|
|
12115
|
+
return { target: normalizedRuntimeTarget, documentCount: documents.length, summary: ingest.summary };
|
|
12644
12116
|
})();
|
|
12645
12117
|
activeBuildKey = buildKey;
|
|
12646
12118
|
activeBuildPromise = buildPromise;
|
|
@@ -12707,6 +12179,10 @@ const startServer = async (options = {}) => {
|
|
|
12707
12179
|
return;
|
|
12708
12180
|
}
|
|
12709
12181
|
}
|
|
12182
|
+
if (!res.writableEnded && !res.destroyed) {
|
|
12183
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
12184
|
+
res.end(JSON.stringify({ success: false, error: 'API route not found' }));
|
|
12185
|
+
}
|
|
12710
12186
|
});
|
|
12711
12187
|
server.on('close', () => {
|
|
12712
12188
|
try {
|