noteconnection 1.8.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +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/appimage.update.test.js +127 -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
|
@@ -1137,6 +1137,30 @@ function createWorkspaceHtml() {
|
|
|
1137
1137
|
<select id="agent-workspace-scope-select"></select>
|
|
1138
1138
|
<div id="agent-workspace-scope-summary"></div>
|
|
1139
1139
|
</div>
|
|
1140
|
+
<div class="agent-answer-language-control">
|
|
1141
|
+
<label for="agent-workspace-answer-language-select">Answer language</label>
|
|
1142
|
+
<select id="agent-workspace-answer-language-select">
|
|
1143
|
+
<option value="en">English</option>
|
|
1144
|
+
<option value="zh">中文</option>
|
|
1145
|
+
</select>
|
|
1146
|
+
<div id="agent-workspace-answer-language-summary"></div>
|
|
1147
|
+
</div>
|
|
1148
|
+
<div class="agent-response-mode-control">
|
|
1149
|
+
<label for="agent-workspace-response-mode-select">Answer detail</label>
|
|
1150
|
+
<select id="agent-workspace-response-mode-select">
|
|
1151
|
+
<option value="slim">Slim</option>
|
|
1152
|
+
<option value="full">Full report</option>
|
|
1153
|
+
</select>
|
|
1154
|
+
<div id="agent-workspace-response-mode-summary"></div>
|
|
1155
|
+
</div>
|
|
1156
|
+
<div class="agent-response-budget-control" data-agent-desktop-only="true">
|
|
1157
|
+
<label for="agent-workspace-response-budget-select">Full report budget</label>
|
|
1158
|
+
<select id="agent-workspace-response-budget-select">
|
|
1159
|
+
<option value="adaptive">Adaptive</option>
|
|
1160
|
+
<option value="unbounded">No product cap</option>
|
|
1161
|
+
</select>
|
|
1162
|
+
<div id="agent-workspace-response-budget-summary"></div>
|
|
1163
|
+
</div>
|
|
1140
1164
|
<input id="agent-workspace-user-id" value="path_user_default" />
|
|
1141
1165
|
<div id="agent-workspace-chat-messages"></div>
|
|
1142
1166
|
<div id="agent-workspace-knowledge-points"></div>
|
|
@@ -1188,6 +1212,7 @@ function createBaseSandbox(dom) {
|
|
|
1188
1212
|
const sandbox = {
|
|
1189
1213
|
window: dom.window,
|
|
1190
1214
|
document: dom.window.document,
|
|
1215
|
+
localStorage: dom.window.localStorage,
|
|
1191
1216
|
console: {
|
|
1192
1217
|
log: jest.fn(),
|
|
1193
1218
|
warn: jest.fn(),
|
|
@@ -4157,7 +4182,6 @@ describe('workspace panes controller', () => {
|
|
|
4157
4182
|
const toolbarRule = styles.match(/\.agent-workspace-drawer-toolbar\s*\{[^}]*\}/)?.[0] || '';
|
|
4158
4183
|
const workspaceScopeRule = styles.match(/\.agent-scope-control--workspace\s*\{[^}]*\}/)?.[0] || '';
|
|
4159
4184
|
const scopeStyle = styles.match(/\.agent-scope-control\s*\{[^}]*\}/)?.[0] || '';
|
|
4160
|
-
const chatPaneRule = styles.match(/\.agent-chat-pane\s*\{[^}]*\}/)?.[0] || '';
|
|
4161
4185
|
const knowledgePointsRule = styles.match(/\.agent-knowledge-points\s*\{[^}]*\}/)?.[0] || '';
|
|
4162
4186
|
const knowledgeCardHeaderRule = styles.match(/\.agent-knowledge-card-header\s*\{[^}]*\}/)?.[0] || '';
|
|
4163
4187
|
const knowledgeMenuButtonRule = styles.match(/\.agent-knowledge-menu-button\s*\{[^}]*\}/)?.[0] || '';
|
|
@@ -4165,8 +4189,6 @@ describe('workspace panes controller', () => {
|
|
|
4165
4189
|
expect(toolbarRule).toContain('grid-template-columns: minmax(180px, 1fr) auto');
|
|
4166
4190
|
expect(workspaceScopeRule).toContain('grid-template-columns: minmax(94px, auto) minmax(0, 1fr)');
|
|
4167
4191
|
expect(scopeStyle).not.toContain('position: sticky');
|
|
4168
|
-
expect(chatPaneRule).toContain('display: grid');
|
|
4169
|
-
expect(chatPaneRule).toContain('grid-template-rows:');
|
|
4170
4192
|
expect(knowledgePointsRule).toContain('overflow-y: auto');
|
|
4171
4193
|
expect(knowledgeCardHeaderRule).toContain('grid-template-columns: minmax(0, 1fr) 44px');
|
|
4172
4194
|
expect(knowledgeMenuButtonRule).toContain('width: 44px');
|
|
@@ -4290,12 +4312,8 @@ describe('workspace panes controller', () => {
|
|
|
4290
4312
|
expect(chatPaneRule).toContain('overflow-x: hidden');
|
|
4291
4313
|
expect(chatPaneRule).toContain('overflow-y: auto');
|
|
4292
4314
|
expect(chatPaneRule).not.toContain('overflow: hidden');
|
|
4293
|
-
|
|
4294
|
-
expect(chatPaneRule).toContain('grid-template-rows:');
|
|
4295
|
-
expect(chatPaneRule).toContain('minmax(128px, 0.62fr)');
|
|
4296
|
-
expect(chatPaneRule).toContain('max-height: calc(100vh - 142px)');
|
|
4315
|
+
// The browser verifier measures readable transcript height with optional controls present.
|
|
4297
4316
|
expect(chatMessagesRule).toContain('overflow-y: auto');
|
|
4298
|
-
expect(chatMessagesRule).toContain('min-height: 0');
|
|
4299
4317
|
expect(knowledgePointsRule).toContain('overflow-y: auto');
|
|
4300
4318
|
expect(knowledgePointsRule).toContain('min-height: 0');
|
|
4301
4319
|
expect(knowledgePointsRule).toContain('scrollbar-gutter: stable');
|
|
@@ -6112,6 +6130,289 @@ describe('agent workspace learning-path integration', () => {
|
|
|
6112
6130
|
corpusId: 'waterglass',
|
|
6113
6131
|
sourcePathPrefixes: ['Knowledge_Base/waterglass'],
|
|
6114
6132
|
});
|
|
6133
|
+
expect(requestBody.answerLanguage).toBe('en');
|
|
6134
|
+
});
|
|
6135
|
+
test('uses the software language by default and preserves an explicit answer-language override in requests', async () => {
|
|
6136
|
+
const { document, window, fetchMock, } = loadAgentWorkspaceHarness({ withI18n: true });
|
|
6137
|
+
if (!fetchMock) {
|
|
6138
|
+
throw new Error('expected fetch mock');
|
|
6139
|
+
}
|
|
6140
|
+
const completedTurn = (turnId) => createSseResponse([
|
|
6141
|
+
{
|
|
6142
|
+
event: 'turn_completed',
|
|
6143
|
+
payload: {
|
|
6144
|
+
type: 'turn_completed',
|
|
6145
|
+
turnId,
|
|
6146
|
+
emittedAt: '2026-08-24T00:00:00.000Z',
|
|
6147
|
+
result: {
|
|
6148
|
+
assistantMessage: 'scoped response',
|
|
6149
|
+
citations: [],
|
|
6150
|
+
recalledMemories: [],
|
|
6151
|
+
memoryActions: [],
|
|
6152
|
+
knowledgePoints: [],
|
|
6153
|
+
summary: {
|
|
6154
|
+
generatedAt: '2026-08-24T00:00:00.000Z',
|
|
6155
|
+
topK: 6,
|
|
6156
|
+
returnedKnowledgePoints: 0,
|
|
6157
|
+
returnedCitations: 0,
|
|
6158
|
+
recalledMemoryCount: 0,
|
|
6159
|
+
queryEvidenceCoverageRatioPct: 0,
|
|
6160
|
+
},
|
|
6161
|
+
},
|
|
6162
|
+
},
|
|
6163
|
+
},
|
|
6164
|
+
]);
|
|
6165
|
+
fetchMock.mockImplementationOnce(async () => completedTurn('turn_answer_language_default'));
|
|
6166
|
+
fetchMock.mockImplementationOnce(async () => completedTurn('turn_answer_language_override'));
|
|
6167
|
+
const selector = document.getElementById('agent-workspace-answer-language-select');
|
|
6168
|
+
await window.i18n.setLanguage('zh');
|
|
6169
|
+
expect(selector.value).toBe('zh');
|
|
6170
|
+
const input = document.getElementById('agent-workspace-chat-input');
|
|
6171
|
+
input.value = 'what is water glass?';
|
|
6172
|
+
await window.NoteConnectionAgentWorkspace.sendConversation();
|
|
6173
|
+
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body || '{}')).answerLanguage).toBe('zh');
|
|
6174
|
+
selector.value = 'en';
|
|
6175
|
+
selector.dispatchEvent(new window.Event('change', { bubbles: true }));
|
|
6176
|
+
expect(window.localStorage.getItem('nc_agent_answer_language')).toBe('en');
|
|
6177
|
+
await window.i18n.setLanguage('zh');
|
|
6178
|
+
expect(selector.value).toBe('en');
|
|
6179
|
+
input.value = 'what is water glass?';
|
|
6180
|
+
await window.NoteConnectionAgentWorkspace.sendConversation();
|
|
6181
|
+
expect(JSON.parse(String(fetchMock.mock.calls[1]?.[1]?.body || '{}')).answerLanguage).toBe('en');
|
|
6182
|
+
});
|
|
6183
|
+
test('persists slim/full answer detail and sends the selected mode', async () => {
|
|
6184
|
+
const { document, window, fetchMock } = loadAgentWorkspaceHarness({ withI18n: true });
|
|
6185
|
+
if (!fetchMock) {
|
|
6186
|
+
throw new Error('expected fetch mock');
|
|
6187
|
+
}
|
|
6188
|
+
const response = () => createSseResponse([
|
|
6189
|
+
{
|
|
6190
|
+
event: 'turn_completed',
|
|
6191
|
+
payload: {
|
|
6192
|
+
type: 'turn_completed',
|
|
6193
|
+
turnId: 'turn_response_mode',
|
|
6194
|
+
emittedAt: '2026-09-03T00:00:00.000Z',
|
|
6195
|
+
result: {
|
|
6196
|
+
responseMode: 'full',
|
|
6197
|
+
assistantMessage: 'full report',
|
|
6198
|
+
answer: 'full report',
|
|
6199
|
+
citations: [],
|
|
6200
|
+
recalledMemories: [],
|
|
6201
|
+
memoryActions: [],
|
|
6202
|
+
knowledgePoints: [],
|
|
6203
|
+
summary: {
|
|
6204
|
+
generatedAt: '2026-09-03T00:00:00.000Z',
|
|
6205
|
+
topK: 6,
|
|
6206
|
+
returnedKnowledgePoints: 0,
|
|
6207
|
+
returnedCitations: 0,
|
|
6208
|
+
recalledMemoryCount: 0,
|
|
6209
|
+
queryEvidenceCoverageRatioPct: 0,
|
|
6210
|
+
},
|
|
6211
|
+
},
|
|
6212
|
+
},
|
|
6213
|
+
},
|
|
6214
|
+
]);
|
|
6215
|
+
fetchMock.mockImplementationOnce(async () => response());
|
|
6216
|
+
const selector = document.getElementById('agent-workspace-response-mode-select');
|
|
6217
|
+
expect(selector.value).toBe('slim');
|
|
6218
|
+
selector.value = 'full';
|
|
6219
|
+
selector.dispatchEvent(new window.Event('change', { bubbles: true }));
|
|
6220
|
+
expect(window.localStorage.getItem('nc_agent_response_mode')).toBe('full');
|
|
6221
|
+
expect(String(document.getElementById('agent-workspace-response-mode-summary')?.textContent || '')).toContain('Complete');
|
|
6222
|
+
const input = document.getElementById('agent-workspace-chat-input');
|
|
6223
|
+
input.value = 'what is water glass?';
|
|
6224
|
+
await window.NoteConnectionAgentWorkspace.sendConversation();
|
|
6225
|
+
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body || '{}')).responseMode).toBe('full');
|
|
6226
|
+
selector.value = 'slim';
|
|
6227
|
+
selector.dispatchEvent(new window.Event('change', { bubbles: true }));
|
|
6228
|
+
input.value = 'what is water glass?';
|
|
6229
|
+
await window.NoteConnectionAgentWorkspace.sendConversation();
|
|
6230
|
+
expect(JSON.parse(String(fetchMock.mock.calls[1]?.[1]?.body || '{}')).responseMode).toBe('slim');
|
|
6231
|
+
});
|
|
6232
|
+
test('persists adaptive/unbounded full-report budget and sends it only on desktop', async () => {
|
|
6233
|
+
const { document, window, fetchMock } = loadAgentWorkspaceHarness({ withI18n: true });
|
|
6234
|
+
if (!fetchMock) {
|
|
6235
|
+
throw new Error('expected fetch mock');
|
|
6236
|
+
}
|
|
6237
|
+
const response = () => createSseResponse([{
|
|
6238
|
+
event: 'turn_completed',
|
|
6239
|
+
payload: {
|
|
6240
|
+
type: 'turn_completed',
|
|
6241
|
+
turnId: 'turn_response_budget_ui',
|
|
6242
|
+
emittedAt: '2026-09-03T00:00:00.000Z',
|
|
6243
|
+
result: {
|
|
6244
|
+
responseMode: 'full',
|
|
6245
|
+
responseBudget: { mode: 'unbounded', tier: 'unbounded', productCapDisabled: true },
|
|
6246
|
+
assistantMessage: 'full report',
|
|
6247
|
+
answer: 'full report',
|
|
6248
|
+
citations: [],
|
|
6249
|
+
recalledMemories: [],
|
|
6250
|
+
memoryActions: [],
|
|
6251
|
+
knowledgePoints: [],
|
|
6252
|
+
summary: {
|
|
6253
|
+
generatedAt: '2026-09-03T00:00:00.000Z',
|
|
6254
|
+
topK: 6,
|
|
6255
|
+
returnedKnowledgePoints: 0,
|
|
6256
|
+
returnedCitations: 0,
|
|
6257
|
+
recalledMemoryCount: 0,
|
|
6258
|
+
queryEvidenceCoverageRatioPct: 0,
|
|
6259
|
+
},
|
|
6260
|
+
},
|
|
6261
|
+
},
|
|
6262
|
+
}]);
|
|
6263
|
+
fetchMock.mockImplementationOnce(async () => response());
|
|
6264
|
+
Object.defineProperty(window.navigator, 'deviceMemory', { configurable: true, value: 8 });
|
|
6265
|
+
Object.defineProperty(window.navigator, 'hardwareConcurrency', { configurable: true, value: 8 });
|
|
6266
|
+
const budgetSelector = document.getElementById('agent-workspace-response-budget-select');
|
|
6267
|
+
expect(budgetSelector.value).toBe('adaptive');
|
|
6268
|
+
budgetSelector.value = 'unbounded';
|
|
6269
|
+
budgetSelector.dispatchEvent(new window.Event('change', { bubbles: true }));
|
|
6270
|
+
expect(window.localStorage.getItem('nc_agent_response_budget_mode')).toBe('unbounded');
|
|
6271
|
+
const input = document.getElementById('agent-workspace-chat-input');
|
|
6272
|
+
input.value = 'what is water glass?';
|
|
6273
|
+
await window.NoteConnectionAgentWorkspace.sendConversation();
|
|
6274
|
+
const desktopRequest = JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body || '{}'));
|
|
6275
|
+
expect(desktopRequest.responseBudgetMode).toBe('unbounded');
|
|
6276
|
+
expect(desktopRequest.responseBudgetCapability).toEqual({ memoryClass: 'high', workload: 'max' });
|
|
6277
|
+
window.__NC_RUNTIME_CAPS = { platform: 'capacitor-android', supports_sidecar: false };
|
|
6278
|
+
fetchMock.mockImplementationOnce(async () => response());
|
|
6279
|
+
window.NoteConnectionAgentWorkspace.init();
|
|
6280
|
+
const control = budgetSelector.closest('[data-agent-desktop-only]');
|
|
6281
|
+
expect(control.hidden).toBe(true);
|
|
6282
|
+
input.value = 'what is water glass?';
|
|
6283
|
+
await window.NoteConnectionAgentWorkspace.sendConversation();
|
|
6284
|
+
const mobileRequest = JSON.parse(String(fetchMock.mock.calls[1]?.[1]?.body || '{}'));
|
|
6285
|
+
expect(mobileRequest.responseBudgetMode).toBeUndefined();
|
|
6286
|
+
expect(mobileRequest.responseProfile).toBe('mobile_compact');
|
|
6287
|
+
});
|
|
6288
|
+
test('uses the bounded local exact path on native mobile and exposes only the compact projection', async () => {
|
|
6289
|
+
const { document, window, fetchMock, } = loadAgentWorkspaceHarness({ withI18n: true });
|
|
6290
|
+
if (!fetchMock) {
|
|
6291
|
+
throw new Error('expected fetch mock');
|
|
6292
|
+
}
|
|
6293
|
+
window.__NC_RUNTIME_CAPS = {
|
|
6294
|
+
platform: 'capacitor-android',
|
|
6295
|
+
supports_sidecar: false,
|
|
6296
|
+
};
|
|
6297
|
+
const matches = [
|
|
6298
|
+
{ id: 'amorphous_ice', label: '非晶冰', sourceUri: 'Knowledge_Base/waterglass/amorphous-ice.md' },
|
|
6299
|
+
{ id: 'water_structure', label: '水分子与氢键网络', sourceUri: 'Knowledge_Base/waterglass/amorphous-ice.md' },
|
|
6300
|
+
{ id: 'radial_distribution', label: '径向分布函数 g(r)', sourceUri: 'Knowledge_Base/waterglass/amorphous-ice.md' },
|
|
6301
|
+
];
|
|
6302
|
+
const queriedTerms = [];
|
|
6303
|
+
const queryKnowledgeBaseExact = jest.fn(async ({ query }) => {
|
|
6304
|
+
queriedTerms.push(query);
|
|
6305
|
+
return { matches: query === '非晶冰' ? matches : [] };
|
|
6306
|
+
});
|
|
6307
|
+
const readContent = jest.fn(async () => [
|
|
6308
|
+
'# 非晶冰',
|
|
6309
|
+
'',
|
|
6310
|
+
'非晶冰是水的一种缺乏长程有序的固态形式。',
|
|
6311
|
+
'',
|
|
6312
|
+
'## 常见用例与性能指标',
|
|
6313
|
+
'该章节不应进入移动端直接回答。',
|
|
6314
|
+
].join('\n'));
|
|
6315
|
+
window.NoteConnectionStorage = {
|
|
6316
|
+
createProvider: () => ({ queryKnowledgeBaseExact, readContent }),
|
|
6317
|
+
};
|
|
6318
|
+
const input = document.getElementById('agent-workspace-chat-input');
|
|
6319
|
+
input.value = '什么是非晶冰?我应该通过哪些知识点学习?';
|
|
6320
|
+
await window.NoteConnectionAgentWorkspace.sendConversation();
|
|
6321
|
+
expect(queryKnowledgeBaseExact).toHaveBeenCalled();
|
|
6322
|
+
expect(queriedTerms).toContain('非晶冰');
|
|
6323
|
+
expect(readContent).toHaveBeenCalledWith('Knowledge_Base/waterglass/amorphous-ice.md');
|
|
6324
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
6325
|
+
const result = window.__NC_LAST_AGENT_CONVERSATION_RESULT;
|
|
6326
|
+
expect(result).toEqual(expect.objectContaining({
|
|
6327
|
+
responseProfile: 'mobile_compact',
|
|
6328
|
+
mobileProjection: expect.objectContaining({
|
|
6329
|
+
primarySubject: '非晶冰',
|
|
6330
|
+
directAnswer: '非晶冰是水的一种缺乏长程有序的固态形式。',
|
|
6331
|
+
route: expect.arrayContaining([
|
|
6332
|
+
expect.objectContaining({ title: '非晶冰', role: 'core' }),
|
|
6333
|
+
expect.objectContaining({ title: '水分子与氢键网络' }),
|
|
6334
|
+
]),
|
|
6335
|
+
}),
|
|
6336
|
+
}));
|
|
6337
|
+
expect(result.knowledgePoints).toEqual([]);
|
|
6338
|
+
expect(result.citations).toEqual([]);
|
|
6339
|
+
expect(result.trace).toBeUndefined();
|
|
6340
|
+
expect(JSON.stringify(result)).not.toContain('该章节不应进入移动端直接回答');
|
|
6341
|
+
const renderedAssistant = Array.from(document.querySelectorAll('.agent-chat-message-assistant')).map((node) => String(node.textContent || '')).join('\n');
|
|
6342
|
+
expect(renderedAssistant).toContain('建议学习路径');
|
|
6343
|
+
expect(renderedAssistant).toContain('水分子与氢键网络');
|
|
6344
|
+
});
|
|
6345
|
+
test('falls back to the bounded remote projection when the native exact index has no match', async () => {
|
|
6346
|
+
const { document, window, fetchMock, } = loadAgentWorkspaceHarness({ withI18n: true });
|
|
6347
|
+
if (!fetchMock) {
|
|
6348
|
+
throw new Error('expected fetch mock');
|
|
6349
|
+
}
|
|
6350
|
+
window.__NC_RUNTIME_CAPS = {
|
|
6351
|
+
platform: 'capacitor-android',
|
|
6352
|
+
supports_sidecar: false,
|
|
6353
|
+
};
|
|
6354
|
+
const queryKnowledgeBaseExact = jest.fn(async () => ({ matches: [] }));
|
|
6355
|
+
window.NoteConnectionStorage = {
|
|
6356
|
+
createProvider: () => ({ queryKnowledgeBaseExact }),
|
|
6357
|
+
};
|
|
6358
|
+
fetchMock.mockImplementationOnce(async (_url, init) => {
|
|
6359
|
+
const request = JSON.parse(String(init?.body || '{}'));
|
|
6360
|
+
expect(request.responseProfile).toBe('mobile_compact');
|
|
6361
|
+
return createSseResponse([
|
|
6362
|
+
{
|
|
6363
|
+
event: 'turn_completed',
|
|
6364
|
+
payload: {
|
|
6365
|
+
type: 'turn_completed',
|
|
6366
|
+
turnId: 'turn_remote_mobile_projection',
|
|
6367
|
+
emittedAt: '2026-08-01T00:00:00.000Z',
|
|
6368
|
+
result: {
|
|
6369
|
+
responseProfile: 'mobile_compact',
|
|
6370
|
+
assistantMessage: '非晶冰是缺乏长程有序的水的固态形式。',
|
|
6371
|
+
answer: '非晶冰是缺乏长程有序的水的固态形式。',
|
|
6372
|
+
mobileProjection: {
|
|
6373
|
+
schemaVersion: 1,
|
|
6374
|
+
primarySubject: '非晶冰',
|
|
6375
|
+
directAnswer: '非晶冰是缺乏长程有序的水的固态形式。',
|
|
6376
|
+
route: [
|
|
6377
|
+
{ nodeId: 'water', title: '水分子与氢键网络', role: 'prerequisite', order: 1 },
|
|
6378
|
+
{ nodeId: 'ice', title: '非晶冰', role: 'core', order: 2 },
|
|
6379
|
+
],
|
|
6380
|
+
citations: [],
|
|
6381
|
+
},
|
|
6382
|
+
assistantBlocks: [],
|
|
6383
|
+
knowledgePoints: [],
|
|
6384
|
+
citations: [],
|
|
6385
|
+
recalledMemories: [],
|
|
6386
|
+
memoryActions: [],
|
|
6387
|
+
summary: {
|
|
6388
|
+
generatedAt: '2026-08-01T00:00:00.000Z',
|
|
6389
|
+
topK: 6,
|
|
6390
|
+
returnedKnowledgePoints: 2,
|
|
6391
|
+
returnedCitations: 0,
|
|
6392
|
+
recalledMemoryCount: 0,
|
|
6393
|
+
appliedMemoryCount: 0,
|
|
6394
|
+
queryEvidenceCoverageRatioPct: 100,
|
|
6395
|
+
},
|
|
6396
|
+
},
|
|
6397
|
+
},
|
|
6398
|
+
},
|
|
6399
|
+
]);
|
|
6400
|
+
});
|
|
6401
|
+
const input = document.getElementById('agent-workspace-chat-input');
|
|
6402
|
+
input.value = '什么是非晶冰?';
|
|
6403
|
+
await window.NoteConnectionAgentWorkspace.sendConversation();
|
|
6404
|
+
expect(queryKnowledgeBaseExact).toHaveBeenCalled();
|
|
6405
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
6406
|
+
expect(String(fetchMock.mock.calls[0]?.[1]?.headers?.Accept || '')).toBe('text/event-stream');
|
|
6407
|
+
const result = window.__NC_LAST_AGENT_CONVERSATION_RESULT;
|
|
6408
|
+
expect(result).toEqual(expect.objectContaining({
|
|
6409
|
+
responseProfile: 'mobile_compact',
|
|
6410
|
+
mobileProjection: expect.objectContaining({ primarySubject: '非晶冰' }),
|
|
6411
|
+
}));
|
|
6412
|
+
expect(result.trace).toBeUndefined();
|
|
6413
|
+
const renderedAssistant = Array.from(document.querySelectorAll('.agent-chat-message-assistant')).map((node) => String(node.textContent || '')).join('\n');
|
|
6414
|
+
expect(renderedAssistant).toContain('建议学习路径');
|
|
6415
|
+
expect(renderedAssistant).toContain('非晶冰');
|
|
6115
6416
|
});
|
|
6116
6417
|
test('prefers SSE turn streaming for conversation and renders the completed turn payload', async () => {
|
|
6117
6418
|
const { document, window, fetchMock, } = loadAgentWorkspaceHarness();
|
|
@@ -6669,6 +6970,9 @@ describe('agent workspace learning-path integration', () => {
|
|
|
6669
6970
|
}));
|
|
6670
6971
|
window.marked = {
|
|
6671
6972
|
parse: jest.fn((markdown) => {
|
|
6973
|
+
if (markdown.includes('Scoped Answer')) {
|
|
6974
|
+
return '<p>Scoped Answer with inline math $E=mc^2$ and display math $$\\frac{\\partial T}{\\partial t}=\\alpha\\nabla^2 T$$.</p>';
|
|
6975
|
+
}
|
|
6672
6976
|
if (markdown.includes('## Answer Context')) {
|
|
6673
6977
|
return ('<h2>Answer Context</h2>'
|
|
6674
6978
|
+ '<ul><li>Relevant knowledge points: <strong>1</strong></li></ul>');
|
|
@@ -6713,7 +7017,7 @@ describe('agent workspace learning-path integration', () => {
|
|
|
6713
7017
|
blockId: 'block_structured_answer_1',
|
|
6714
7018
|
type: 'structured_answer',
|
|
6715
7019
|
title: 'Grounded Answer',
|
|
6716
|
-
directAnswer: 'Scoped Answer',
|
|
7020
|
+
directAnswer: 'Scoped Answer with inline math $E=mc^2$ and display math $$\\frac{\\partial T}{\\partial t}=\\alpha\\nabla^2 T$$.',
|
|
6717
7021
|
overviewMarkdown: '## Answer Context\n\n- Relevant knowledge points: **1**\n- Citations returned: **1**\n- Scoped memories recalled: **0**',
|
|
6718
7022
|
explanationMarkdown: '## Explanation\n\n**Blocks Citation** is the current best scoped anchor.\n\nInline math $E=mc^2$ and a diagram:\n\n```mermaid\ngraph TD;A-->B;\n```',
|
|
6719
7023
|
evidenceMarkdown: '## Evidence Summary\n\n1. **Blocks Citation** (Knowledge_Base/optics/blocks.md:18)\n - Scoped snippet',
|
|
@@ -6836,6 +7140,12 @@ describe('agent workspace learning-path integration', () => {
|
|
|
6836
7140
|
},
|
|
6837
7141
|
],
|
|
6838
7142
|
},
|
|
7143
|
+
{
|
|
7144
|
+
blockId: 'block_knowledge_actions_1',
|
|
7145
|
+
type: 'knowledge_actions',
|
|
7146
|
+
title: 'Knowledge Actions',
|
|
7147
|
+
atomIds: ['atom_blocks_1'],
|
|
7148
|
+
},
|
|
6839
7149
|
],
|
|
6840
7150
|
citations: [
|
|
6841
7151
|
{
|
|
@@ -6905,13 +7215,45 @@ describe('agent workspace learning-path integration', () => {
|
|
|
6905
7215
|
expect(assistantNode?.querySelector('[data-structured-answer-section="overviewMarkdown"]')).toBeNull();
|
|
6906
7216
|
expect(assistantNode?.querySelector('[data-structured-answer-section="explanationMarkdown"]')).toBeNull();
|
|
6907
7217
|
expect(assistantNode?.querySelector('[data-structured-answer-section="evidenceMarkdown"]')).toBeNull();
|
|
7218
|
+
expect(assistantNode?.querySelector('[data-structured-answer-section="nextActionsMarkdown"]')).toBeNull();
|
|
6908
7219
|
expect(String(assistantNode?.textContent || '')).not.toContain('Answer Context');
|
|
6909
7220
|
expect(String(assistantNode?.textContent || '')).not.toContain('Evidence Summary');
|
|
7221
|
+
expect(String(assistantNode?.textContent || '')).not.toContain('Explanation');
|
|
6910
7222
|
expect(String(assistantNode?.textContent || '')).not.toContain('Knowledge Run');
|
|
7223
|
+
expect(String(assistantNode?.textContent || '')).not.toContain('Knowledge Actions');
|
|
6911
7224
|
expect(String(assistantNode?.textContent || '')).not.toContain('Inspect Run');
|
|
6912
|
-
expect(
|
|
6913
|
-
expect(
|
|
7225
|
+
expect(String(assistantNode?.querySelector('[data-structured-answer-section="directAnswer"]')?.textContent || '')).toContain('$E=mc^2$');
|
|
7226
|
+
expect(String(assistantNode?.querySelector('[data-structured-answer-section="directAnswer"]')?.textContent || '')).toContain('$$\\frac{\\partial T}{\\partial t}=\\alpha\\nabla^2 T$$');
|
|
7227
|
+
expect(renderMathInElement).toHaveBeenCalledTimes(1);
|
|
7228
|
+
expect(window.mermaid.initialize).toHaveBeenCalledTimes(1);
|
|
6914
7229
|
expect(mermaidRender).not.toHaveBeenCalled();
|
|
7230
|
+
const nextActionsToggle = assistantNode?.querySelector('[data-structured-answer-next-actions-toggle]');
|
|
7231
|
+
expect(nextActionsToggle).not.toBeNull();
|
|
7232
|
+
expect(nextActionsToggle?.getAttribute('aria-expanded')).toBe('false');
|
|
7233
|
+
expect(nextActionsToggle?.getAttribute('aria-label')).toBe('Show next actions');
|
|
7234
|
+
expect(assistantNode?.querySelector('[data-structured-answer-next-actions-panel]')).toBeNull();
|
|
7235
|
+
nextActionsToggle?.click();
|
|
7236
|
+
await Promise.resolve();
|
|
7237
|
+
expect(nextActionsToggle?.getAttribute('aria-expanded')).toBe('true');
|
|
7238
|
+
expect(nextActionsToggle?.getAttribute('aria-label')).toBe('Hide next actions');
|
|
7239
|
+
expect(assistantNode?.querySelector('[data-structured-answer-next-actions-panel]')).not.toBeNull();
|
|
7240
|
+
expect(String(assistantNode?.textContent || '')).toContain('Next Actions');
|
|
7241
|
+
expect(renderMathInElement).toHaveBeenCalled();
|
|
7242
|
+
nextActionsToggle?.click();
|
|
7243
|
+
expect(nextActionsToggle?.getAttribute('aria-expanded')).toBe('false');
|
|
7244
|
+
expect(assistantNode?.querySelector('[data-structured-answer-next-actions-panel]')).toBeNull();
|
|
7245
|
+
const markdownRuntime = window.NoteConnectionMarkdownRuntime;
|
|
7246
|
+
const renderMarkdownInto = markdownRuntime.renderMarkdownInto;
|
|
7247
|
+
markdownRuntime.renderMarkdownInto = jest.fn((container, markdown) => {
|
|
7248
|
+
if (markdown.includes('## Next Actions')) {
|
|
7249
|
+
return Promise.reject(new Error('next actions renderer failure'));
|
|
7250
|
+
}
|
|
7251
|
+
return renderMarkdownInto(container, markdown);
|
|
7252
|
+
});
|
|
7253
|
+
nextActionsToggle?.click();
|
|
7254
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
7255
|
+
expect(nextActionsToggle?.getAttribute('aria-expanded')).toBe('false');
|
|
7256
|
+
expect(assistantNode?.querySelector('[data-structured-answer-next-actions-panel]')).toBeNull();
|
|
6915
7257
|
expect(window.NoteConnectionAgentWorkspace.getLastConversationResult()?.assistantBlocks?.[0]).toEqual(expect.objectContaining({
|
|
6916
7258
|
overviewMarkdown: expect.stringContaining('## Answer Context'),
|
|
6917
7259
|
evidenceMarkdown: expect.stringContaining('## Evidence Summary'),
|
|
@@ -6924,6 +7266,63 @@ describe('agent workspace learning-path integration', () => {
|
|
|
6924
7266
|
]),
|
|
6925
7267
|
}));
|
|
6926
7268
|
});
|
|
7269
|
+
test('keeps non-public assistant blocks out of the chat release surface', async () => {
|
|
7270
|
+
const { document, window, fetchMock } = loadAgentWorkspaceHarness({ withI18n: true });
|
|
7271
|
+
if (!fetchMock) {
|
|
7272
|
+
throw new Error('expected fetch mock');
|
|
7273
|
+
}
|
|
7274
|
+
fetchMock.mockImplementationOnce(async () => createSseResponse([
|
|
7275
|
+
{
|
|
7276
|
+
event: 'turn_completed',
|
|
7277
|
+
payload: {
|
|
7278
|
+
type: 'turn_completed',
|
|
7279
|
+
turnId: 'turn_public_projection_only',
|
|
7280
|
+
emittedAt: '2026-04-13T00:02:00.000Z',
|
|
7281
|
+
result: {
|
|
7282
|
+
assistantMessage: 'Grounded Answer fallback',
|
|
7283
|
+
answer: 'Grounded Answer fallback',
|
|
7284
|
+
assistantBlocks: [
|
|
7285
|
+
{
|
|
7286
|
+
blockId: 'structured_public',
|
|
7287
|
+
type: 'structured_answer',
|
|
7288
|
+
title: 'Grounded Answer',
|
|
7289
|
+
directAnswer: 'Public answer.',
|
|
7290
|
+
knowledgePointCount: 0,
|
|
7291
|
+
citationCount: 0,
|
|
7292
|
+
recalledMemoryCount: 0,
|
|
7293
|
+
},
|
|
7294
|
+
{ blockId: 'metadata_markdown', type: 'main_markdown', markdown: '# Answer Context' },
|
|
7295
|
+
{ blockId: 'metadata_html', type: 'html_artifact', title: 'Evidence Summary', html: '<p>leak</p>' },
|
|
7296
|
+
{ blockId: 'metadata_notice', type: 'system_notice', text: 'internal notice' },
|
|
7297
|
+
{ blockId: 'metadata_citations', type: 'citations', title: 'Citations', citations: [] },
|
|
7298
|
+
{ blockId: 'metadata_actions', type: 'knowledge_actions', title: 'Knowledge Actions', atomIds: [] },
|
|
7299
|
+
],
|
|
7300
|
+
knowledgePoints: [],
|
|
7301
|
+
citations: [],
|
|
7302
|
+
recalledMemories: [],
|
|
7303
|
+
memoryActions: [],
|
|
7304
|
+
summary: { generatedAt: '2026-04-13T00:02:00.000Z', topK: 6, returnedKnowledgePoints: 0, returnedCitations: 0, recalledMemoryCount: 0, appliedMemoryCount: 0, queryEvidenceCoverageRatioPct: 100 },
|
|
7305
|
+
trace: {},
|
|
7306
|
+
},
|
|
7307
|
+
},
|
|
7308
|
+
},
|
|
7309
|
+
]));
|
|
7310
|
+
const input = document.getElementById('agent-workspace-chat-input');
|
|
7311
|
+
input.value = 'public projection';
|
|
7312
|
+
await window.NoteConnectionAgentWorkspace.sendConversation();
|
|
7313
|
+
const renderedMessages = document.querySelectorAll('.agent-chat-message-rendered.agent-chat-message-assistant');
|
|
7314
|
+
expect(renderedMessages).toHaveLength(1);
|
|
7315
|
+
const assistantNode = renderedMessages[0];
|
|
7316
|
+
expect(assistantNode.querySelectorAll('.agent-chat-structured-answer-card')).toHaveLength(1);
|
|
7317
|
+
expect(assistantNode.querySelectorAll('.agent-chat-render-block-main_markdown')).toHaveLength(0);
|
|
7318
|
+
expect(assistantNode.querySelectorAll('.agent-chat-inline-artifact')).toHaveLength(0);
|
|
7319
|
+
expect(assistantNode.querySelectorAll('.agent-chat-knowledge-run-card')).toHaveLength(0);
|
|
7320
|
+
expect(assistantNode.textContent).toContain('Public answer.');
|
|
7321
|
+
expect(assistantNode.textContent).not.toContain('Answer Context');
|
|
7322
|
+
expect(assistantNode.textContent).not.toContain('Evidence Summary');
|
|
7323
|
+
expect(assistantNode.textContent).not.toContain('internal notice');
|
|
7324
|
+
expect(assistantNode.textContent).not.toContain('Knowledge Actions');
|
|
7325
|
+
});
|
|
6927
7326
|
test('inspects durable knowledge-run artifacts from structured conversation blocks', async () => {
|
|
6928
7327
|
const { document, window, fetchMock, } = loadAgentWorkspaceHarness({ withI18n: true });
|
|
6929
7328
|
if (!fetchMock) {
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("fs"));
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
describe('Android knowledge-base picker contract', () => {
|
|
39
|
+
const repoRoot = path.resolve(__dirname, '..');
|
|
40
|
+
const templatePath = path.join(repoRoot, 'src-tauri', 'mobile', 'android', 'KnowledgeBasePickerBridge.kt');
|
|
41
|
+
const patcherPath = path.join(repoRoot, 'scripts', 'apply-tauri-android-pathmode.js');
|
|
42
|
+
const rustPath = path.join(repoRoot, 'src-tauri', 'src', 'lib.rs');
|
|
43
|
+
const sourceManagerPath = path.join(repoRoot, 'src', 'frontend', 'source_manager.js');
|
|
44
|
+
test('uses SAF tree permissions and bounded app-local streaming import', () => {
|
|
45
|
+
const template = fs.readFileSync(templatePath, 'utf8');
|
|
46
|
+
expect(template).toContain('ACTION_OPEN_DOCUMENT_TREE');
|
|
47
|
+
expect(template).toContain('FLAG_GRANT_PERSISTABLE_URI_PERMISSION');
|
|
48
|
+
expect(template).toContain('takePersistableUriPermission');
|
|
49
|
+
expect(template).toContain('context.filesDir, "Knowledge_Base"');
|
|
50
|
+
expect(template).toContain('MAX_DOCUMENT_BYTES');
|
|
51
|
+
expect(template).toContain('MAX_TOTAL_BYTES');
|
|
52
|
+
expect(template).toContain('input.copyTo(output, 32 * 1024)');
|
|
53
|
+
expect(template).toContain('replaceImportedTree');
|
|
54
|
+
expect(template).toContain('Duplicate import path');
|
|
55
|
+
expect(template).toContain('knowledge_base_import_journal.v1.json');
|
|
56
|
+
expect(template).toContain('recoverImportTransaction(activity.applicationContext)');
|
|
57
|
+
expect(template).toContain('target-backed-up');
|
|
58
|
+
expect(template).toContain('target-activated');
|
|
59
|
+
expect(template).toContain('invalid_import_journal');
|
|
60
|
+
expect(template).toContain('output.fd.sync()');
|
|
61
|
+
expect(template).toContain('isSafeTransactionPath');
|
|
62
|
+
expect(template).toContain('isKnownJournalPhase');
|
|
63
|
+
expect(template).toContain('recovered_previous');
|
|
64
|
+
expect(template).toContain('importInFlight');
|
|
65
|
+
expect(template).toContain('import_in_progress');
|
|
66
|
+
expect(template).toContain('synchronized(importLock)');
|
|
67
|
+
expect(template).toContain('Retaining import backup for startup recovery');
|
|
68
|
+
expect(template).toContain('if (!backupRoot.exists())');
|
|
69
|
+
expect(template).toContain('import_recovery_pending');
|
|
70
|
+
expect(template).toContain('Unable to restore import backup; retaining journal for retry');
|
|
71
|
+
expect(template).toContain('orphan_recovery_pending');
|
|
72
|
+
const importFailureCatch = template.match(/Log\.e\(TAG, "Knowledge base import failed"[\s\S]*?writeResult\(resultFile, "failed", "", error\.message \?: "import_failed"\)/)?.[0];
|
|
73
|
+
expect(importFailureCatch).toBeDefined();
|
|
74
|
+
expect(importFailureCatch).not.toContain('backupRoot.deleteRecursively()');
|
|
75
|
+
expect(template).not.toContain('readText()');
|
|
76
|
+
});
|
|
77
|
+
test('patches picker bridge into mobile-slim without enabling Godot', () => {
|
|
78
|
+
const patcher = fs.readFileSync(patcherPath, 'utf8');
|
|
79
|
+
expect(patcher).toContain('KnowledgeBasePickerBridge.kt');
|
|
80
|
+
expect(patcher).toContain('patchKnowledgeBasePicker');
|
|
81
|
+
expect(patcher).toContain('KnowledgeBasePickerBridge.bindActivity(this)');
|
|
82
|
+
expect(patcher).toContain('removePathmodeAssets');
|
|
83
|
+
});
|
|
84
|
+
test('exposes additive request/poll commands and enables the Android capability', () => {
|
|
85
|
+
const rust = fs.readFileSync(rustPath, 'utf8');
|
|
86
|
+
const sourceManager = fs.readFileSync(sourceManagerPath, 'utf8');
|
|
87
|
+
expect(rust).toContain('request_android_knowledge_base_picker');
|
|
88
|
+
expect(rust).toContain('consume_android_knowledge_base_picker_result');
|
|
89
|
+
expect(rust).toContain('kb_import_mode: "android-saf-copy"');
|
|
90
|
+
expect(sourceManager).toContain("request_kb_path_change");
|
|
91
|
+
expect(sourceManager).toContain("poll_kb_path_change");
|
|
92
|
+
expect(sourceManager).toContain('Timed out waiting for Android knowledge base import.');
|
|
93
|
+
});
|
|
94
|
+
test('keeps Android slim packaging size-first and clears stale generated outputs', () => {
|
|
95
|
+
const runner = fs.readFileSync(path.join(repoRoot, 'scripts', 'run-tauri-android.js'), 'utf8');
|
|
96
|
+
expect(runner).toContain("CARGO_PROFILE_RELEASE_OPT_LEVEL || 'z'");
|
|
97
|
+
expect(runner).toContain('CARGO_PROFILE_RELEASE_CODEGEN_UNITS, 1');
|
|
98
|
+
expect(runner).toContain("CARGO_PROFILE_RELEASE_LTO || 'thin'");
|
|
99
|
+
expect(runner).toContain('cleanAndroidSlimOutputs');
|
|
100
|
+
expect(runner).toContain('!includeGodotPathmode');
|
|
101
|
+
});
|
|
102
|
+
});
|