noteconnection 1.6.8 → 1.8.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.
Files changed (212) hide show
  1. package/LICENSE +674 -21
  2. package/README.md +258 -64
  3. package/dist/src/agent_workspace.contract.parity.test.js +475 -0
  4. package/dist/src/agent_workspace.frontend.test.js +9989 -0
  5. package/dist/src/agent_workspace.locale.contract.test.js +95 -0
  6. package/dist/src/agent_workspace.runtime.behavior.test.js +5072 -0
  7. package/dist/src/copy.assets.contract.test.js +130 -0
  8. package/dist/src/core/PathBridge.js +28 -3
  9. package/dist/src/export/WorkspaceExportBundle.js +1130 -0
  10. package/dist/src/export/WorkspaceExportBundle.test.js +1480 -0
  11. package/dist/src/export/index.js +18 -0
  12. package/dist/src/export/types.js +2 -0
  13. package/dist/src/fixrisk.issue.verifier.contract.test.js +3 -1
  14. package/dist/src/foundation.ann.runtime.contract.test.js +81 -0
  15. package/dist/src/foundation.release.evidence.contract.test.js +406 -0
  16. package/dist/src/foundation.sqlite.runtime.contract.test.js +82 -0
  17. package/dist/src/frontend/README.md +258 -64
  18. package/dist/src/frontend/agent_workspace.js +5353 -0
  19. package/dist/src/frontend/agent_workspace_runtime.js +4434 -0
  20. package/dist/src/frontend/analysis.js +31 -4
  21. package/dist/src/frontend/app.js +3597 -141
  22. package/dist/src/frontend/focus_mode_interactions.js +33 -0
  23. package/dist/src/frontend/godot_future_path_renderer.js +540 -0
  24. package/dist/src/frontend/godot_tree_interactions.js +178 -0
  25. package/dist/src/frontend/graph_state.mjs +105 -0
  26. package/dist/src/frontend/hosted_future_path_runtime.js +157 -0
  27. package/dist/src/frontend/i18n.mjs +186 -0
  28. package/dist/src/frontend/index.html +641 -266
  29. package/dist/src/frontend/layout_gpu.js +12 -4
  30. package/dist/src/frontend/locales/en.json +721 -5
  31. package/dist/src/frontend/locales/zh.json +721 -5
  32. package/dist/src/frontend/main.mjs +60 -0
  33. package/dist/src/frontend/markdown_runtime.js +827 -0
  34. package/dist/src/frontend/notemd.css +49 -0
  35. package/dist/src/frontend/notemd.html +7 -1
  36. package/dist/src/frontend/notemd.js +64 -0
  37. package/dist/src/frontend/path.html +107 -0
  38. package/dist/src/frontend/path_app.js +2189 -150
  39. package/dist/src/frontend/path_layout.mjs +143 -0
  40. package/dist/src/frontend/path_mermaid_utils.mjs +108 -0
  41. package/dist/src/frontend/path_modules_bridge.js +486 -0
  42. package/dist/src/frontend/path_state.mjs +118 -0
  43. package/dist/src/frontend/path_styles.css +146 -0
  44. package/dist/src/frontend/path_worker_bridge.mjs +85 -0
  45. package/dist/src/frontend/reader.js +522 -27
  46. package/dist/src/frontend/runtime_bridge.js +67 -54
  47. package/dist/src/frontend/runtime_bridge.mjs +279 -0
  48. package/dist/src/frontend/settings.js +130 -12
  49. package/dist/src/frontend/simulationWorker.js +241 -6
  50. package/dist/src/frontend/source_manager.js +190 -21
  51. package/dist/src/frontend/styles.css +2853 -72
  52. package/dist/src/frontend/workbench_state.mjs +101 -0
  53. package/dist/src/frontend/workspace_panes.js +10168 -0
  54. package/dist/src/frontend.locale.contract.test.js +62 -0
  55. package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
  56. package/dist/src/indexing/IndexLifecycle.js +195 -0
  57. package/dist/src/indexing/IndexLifecycle.test.js +49 -0
  58. package/dist/src/indexing/SegmentBuilder.js +64 -0
  59. package/dist/src/indexing/UnitBuilder.js +48 -0
  60. package/dist/src/indexing/types.js +2 -0
  61. package/dist/src/knowledge.api.contract.test.js +170 -0
  62. package/dist/src/learning/KnowledgeLearningPlatform.js +10386 -0
  63. package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +327 -0
  64. package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
  65. package/dist/src/learning/KnowledgeLearningPlatform.test.js +2971 -0
  66. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2974 -0
  67. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3928 -0
  68. package/dist/src/learning/answerReleaseReview.js +4319 -0
  69. package/dist/src/learning/answerReleaseReview.test.js +2888 -0
  70. package/dist/src/learning/api.js +2 -0
  71. package/dist/src/learning/conversationComposer.js +1480 -0
  72. package/dist/src/learning/conversationComposer.test.js +1817 -0
  73. package/dist/src/learning/domains/ConversationManager.js +53 -0
  74. package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
  75. package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
  76. package/dist/src/learning/domains/MasteryEngine.js +387 -0
  77. package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
  78. package/dist/src/learning/domains/QualityEvaluator.js +307 -0
  79. package/dist/src/learning/domains/TutorRouter.js +313 -0
  80. package/dist/src/learning/domains/index.js +33 -0
  81. package/dist/src/learning/domains/types.js +7 -0
  82. package/dist/src/learning/errors.js +29 -0
  83. package/dist/src/learning/evidenceContextAssembler.js +1176 -0
  84. package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
  85. package/dist/src/learning/graphContextAssembler.js +870 -0
  86. package/dist/src/learning/graphContextAssembler.test.js +1033 -0
  87. package/dist/src/learning/index.js +28 -0
  88. package/dist/src/learning/queryBackend.js +1898 -0
  89. package/dist/src/learning/queryBackend.test.js +955 -0
  90. package/dist/src/learning/ragContextPack.js +257 -0
  91. package/dist/src/learning/ragContextPack.test.js +160 -0
  92. package/dist/src/learning/ragPublicText.js +38 -0
  93. package/dist/src/learning/ragSufficiencyJudge.js +161 -0
  94. package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
  95. package/dist/src/learning/ragSufficiencyProviderJudge.js +227 -0
  96. package/dist/src/learning/ragSufficiencyProviderJudge.test.js +156 -0
  97. package/dist/src/learning/requestNormalization.js +198 -0
  98. package/dist/src/learning/runtimeCapability.js +4677 -0
  99. package/dist/src/learning/runtimeCapability.test.js +3635 -0
  100. package/dist/src/learning/store.js +1240 -0
  101. package/dist/src/learning/store.test.js +1126 -0
  102. package/dist/src/learning/tutorAdapter.js +2 -0
  103. package/dist/src/learning/types.js +2 -0
  104. package/dist/src/learning/vectorAccelerationAdapter.js +942 -0
  105. package/dist/src/learning/vectorAccelerationAdapter.test.js +382 -0
  106. package/dist/src/lfs.asset.policy.contract.test.js +153 -0
  107. package/dist/src/license.policy.contract.test.js +66 -0
  108. package/dist/src/memory/MemoryGovernance.js +74 -0
  109. package/dist/src/memory/MemoryGovernance.test.js +46 -0
  110. package/dist/src/memory/types.js +2 -0
  111. package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
  112. package/dist/src/middleware/auth.js +17 -0
  113. package/dist/src/middleware/body-parser.js +45 -0
  114. package/dist/src/middleware/cors.js +44 -0
  115. package/dist/src/middleware/index.js +21 -0
  116. package/dist/src/middleware/request-trace.js +96 -0
  117. package/dist/src/notemd/AppConfigToml.js +6 -4
  118. package/dist/src/notemd/MermaidProcessor.js +400 -50
  119. package/dist/src/notemd/NotemdService.js +498 -13
  120. package/dist/src/notemd/PromptManager.js +15 -0
  121. package/dist/src/notemd/cli/commands.js +357 -0
  122. package/dist/src/notemd/cli/dispatcher.js +225 -0
  123. package/dist/src/notemd/cli/index.js +169 -0
  124. package/dist/src/notemd/cli/parser.js +68 -0
  125. package/dist/src/notemd/cli/types.js +2 -0
  126. package/dist/src/notemd/constants.js +43 -0
  127. package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
  128. package/dist/src/notemd/diagram/diagramSpec.js +79 -0
  129. package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
  130. package/dist/src/notemd/diagram/intent.js +95 -0
  131. package/dist/src/notemd/diagram/planner.js +71 -0
  132. package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
  133. package/dist/src/notemd/diagram/types.js +18 -0
  134. package/dist/src/notemd/index.js +26 -0
  135. package/dist/src/notemd/operations/capabilityManifest.js +23 -0
  136. package/dist/src/notemd/operations/cliContracts.js +17 -0
  137. package/dist/src/notemd/operations/configProfileCommands.js +85 -0
  138. package/dist/src/notemd/operations/registry.contract.test.js +95 -0
  139. package/dist/src/notemd/operations/registry.js +991 -0
  140. package/dist/src/notemd/operations/types.js +2 -0
  141. package/dist/src/notemd/providerDiagnostics.js +220 -0
  142. package/dist/src/notemd/providerProfiles.js +42 -0
  143. package/dist/src/notemd/providerTemplates.js +231 -0
  144. package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
  145. package/dist/src/notemd/search/SearchManager.js +13 -0
  146. package/dist/src/notemd/search/SearchProvider.js +2 -0
  147. package/dist/src/notemd/search/TavilyProvider.js +44 -0
  148. package/dist/src/notemd.agent.manifest.test.js +85 -0
  149. package/dist/src/notemd.api.contract.test.js +14 -1
  150. package/dist/src/notemd.app_config_toml.test.js +2 -1
  151. package/dist/src/notemd.batch.workflow.test.js +117 -0
  152. package/dist/src/notemd.cli.e2e.test.js +136 -0
  153. package/dist/src/notemd.core.test.js +51 -0
  154. package/dist/src/notemd.diagram.pipeline.test.js +233 -0
  155. package/dist/src/notemd.providerTemplates.test.js +34 -0
  156. package/dist/src/notemd.server.integration.test.js +143 -35
  157. package/dist/src/notemd.workflow.pipeline.test.js +162 -0
  158. package/dist/src/pathbridge.handshake.contract.test.js +16 -2
  159. package/dist/src/pathmode.background.contract.test.js +69 -0
  160. package/dist/src/pathmode.settings.api.contract.test.js +9 -0
  161. package/dist/src/pkg.sidecar.contract.test.js +9 -3
  162. package/dist/src/platform/ExportProfile.js +58 -0
  163. package/dist/src/platform/PlatformCapabilities.js +45 -0
  164. package/dist/src/platform/PlatformCapabilities.test.js +30 -0
  165. package/dist/src/platform/RenderMaterializer.js +33 -0
  166. package/dist/src/platform/RenderMaterializer.test.js +32 -0
  167. package/dist/src/query_backend.external_http.integration.test.js +410 -0
  168. package/dist/src/reader_renderer.js +404 -3
  169. package/dist/src/reader_renderer.test.js +87 -0
  170. package/dist/src/release.godot.mirror.contract.test.js +73 -0
  171. package/dist/src/resources/ResourceRegistry.js +223 -0
  172. package/dist/src/resources/ResourceRegistry.test.js +61 -0
  173. package/dist/src/resources/types.js +2 -0
  174. package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
  175. package/dist/src/routes/data.js +267 -0
  176. package/dist/src/routes/diagnostics.js +51 -0
  177. package/dist/src/routes/index.js +23 -0
  178. package/dist/src/routes/knowledge.js +968 -0
  179. package/dist/src/routes/markdown.js +287 -0
  180. package/dist/src/routes/notemd.js +565 -0
  181. package/dist/src/routes/registry.contract.test.js +130 -0
  182. package/dist/src/routes/render.js +285 -0
  183. package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
  184. package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
  185. package/dist/src/routes/settings.js +6 -0
  186. package/dist/src/routes/staticFiles.js +94 -0
  187. package/dist/src/routes/types.js +2 -0
  188. package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
  189. package/dist/src/server.js +11090 -1549
  190. package/dist/src/server.migration.test.js +193 -21
  191. package/dist/src/server.port.fallback.contract.test.js +63 -0
  192. package/dist/src/session/SessionStateStore.js +81 -0
  193. package/dist/src/session/SessionStateStore.test.js +58 -0
  194. package/dist/src/session/types.js +2 -0
  195. package/dist/src/settings.runtime.contract.test.js +50 -0
  196. package/dist/src/shared/types.contract.test.js +107 -0
  197. package/dist/src/shared/types.js +22 -0
  198. package/dist/src/sidecar.replacement.boundary.contract.test.js +128 -0
  199. package/dist/src/sidecar.supply.readiness.contract.test.js +144 -0
  200. package/dist/src/source_manager.loadflow.test.js +46 -0
  201. package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
  202. package/dist/src/tauri.frontend.build.contract.test.js +60 -0
  203. package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
  204. package/dist/src/utils/RuntimePaths.js +4 -13
  205. package/dist/src/utils/platform.js +153 -0
  206. package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
  207. package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
  208. package/dist/src/workflows/types.js +2 -0
  209. package/dist/src/workspace/WorkspaceRegistry.js +122 -0
  210. package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
  211. package/dist/src/workspace/types.js +2 -0
  212. package/package.json +61 -10
@@ -0,0 +1,4434 @@
1
+ (
2
+ function bootstrapAgentWorkspaceRuntime(globalScope, factory) {
3
+ const runtimeModule = factory(globalScope);
4
+ if (typeof module !== 'undefined' && module.exports) {
5
+ module.exports = runtimeModule;
6
+ }
7
+ if (globalScope && typeof globalScope === 'object') {
8
+ globalScope.NoteConnectionAgentWorkspaceRuntime = runtimeModule;
9
+ }
10
+ }
11
+ )(
12
+ typeof globalThis !== 'undefined'
13
+ ? globalThis
14
+ : (typeof window !== 'undefined' ? window : this),
15
+ function agentWorkspaceRuntimeFactory(globalScope) {
16
+ const BODY_CLASS_ENABLED = 'agent-workspace-enabled';
17
+ const BODY_CLASS_PATH_VISIBLE = 'agent-workspace-path-visible';
18
+ const BODY_CLASS_PATH_FULLSCREEN = 'agent-workspace-path-fullscreen';
19
+ const PATH_DOCK_CLASS = 'agent-workspace-path-docked';
20
+ const MAX_ACTIVE_POINT_CONTEXTUAL_ACTIONS = 2;
21
+ const MAX_DIAGNOSTIC_TURNS = 120;
22
+ const MAX_DIAGNOSTIC_CAPABILITY_EVENTS = 120;
23
+ const DIAGNOSTIC_RUNBOOK_LINKS = Object.freeze([
24
+ Object.freeze({
25
+ id: 'development-progress-dashboard',
26
+ label: 'Development Progress Dashboard',
27
+ paths: Object.freeze({
28
+ en: 'docs/diataxis/en/explanation/development-progress-dashboard.md',
29
+ zh: 'docs/diataxis/zh/explanation/development-progress-dashboard.md',
30
+ }),
31
+ }),
32
+ Object.freeze({
33
+ id: 'agent-conversation-focus-mode-plan',
34
+ label: 'Agent Conversation + Focus Mode Delivery Plan',
35
+ paths: Object.freeze({
36
+ en: 'docs/diataxis/en/explanation/agent-conversation-focus-mode-plan.md',
37
+ zh: 'docs/diataxis/zh/explanation/agent-conversation-focus-mode-plan.md',
38
+ }),
39
+ }),
40
+ Object.freeze({
41
+ id: 'm7-direction-requirements',
42
+ label: 'Mainline CI Stabilization and M7 Direction Requirements',
43
+ paths: Object.freeze({
44
+ en: 'docs/brainstorms/2026-04-16-mainline-ci-stabilization-and-m7-direction-requirements.md',
45
+ zh: 'docs/brainstorms/2026-04-16-mainline-ci-stabilization-and-m7-direction-requirements.md',
46
+ }),
47
+ }),
48
+ Object.freeze({
49
+ id: 'foundation-reentry-readiness-checklist',
50
+ label: 'Foundation Re-entry Readiness Checklist',
51
+ paths: Object.freeze({
52
+ en: 'docs/diataxis/en/explanation/foundation-reentry-readiness-checklist.md',
53
+ zh: 'docs/diataxis/zh/explanation/foundation-reentry-readiness-checklist.md',
54
+ }),
55
+ }),
56
+ ]);
57
+ const SUPPORTED_TUTOR_ACTION_KINDS = Object.freeze([
58
+ 'generate_quiz',
59
+ 'analyze_answer',
60
+ 'follow_up',
61
+ 'generate_transfer',
62
+ 'generate_counterexample',
63
+ 'recap',
64
+ ]);
65
+ const SUPPORTED_MEMORY_POLICY_LAYERS = Object.freeze([
66
+ 'session',
67
+ 'unit',
68
+ 'long_term',
69
+ ]);
70
+ const SUPPORTED_MEMORY_POLICY_OPERATIONS = Object.freeze([
71
+ 'read',
72
+ 'snapshot',
73
+ 'retrain_plan',
74
+ 'write',
75
+ 'evict',
76
+ ]);
77
+ const SUPPORTED_READONLY_MEMORY_POLICY_OPERATIONS = Object.freeze([
78
+ 'read',
79
+ 'snapshot',
80
+ 'retrain_plan',
81
+ ]);
82
+ const MANAGED_CONVERSATION_ACTION_IDS = Object.freeze([
83
+ 'inspect_managed_memory_state',
84
+ 'write_memory_note',
85
+ 'record_memory_correction',
86
+ 'evict_memory_note',
87
+ ]);
88
+ const STUDY_LOOP_ACTION_IDS = Object.freeze([
89
+ 'generate_quiz',
90
+ 'analyze_answer',
91
+ 'recap',
92
+ 'follow_up',
93
+ 'generate_transfer',
94
+ 'generate_counterexample',
95
+ 'build_study_session',
96
+ ]);
97
+ const MEMORY_ACTION_IDS = Object.freeze([
98
+ 'inspect_memory_snapshot',
99
+ 'inspect_unit_memory_snapshot',
100
+ 'inspect_long_term_memory_snapshot',
101
+ 'inspect_memory_retrain_plan',
102
+ 'inspect_memory_read',
103
+ 'inspect_long_term_memory_read',
104
+ 'inspect_managed_memory_state',
105
+ 'write_memory_note',
106
+ 'record_memory_correction',
107
+ 'evict_memory_note',
108
+ ]);
109
+ const HISTORY_FOLLOW_UP_CANDIDATES_BY_ACTION_ID = Object.freeze({
110
+ open_learning_path: Object.freeze([
111
+ 'open_focus_mode',
112
+ 'build_study_session',
113
+ 'recap',
114
+ 'follow_up',
115
+ ]),
116
+ build_study_session: Object.freeze([
117
+ 'recap',
118
+ 'follow_up',
119
+ 'generate_transfer',
120
+ 'generate_quiz',
121
+ 'analyze_answer',
122
+ 'open_learning_path',
123
+ 'open_focus_mode',
124
+ ]),
125
+ recap: Object.freeze([
126
+ 'follow_up',
127
+ 'generate_transfer',
128
+ 'generate_counterexample',
129
+ 'generate_quiz',
130
+ 'build_study_session',
131
+ 'open_learning_path',
132
+ 'open_focus_mode',
133
+ ]),
134
+ });
135
+ const DEFAULT_HISTORY_FOLLOW_UP_CANDIDATES = Object.freeze([
136
+ 'build_study_session',
137
+ 'recap',
138
+ 'follow_up',
139
+ 'open_learning_path',
140
+ 'open_focus_mode',
141
+ ]);
142
+
143
+ function createEmptyMemoryPolicyLayerCounts() {
144
+ return {
145
+ session: 0,
146
+ unit: 0,
147
+ long_term: 0,
148
+ };
149
+ }
150
+
151
+ function createEmptyMemoryPolicyOperationCounts() {
152
+ return {
153
+ read: 0,
154
+ snapshot: 0,
155
+ retrain_plan: 0,
156
+ write: 0,
157
+ evict: 0,
158
+ };
159
+ }
160
+
161
+ function trimString(value) {
162
+ return typeof value === 'string' ? value.trim() : '';
163
+ }
164
+
165
+ function normalizeMemoryPolicyLayerForDiagnostics(rawLayer) {
166
+ const layer = trimString(rawLayer);
167
+ return SUPPORTED_MEMORY_POLICY_LAYERS.includes(layer) ? layer : '';
168
+ }
169
+
170
+ function normalizeMemoryPolicyOperationForDiagnostics(rawOperation) {
171
+ const operation = trimString(rawOperation);
172
+ return SUPPORTED_MEMORY_POLICY_OPERATIONS.includes(operation) ? operation : '';
173
+ }
174
+
175
+ function isReadonlyMemoryPolicyOperation(rawOperation) {
176
+ const operation = normalizeMemoryPolicyOperationForDiagnostics(rawOperation);
177
+ return SUPPORTED_READONLY_MEMORY_POLICY_OPERATIONS.includes(operation);
178
+ }
179
+
180
+ function resolveTutorActionKind(rawActionKind) {
181
+ const actionKind = trimString(rawActionKind);
182
+ if (!actionKind) {
183
+ throw new Error('execute_tutor_action requires request.actionKind.');
184
+ }
185
+ if (!SUPPORTED_TUTOR_ACTION_KINDS.includes(actionKind)) {
186
+ throw new Error(
187
+ `Unsupported tutor actionKind "${actionKind}". Supported kinds: ${SUPPORTED_TUTOR_ACTION_KINDS.join(', ')}.`
188
+ );
189
+ }
190
+ return actionKind;
191
+ }
192
+
193
+ function resolveMemoryPolicyLayer(rawLayer) {
194
+ const layer = trimString(rawLayer) || 'session';
195
+ if (!SUPPORTED_MEMORY_POLICY_LAYERS.includes(layer)) {
196
+ throw new Error(
197
+ `Unsupported memory layer "${layer}". Supported layers: ${SUPPORTED_MEMORY_POLICY_LAYERS.join(', ')}.`
198
+ );
199
+ }
200
+ return layer;
201
+ }
202
+
203
+ function resolveMemoryPolicyOperation(rawOperation) {
204
+ const operation = trimString(rawOperation) || 'snapshot';
205
+ if (!SUPPORTED_MEMORY_POLICY_OPERATIONS.includes(operation)) {
206
+ throw new Error(
207
+ `Unsupported memory operation "${operation}". Supported operations: ${SUPPORTED_MEMORY_POLICY_OPERATIONS.join(', ')}.`
208
+ );
209
+ }
210
+ return operation;
211
+ }
212
+
213
+ function resolveReadonlyMemoryPolicyOperation(rawOperation) {
214
+ const operation = resolveMemoryPolicyOperation(rawOperation);
215
+ if (!SUPPORTED_READONLY_MEMORY_POLICY_OPERATIONS.includes(operation)) {
216
+ throw new Error(
217
+ `Unsupported readonly memory operation "${operation}". Supported readonly operations: ${SUPPORTED_READONLY_MEMORY_POLICY_OPERATIONS.join(', ')}.`
218
+ );
219
+ }
220
+ return operation;
221
+ }
222
+
223
+ function resolveStringArray(values) {
224
+ if (!Array.isArray(values)) {
225
+ return [];
226
+ }
227
+ return Array.from(new Set(values
228
+ .map((value) => trimString(value))
229
+ .filter((value) => value.length > 0)));
230
+ }
231
+
232
+ function resolveMemoryPolicyLimit(rawLimit, fallback) {
233
+ const numericLimit = Number(rawLimit);
234
+ const numericFallback = Number(fallback);
235
+ const base = Number.isFinite(numericLimit) ? numericLimit : (Number.isFinite(numericFallback) ? numericFallback : 20);
236
+ return Math.max(1, Math.min(100, Math.floor(base)));
237
+ }
238
+
239
+ function cloneCapabilityResultPreview(resultPreview) {
240
+ if (!resultPreview || typeof resultPreview !== 'object') {
241
+ return null;
242
+ }
243
+ return { ...resultPreview };
244
+ }
245
+
246
+ function cloneCapabilityEvent(event) {
247
+ if (!event || typeof event !== 'object') {
248
+ return null;
249
+ }
250
+ const resultPreview = cloneCapabilityResultPreview(event.resultPreview);
251
+ return {
252
+ ...event,
253
+ ...(resultPreview ? { resultPreview } : {}),
254
+ };
255
+ }
256
+
257
+ function formatMemoryKeyLabel(rawKey) {
258
+ const key = trimString(rawKey);
259
+ if (!key) {
260
+ return '';
261
+ }
262
+ if (key.startsWith('conversation_note:')) {
263
+ return 'note';
264
+ }
265
+ if (key.startsWith('conversation_correction:')) {
266
+ return 'correction';
267
+ }
268
+ const segments = key.split(':').map((segment) => trimString(segment)).filter((segment) => segment.length > 0);
269
+ return segments.length > 1 ? segments[segments.length - 1] : key;
270
+ }
271
+
272
+ function summarizeMemoryKeyLabels(keys) {
273
+ const labels = resolveStringArray(keys)
274
+ .map((key) => formatMemoryKeyLabel(key))
275
+ .filter((label) => label.length > 0);
276
+ return labels.length ? labels.join(', ') : 'none';
277
+ }
278
+
279
+ function resolveManagedConversationKeyLabels(keys) {
280
+ return Array.from(new Set(
281
+ resolveStringArray(keys)
282
+ .map((key) => formatMemoryKeyLabel(key))
283
+ .filter((label) => label === 'note' || label === 'correction')
284
+ ));
285
+ }
286
+
287
+ function resolveManagedMemoryFollowUpKeyLabel(actionId) {
288
+ const normalizedActionId = trimString(actionId);
289
+ if (normalizedActionId === 'write_memory_note') {
290
+ return 'note';
291
+ }
292
+ if (normalizedActionId === 'record_memory_correction') {
293
+ return 'correction';
294
+ }
295
+ return '';
296
+ }
297
+
298
+ function resolveManagedConversationFollowUpActionIdsFromKeyCounts(counts) {
299
+ const actionIds = [];
300
+ if (Number(counts && counts.note) > 0) {
301
+ actionIds.push('write_memory_note');
302
+ }
303
+ if (Number(counts && counts.correction) > 0) {
304
+ actionIds.push('record_memory_correction');
305
+ }
306
+ return actionIds;
307
+ }
308
+
309
+ function resolveManagedConversationFollowUpActionIdsFromKeyLabels(keyLabels) {
310
+ return Array.from(new Set(
311
+ resolveManagedConversationKeyLabels(keyLabels)
312
+ .map((keyLabel) => {
313
+ if (keyLabel === 'note') {
314
+ return 'write_memory_note';
315
+ }
316
+ if (keyLabel === 'correction') {
317
+ return 'record_memory_correction';
318
+ }
319
+ return '';
320
+ })
321
+ .filter((actionId) => actionId.length > 0)
322
+ ));
323
+ }
324
+
325
+ function resolveManagedConversationFollowUpActionLabels(actionIds) {
326
+ return Array.from(new Set(
327
+ resolveStringArray(actionIds)
328
+ .map((actionId) => {
329
+ const normalizedActionId = trimString(actionId);
330
+ if (normalizedActionId === 'write_memory_note') {
331
+ return getI18nText(
332
+ 'agentWorkspace.actions.writeMemoryNote',
333
+ 'Store Memory Note'
334
+ );
335
+ }
336
+ if (normalizedActionId === 'record_memory_correction') {
337
+ return getI18nText(
338
+ 'agentWorkspace.actions.recordMemoryCorrection',
339
+ 'Record Correction'
340
+ );
341
+ }
342
+ return normalizedActionId;
343
+ })
344
+ .filter((label) => trimString(label).length > 0)
345
+ ));
346
+ }
347
+
348
+ function buildManagedConversationLastTransition(
349
+ atomId,
350
+ newerEvent,
351
+ olderEvent,
352
+ resolvedKeyLabels,
353
+ persistentKeyLabels
354
+ ) {
355
+ const normalizedResolvedKeyLabels = resolveManagedConversationKeyLabels(resolvedKeyLabels);
356
+ const normalizedPersistentKeyLabels = resolveManagedConversationKeyLabels(persistentKeyLabels);
357
+ const keyLabels = normalizedResolvedKeyLabels.concat(
358
+ normalizedPersistentKeyLabels.filter((label) => !normalizedResolvedKeyLabels.includes(label))
359
+ );
360
+ const resolvedFollowUpActionIds = resolveManagedConversationFollowUpActionIdsFromKeyLabels(
361
+ normalizedResolvedKeyLabels
362
+ );
363
+ const resolvedFollowUpActionLabels = resolveManagedConversationFollowUpActionLabels(
364
+ resolvedFollowUpActionIds
365
+ );
366
+ const persistentFollowUpActionIds = resolveManagedConversationFollowUpActionIdsFromKeyLabels(
367
+ normalizedPersistentKeyLabels
368
+ );
369
+ const persistentFollowUpActionLabels = resolveManagedConversationFollowUpActionLabels(
370
+ persistentFollowUpActionIds
371
+ );
372
+ const followUpActionIds = Array.from(new Set(
373
+ resolvedFollowUpActionIds.concat(persistentFollowUpActionIds)
374
+ ));
375
+ const followUpActionLabels = resolveManagedConversationFollowUpActionLabels(followUpActionIds);
376
+ const kind = normalizedResolvedKeyLabels.length > 0 && normalizedPersistentKeyLabels.length > 0
377
+ ? 'mixed'
378
+ : normalizedResolvedKeyLabels.length > 0
379
+ ? 'resolved'
380
+ : 'persistent';
381
+ return {
382
+ atomId,
383
+ keyLabels,
384
+ keyLabel: keyLabels.join(', '),
385
+ kind,
386
+ newerEventId: trimString(newerEvent && newerEvent.eventId),
387
+ olderEventId: trimString(olderEvent && olderEvent.eventId),
388
+ newerAt: trimString(newerEvent && newerEvent.at),
389
+ olderAt: trimString(olderEvent && olderEvent.at),
390
+ resolvedKeyLabels: normalizedResolvedKeyLabels,
391
+ resolvedKeyLabel: normalizedResolvedKeyLabels.join(', '),
392
+ persistentKeyLabels: normalizedPersistentKeyLabels,
393
+ persistentKeyLabel: normalizedPersistentKeyLabels.join(', '),
394
+ followUpActionId: followUpActionIds[0] || '',
395
+ followUpActionIds,
396
+ followUpActionLabel: followUpActionLabels[0] || '',
397
+ followUpActionLabels,
398
+ resolvedFollowUpActionIds,
399
+ resolvedFollowUpActionLabels,
400
+ persistentFollowUpActionIds,
401
+ persistentFollowUpActionLabels,
402
+ };
403
+ }
404
+
405
+ function isManagedMemoryStateActionId(actionId) {
406
+ return trimString(actionId) === 'inspect_managed_memory_state';
407
+ }
408
+
409
+ function summarizeManagedMemoryFollowUpActions(keys) {
410
+ const labels = [];
411
+ resolveStringArray(keys).forEach((key) => {
412
+ if (key.startsWith('conversation_note:')) {
413
+ labels.push(
414
+ getI18nText(
415
+ 'agentWorkspace.actions.writeMemoryNote',
416
+ 'Store Memory Note'
417
+ )
418
+ );
419
+ return;
420
+ }
421
+ if (key.startsWith('conversation_correction:')) {
422
+ labels.push(
423
+ getI18nText(
424
+ 'agentWorkspace.actions.recordMemoryCorrection',
425
+ 'Record Correction'
426
+ )
427
+ );
428
+ }
429
+ });
430
+ return Array.from(new Set(labels)).join(', ');
431
+ }
432
+
433
+ function resolveMisconceptionTopK(rawTopK, fallback) {
434
+ const numericTopK = Number(rawTopK);
435
+ const numericFallback = Number(fallback);
436
+ const base = Number.isFinite(numericTopK) ? numericTopK : (Number.isFinite(numericFallback) ? numericFallback : 5);
437
+ return Math.max(1, Math.min(20, Math.floor(base)));
438
+ }
439
+
440
+ function resolveQueryTopK(rawTopK, fallback) {
441
+ const numericTopK = Number(rawTopK);
442
+ const numericFallback = Number(fallback);
443
+ const base = Number.isFinite(numericTopK) ? numericTopK : (Number.isFinite(numericFallback) ? numericFallback : 4);
444
+ return Math.max(1, Math.min(8, Math.floor(base)));
445
+ }
446
+
447
+ function formatI18nTemplate(template, params) {
448
+ if (typeof template !== 'string' || !template) {
449
+ return '';
450
+ }
451
+ return template.replace(/\{(\w+)\}/g, (_match, token) => {
452
+ const value = params && Object.prototype.hasOwnProperty.call(params, token)
453
+ ? params[token]
454
+ : '';
455
+ return value == null ? '' : String(value);
456
+ });
457
+ }
458
+
459
+ function getI18nText(key, fallback, params) {
460
+ if (
461
+ globalScope
462
+ && globalScope.i18n
463
+ && typeof globalScope.i18n.t === 'function'
464
+ ) {
465
+ const translated = globalScope.i18n.t(key, params || {});
466
+ if (typeof translated === 'string' && translated.trim().length > 0 && translated !== key) {
467
+ return translated;
468
+ }
469
+ }
470
+ return formatI18nTemplate(fallback, params);
471
+ }
472
+
473
+ function getContractApi() {
474
+ return (globalScope && globalScope.NoteConnectionAgentWorkspaceContract)
475
+ ? globalScope.NoteConnectionAgentWorkspaceContract
476
+ : null;
477
+ }
478
+
479
+ async function requestJson(endpoint, payload, options = {}) {
480
+ const method = trimString(options.method || 'POST').toUpperCase() || 'POST';
481
+ const responseKey = trimString(options.responseKey || 'result') || 'result';
482
+ const headers = method === 'GET'
483
+ ? {}
484
+ : {
485
+ 'Content-Type': 'application/json',
486
+ };
487
+ const requestOptions = {
488
+ method,
489
+ headers,
490
+ };
491
+ if (method !== 'GET') {
492
+ requestOptions.body = JSON.stringify(payload || {});
493
+ }
494
+ const response = await fetch(endpoint, requestOptions);
495
+ const body = await response.json().catch(() => null);
496
+ if (!response.ok) {
497
+ const message = body && body.error
498
+ ? body.error
499
+ : `Request failed (${response.status})`;
500
+ throw new Error(String(message));
501
+ }
502
+ if (!body || body.success !== true) {
503
+ const message = body && body.error
504
+ ? body.error
505
+ : `API ${endpoint} returned unexpected response`;
506
+ throw new Error(String(message));
507
+ }
508
+ return body[responseKey];
509
+ }
510
+
511
+ function resolveConversationPayload(input) {
512
+ const contractApi = getContractApi();
513
+ if (contractApi && typeof contractApi.createAgentConversationPayload === 'function') {
514
+ return contractApi.createAgentConversationPayload(input);
515
+ }
516
+ const userId = trimString(input && input.userId);
517
+ const message = trimString(input && input.message);
518
+ const topKRaw = Number(input && input.topK);
519
+ const topK = Number.isFinite(topKRaw) ? Math.max(1, Math.min(8, Math.floor(topKRaw))) : 4;
520
+ if (!userId) {
521
+ throw new Error('agent_workspace requires a non-empty userId for conversation payload.');
522
+ }
523
+ if (!message) {
524
+ throw new Error('agent_workspace requires a non-empty message for conversation payload.');
525
+ }
526
+ return { userId, message, topK };
527
+ }
528
+
529
+ function resolveOperationPlan(capability) {
530
+ const contractApi = getContractApi();
531
+ if (contractApi && typeof contractApi.buildKnowledgeOperationRequestPayload === 'function') {
532
+ return contractApi.buildKnowledgeOperationRequestPayload(capability);
533
+ }
534
+ const execution = capability && capability.execution && typeof capability.execution === 'object'
535
+ ? capability.execution
536
+ : {};
537
+ const request = capability && capability.request && typeof capability.request === 'object'
538
+ ? capability.request
539
+ : {};
540
+ const operationId = trimString(execution.operationId);
541
+ if (operationId === 'build_learning_path') {
542
+ return {
543
+ operationId,
544
+ endpoint: '/api/knowledge/path',
545
+ method: 'POST',
546
+ resultPresentation: 'learning_path_card',
547
+ body: {
548
+ userId: trimString(request.userId),
549
+ focusAtomIds: [trimString(request.atomId)],
550
+ maxMasteryPaths: 3,
551
+ maxDivergencePaths: 2,
552
+ },
553
+ };
554
+ }
555
+ if (operationId === 'execute_tutor_action') {
556
+ const userId = trimString(request.userId);
557
+ const atomId = trimString(request.atomId);
558
+ if (!userId || !atomId) {
559
+ throw new Error('execute_tutor_action requires request.userId, request.atomId, and request.actionKind.');
560
+ }
561
+ const actionKind = resolveTutorActionKind(request.actionKind);
562
+ const answer = trimString(request.answer);
563
+ return {
564
+ operationId,
565
+ endpoint: '/api/knowledge/tutor/action',
566
+ method: 'POST',
567
+ resultPresentation: trimString(execution.resultPresentation) || 'assistant_message',
568
+ body: {
569
+ userId,
570
+ atomId,
571
+ actionKind,
572
+ prompt: trimString(request.message),
573
+ ...(answer ? { answer } : {}),
574
+ },
575
+ };
576
+ }
577
+ if (operationId === 'build_study_session') {
578
+ return {
579
+ operationId,
580
+ endpoint: '/api/knowledge/session/plan',
581
+ method: 'POST',
582
+ resultPresentation: trimString(execution.resultPresentation) || 'study_session_card',
583
+ body: {
584
+ userId: trimString(request.userId),
585
+ focusAtomIds: [trimString(request.atomId)],
586
+ maxActions: 6,
587
+ includeDivergence: true,
588
+ includeRetrain: false,
589
+ },
590
+ };
591
+ }
592
+ if (operationId === 'query_knowledge') {
593
+ const query = trimString(request.query || request.message);
594
+ if (!query) {
595
+ throw new Error('query_knowledge requires request.query or request.message.');
596
+ }
597
+ return {
598
+ operationId,
599
+ endpoint: '/api/knowledge/query',
600
+ method: 'POST',
601
+ resultPresentation: trimString(execution.resultPresentation) || 'query_trace_card',
602
+ body: {
603
+ query,
604
+ topK: resolveQueryTopK(request.topK, 4),
605
+ },
606
+ };
607
+ }
608
+ if (operationId === 'evaluate_ingest_guardrails') {
609
+ return {
610
+ operationId,
611
+ endpoint: '/api/knowledge/ingest/guardrails/evaluate',
612
+ method: 'POST',
613
+ resultPresentation: trimString(execution.resultPresentation) || 'ingest_guardrail_card',
614
+ body: {},
615
+ };
616
+ }
617
+ if (operationId === 'query_session_history') {
618
+ return {
619
+ operationId,
620
+ endpoint: '/api/knowledge/session/history',
621
+ method: 'POST',
622
+ resultPresentation: trimString(execution.resultPresentation) || 'session_history_card',
623
+ body: {
624
+ userId: trimString(request.userId),
625
+ limit: 5,
626
+ },
627
+ };
628
+ }
629
+ if (operationId === 'query_mastery_misconceptions') {
630
+ const atomId = trimString(request.atomId);
631
+ return {
632
+ operationId,
633
+ endpoint: '/api/knowledge/mastery/misconceptions',
634
+ method: 'POST',
635
+ resultPresentation: trimString(execution.resultPresentation) || 'mastery_misconceptions_card',
636
+ body: {
637
+ userId: trimString(request.userId),
638
+ topK: resolveMisconceptionTopK(request.topK, 5),
639
+ ...(atomId ? { atomIds: [atomId] } : {}),
640
+ },
641
+ };
642
+ }
643
+ if (operationId === 'capture_learning_quality_snapshot') {
644
+ return {
645
+ operationId,
646
+ endpoint: '/api/knowledge/quality/snapshot',
647
+ method: 'POST',
648
+ resultPresentation: trimString(execution.resultPresentation) || 'learning_quality_snapshot_card',
649
+ body: {
650
+ userId: trimString(request.userId),
651
+ },
652
+ };
653
+ }
654
+ if (operationId === 'apply_memory_policy') {
655
+ const userId = trimString(request.userId);
656
+ if (!userId) {
657
+ throw new Error('apply_memory_policy requires request.userId.');
658
+ }
659
+ const layer = resolveMemoryPolicyLayer(request.memoryLayer || request.layer);
660
+ const operation = resolveMemoryPolicyOperation(request.memoryOperation || request.operation);
661
+ const query = trimString(request.memoryQuery || request.query);
662
+ const limit = resolveMemoryPolicyLimit(
663
+ request.memoryLimit || request.limit,
664
+ operation === 'retrain_plan' ? 8 : 20
665
+ );
666
+ if (operation === 'write') {
667
+ const atomId = trimString(request.atomId);
668
+ const memoryKey = trimString(request.memoryKey) || (atomId ? `conversation_note:${atomId}` : '');
669
+ if (!memoryKey) {
670
+ throw new Error('apply_memory_policy write requires request.memoryKey or request.atomId.');
671
+ }
672
+ const memoryValue = trimString(request.memoryValue);
673
+ const memoryTags = resolveStringArray(request.memoryTags);
674
+ const memoryReferences = Array.from(new Set([
675
+ ...resolveStringArray(request.memoryReferences),
676
+ ...(atomId ? [atomId] : []),
677
+ ]));
678
+ const promptMessage = trimString(request.memoryPromptMessage);
679
+ return {
680
+ operationId,
681
+ endpoint: '/api/knowledge/memory/policy',
682
+ method: 'POST',
683
+ resultPresentation: trimString(execution.resultPresentation) || 'memory_policy_card',
684
+ body: {
685
+ userId,
686
+ layer,
687
+ operation,
688
+ ...(memoryValue ? {
689
+ entries: [
690
+ {
691
+ key: memoryKey,
692
+ value: memoryValue,
693
+ tags: memoryTags,
694
+ references: memoryReferences,
695
+ },
696
+ ],
697
+ } : {}),
698
+ },
699
+ ...(!memoryValue ? {
700
+ promptConfig: {
701
+ kind: 'memory_write',
702
+ message: promptMessage,
703
+ entryTemplate: {
704
+ key: memoryKey,
705
+ tags: memoryTags,
706
+ references: memoryReferences,
707
+ },
708
+ },
709
+ } : {}),
710
+ };
711
+ }
712
+ if (operation === 'evict') {
713
+ const matchKeys = resolveStringArray(request.memoryMatchKeys);
714
+ return {
715
+ operationId,
716
+ endpoint: '/api/knowledge/memory/policy',
717
+ method: 'POST',
718
+ resultPresentation: trimString(execution.resultPresentation) || 'memory_policy_card',
719
+ body: {
720
+ userId,
721
+ layer,
722
+ operation,
723
+ ...(matchKeys.length ? { matchKeys } : {}),
724
+ },
725
+ };
726
+ }
727
+ return {
728
+ operationId,
729
+ endpoint: '/api/knowledge/memory/policy',
730
+ method: 'POST',
731
+ resultPresentation: trimString(execution.resultPresentation) || 'memory_policy_card',
732
+ body: {
733
+ userId,
734
+ layer,
735
+ operation,
736
+ ...(operation === 'read' || operation === 'retrain_plan' ? { limit } : {}),
737
+ ...(operation === 'read' && resolveStringArray(request.memoryMatchKeys).length > 0 ? {
738
+ matchKeys: resolveStringArray(request.memoryMatchKeys),
739
+ } : {}),
740
+ ...(operation === 'read' && query ? { query } : {}),
741
+ },
742
+ };
743
+ }
744
+ throw new Error(`Unsupported knowledge operation: ${operationId || '<empty>'}`);
745
+ }
746
+
747
+ function createAgentWorkspaceRuntime(options = {}) {
748
+ const state = {
749
+ busy: false,
750
+ latestKnowledgePoints: [],
751
+ latestFocusAtomId: '',
752
+ latestPathAtomId: '',
753
+ latestPathRuntimeNodeId: '',
754
+ latestPathRuntimeNodeLabel: '',
755
+ expandedHistoryEventIdsByAtom: Object.create(null),
756
+ pathVisible: false,
757
+ pathFullscreen: false,
758
+ initialized: false,
759
+ };
760
+ const diagnostics = {
761
+ turnSequence: 0,
762
+ capabilityEventSequence: 0,
763
+ turnHistory: [],
764
+ capabilityEvents: [],
765
+ conversationRequests: 0,
766
+ replayCandidateTurns: 0,
767
+ userMessageFingerprintCounts: Object.create(null),
768
+ lastConversation: null,
769
+ lastFoundationReadiness: null,
770
+ lastFailure: null,
771
+ };
772
+
773
+ const dom = {
774
+ panel: null,
775
+ form: null,
776
+ userIdInput: null,
777
+ input: null,
778
+ sendButton: null,
779
+ messages: null,
780
+ knowledgeList: null,
781
+ openPathButton: null,
782
+ closePathButton: null,
783
+ pathFullscreenButton: null,
784
+ foundationReadinessButton: null,
785
+ };
786
+
787
+ function nowIso() {
788
+ return new Date().toISOString();
789
+ }
790
+
791
+ function pushBounded(list, item, maxSize) {
792
+ list.push(item);
793
+ if (list.length > maxSize) {
794
+ list.splice(0, list.length - maxSize);
795
+ }
796
+ }
797
+
798
+ function markUserMessageReplayCandidate(message) {
799
+ const fingerprint = trimString(message).toLowerCase();
800
+ if (!fingerprint) {
801
+ return {
802
+ fingerprint: '',
803
+ replayCandidate: false,
804
+ seenCount: 0,
805
+ };
806
+ }
807
+ const previousCount = Number(diagnostics.userMessageFingerprintCounts[fingerprint] || 0);
808
+ const nextCount = previousCount + 1;
809
+ diagnostics.userMessageFingerprintCounts[fingerprint] = nextCount;
810
+ const replayCandidate = previousCount > 0;
811
+ if (replayCandidate) {
812
+ diagnostics.replayCandidateTurns += 1;
813
+ }
814
+ return {
815
+ fingerprint,
816
+ replayCandidate,
817
+ seenCount: nextCount,
818
+ };
819
+ }
820
+
821
+ function recordTurn(role, text, meta = {}) {
822
+ const content = trimString(text);
823
+ if (!content) {
824
+ return;
825
+ }
826
+ pushBounded(
827
+ diagnostics.turnHistory,
828
+ {
829
+ turnId: `turn_${diagnostics.turnSequence + 1}`,
830
+ role,
831
+ text: content,
832
+ at: nowIso(),
833
+ ...meta,
834
+ },
835
+ MAX_DIAGNOSTIC_TURNS
836
+ );
837
+ diagnostics.turnSequence += 1;
838
+ }
839
+
840
+ function recordCapabilityEvent(payload = {}) {
841
+ const durationMs = Number(payload.durationMs);
842
+ const memoryLayer = normalizeMemoryPolicyLayerForDiagnostics(payload.memoryLayer);
843
+ const memoryOperation = normalizeMemoryPolicyOperationForDiagnostics(payload.memoryOperation);
844
+ const managedKeyLabels = resolveManagedConversationKeyLabels(payload.managedKeyLabels);
845
+ const matchedManagedKeyLabels = resolveManagedConversationKeyLabels(payload.matchedManagedKeyLabels);
846
+ const missingManagedKeyLabels = resolveManagedConversationKeyLabels(payload.missingManagedKeyLabels);
847
+ const resultPreview = cloneCapabilityResultPreview(payload.resultPreview);
848
+ const event = {
849
+ eventId: `cap_${diagnostics.capabilityEventSequence + 1}`,
850
+ at: nowIso(),
851
+ phase: trimString(payload.phase) || 'event',
852
+ status: trimString(payload.status) || 'unknown',
853
+ actionId: trimString(payload.actionId),
854
+ operationId: trimString(payload.operationId),
855
+ resultPresentation: trimString(payload.resultPresentation),
856
+ atomId: trimString(payload.atomId),
857
+ durationMs: Number.isFinite(durationMs) ? Math.max(0, Math.floor(durationMs)) : undefined,
858
+ error: trimString(payload.error),
859
+ memoryLayer: memoryLayer || undefined,
860
+ memoryOperation: memoryOperation || undefined,
861
+ managedKeyLabels: managedKeyLabels.length > 0 ? managedKeyLabels : undefined,
862
+ matchedManagedKeyLabels: matchedManagedKeyLabels.length > 0 ? matchedManagedKeyLabels : undefined,
863
+ missingManagedKeyLabels: missingManagedKeyLabels.length > 0 ? missingManagedKeyLabels : undefined,
864
+ resultPreview: resultPreview || undefined,
865
+ };
866
+ pushBounded(diagnostics.capabilityEvents, event, MAX_DIAGNOSTIC_CAPABILITY_EVENTS);
867
+ diagnostics.capabilityEventSequence += 1;
868
+ return event;
869
+ }
870
+
871
+ function recordFailure(source, errorMessage) {
872
+ diagnostics.lastFailure = {
873
+ at: nowIso(),
874
+ source: trimString(source),
875
+ message: trimString(errorMessage) || 'unknown error',
876
+ };
877
+ }
878
+
879
+ function cloneRunbookLinks() {
880
+ return DIAGNOSTIC_RUNBOOK_LINKS.map((link) => ({
881
+ id: link.id,
882
+ label: link.label,
883
+ paths: {
884
+ en: link.paths.en,
885
+ zh: link.paths.zh,
886
+ },
887
+ }));
888
+ }
889
+
890
+ function resolvePrimaryRunbookAction() {
891
+ // M8.53: bounded primary runbook consumer adoption.
892
+ // Derives the primary recommendation from local diagnostics state
893
+ // using the same contract shape as the server-side AgentWorkspaceDiagnosticsRunbookAction.
894
+ // No new routes, persistence, or backend recomputation.
895
+ var managedSummary = computeManagedConversationSummary();
896
+ var capabilityStats = computeCapabilityOperationStats();
897
+ var hasPersistentGaps = (
898
+ managedSummary.continuitySummary.persistentKeyCounts.note > 0 ||
899
+ managedSummary.continuitySummary.persistentKeyCounts.correction > 0
900
+ );
901
+ var hasResolvedGaps = (
902
+ managedSummary.continuitySummary.resolvedKeyCounts.note > 0 ||
903
+ managedSummary.continuitySummary.resolvedKeyCounts.correction > 0
904
+ );
905
+ var hasFailures = capabilityStats.some(function (stat) {
906
+ return stat.failureCount > 0;
907
+ });
908
+ var primaryAction = null;
909
+ var secondaryActions = [];
910
+
911
+ if (hasPersistentGaps) {
912
+ primaryAction = {
913
+ actionId: 'inspect_managed_memory_state',
914
+ severity: 'warning',
915
+ title: 'Review persistent managed-memory gaps',
916
+ trigger: 'persistentManagedMemoryGap',
917
+ rationale: (
918
+ 'Persistent managed-memory gaps remain for the current atom. ' +
919
+ 'Missing note keys: ' + (managedSummary.continuitySummary.persistentKeyCounts.note || 0) +
920
+ ', missing correction keys: ' + (managedSummary.continuitySummary.persistentKeyCounts.correction || 0) +
921
+ '. Recommended next step: Store Memory Note or Record Correction for this atom.'
922
+ ),
923
+ runbookLinkIds: ['development-progress-dashboard'],
924
+ };
925
+ if (managedSummary.continuitySummary.persistentFollowUpActionIds.length > 0) {
926
+ secondaryActions.push({
927
+ actionId: managedSummary.continuitySummary.persistentFollowUpActionIds[0],
928
+ severity: 'info',
929
+ title: 'Execute pending follow-up: ' + (managedSummary.continuitySummary.persistentFollowUpActionLabels[0] || managedSummary.continuitySummary.persistentFollowUpActionIds[0]),
930
+ trigger: 'persistentFollowUp',
931
+ rationale: 'A deterministic follow-up action remains from the most recent managed-memory gap analysis.',
932
+ runbookLinkIds: ['development-progress-dashboard'],
933
+ });
934
+ }
935
+ } else if (hasFailures) {
936
+ var failedStats = capabilityStats.filter(function (stat) { return stat.failureCount > 0; });
937
+ var firstFailedOp = failedStats[0];
938
+ primaryAction = {
939
+ actionId: 'review_recent_failures',
940
+ severity: 'warning',
941
+ title: 'Review recent capability failures',
942
+ trigger: 'recentCapabilityFailure',
943
+ rationale: (
944
+ 'Recent capability execution failures detected. ' +
945
+ 'Operation: ' + (firstFailedOp ? firstFailedOp.operationId : 'unknown') +
946
+ ', failures: ' + (firstFailedOp ? firstFailedOp.failureCount : 0) +
947
+ '. Review the diagnostics snapshot for failure details.'
948
+ ),
949
+ runbookLinkIds: ['development-progress-dashboard'],
950
+ };
951
+ } else if (hasResolvedGaps) {
952
+ primaryAction = {
953
+ actionId: 'review_continuity_recovery',
954
+ severity: 'info',
955
+ title: 'Continuity recovery confirmed',
956
+ trigger: 'managedMemoryGapResolved',
957
+ rationale: (
958
+ 'Previously missing managed-memory gaps have been resolved by adjacent operations. ' +
959
+ 'Resolved note keys: ' + (managedSummary.continuitySummary.resolvedKeyCounts.note || 0) +
960
+ ', resolved correction keys: ' + (managedSummary.continuitySummary.resolvedKeyCounts.correction || 0) +
961
+ '. No immediate corrective action needed.'
962
+ ),
963
+ runbookLinkIds: ['development-progress-dashboard'],
964
+ };
965
+ }
966
+
967
+ var runbookActions = [];
968
+ if (primaryAction) {
969
+ runbookActions.push(primaryAction);
970
+ }
971
+ runbookActions = runbookActions.concat(secondaryActions);
972
+
973
+ return {
974
+ primaryRunbookAction: primaryAction,
975
+ runbookActions: runbookActions,
976
+ };
977
+ }
978
+
979
+ function computeCapabilityOperationStats() {
980
+ const byOperationId = Object.create(null);
981
+ diagnostics.capabilityEvents.forEach((event) => {
982
+ const operationId = trimString(event.operationId) || '__unknown__';
983
+ const status = trimString(event.status) || 'unknown';
984
+ const phase = trimString(event.phase) || 'event';
985
+ if (!byOperationId[operationId]) {
986
+ byOperationId[operationId] = {
987
+ operationId,
988
+ eventIds: [],
989
+ requestCount: 0,
990
+ resultCount: 0,
991
+ successCount: 0,
992
+ failureCount: 0,
993
+ pendingCount: 0,
994
+ unknownCount: 0,
995
+ durationTotalMs: 0,
996
+ durationSampleCount: 0,
997
+ latestStatus: '',
998
+ latestAt: '',
999
+ };
1000
+ }
1001
+ const stats = byOperationId[operationId];
1002
+ const eventId = trimString(event.eventId);
1003
+ if (eventId) {
1004
+ stats.eventIds.push(eventId);
1005
+ }
1006
+ if (phase === 'request') {
1007
+ stats.requestCount += 1;
1008
+ } else if (phase === 'result') {
1009
+ stats.resultCount += 1;
1010
+ }
1011
+ if (status === 'success') {
1012
+ stats.successCount += 1;
1013
+ } else if (status === 'failure' || status === 'error') {
1014
+ stats.failureCount += 1;
1015
+ } else if (status === 'pending') {
1016
+ stats.pendingCount += 1;
1017
+ } else {
1018
+ stats.unknownCount += 1;
1019
+ }
1020
+ const durationMs = Number(event.durationMs);
1021
+ if (Number.isFinite(durationMs)) {
1022
+ stats.durationTotalMs += Math.max(0, Math.floor(durationMs));
1023
+ stats.durationSampleCount += 1;
1024
+ }
1025
+ stats.latestStatus = status;
1026
+ stats.latestAt = trimString(event.at);
1027
+ });
1028
+
1029
+ return Object.keys(byOperationId)
1030
+ .sort()
1031
+ .map((operationId) => {
1032
+ const stats = byOperationId[operationId];
1033
+ return {
1034
+ operationId: stats.operationId,
1035
+ eventIds: stats.eventIds.slice(),
1036
+ requestCount: stats.requestCount,
1037
+ resultCount: stats.resultCount,
1038
+ successCount: stats.successCount,
1039
+ failureCount: stats.failureCount,
1040
+ pendingCount: stats.pendingCount,
1041
+ unknownCount: stats.unknownCount,
1042
+ averageDurationMs: stats.durationSampleCount > 0
1043
+ ? Math.round(stats.durationTotalMs / stats.durationSampleCount)
1044
+ : null,
1045
+ latestStatus: stats.latestStatus,
1046
+ latestAt: stats.latestAt,
1047
+ };
1048
+ });
1049
+ }
1050
+
1051
+ function computeTurnIndex() {
1052
+ const byTurnId = Object.create(null);
1053
+ const replayCandidateTurnIds = [];
1054
+ diagnostics.turnHistory.forEach((turn, index) => {
1055
+ const turnId = trimString(turn && turn.turnId);
1056
+ if (!turnId) {
1057
+ return;
1058
+ }
1059
+ const replayCandidate = Boolean(turn && turn.replayCandidate);
1060
+ if (replayCandidate) {
1061
+ replayCandidateTurnIds.push(turnId);
1062
+ }
1063
+ byTurnId[turnId] = {
1064
+ index,
1065
+ role: trimString(turn && turn.role),
1066
+ at: trimString(turn && turn.at),
1067
+ replayCandidate,
1068
+ userMessageFingerprint: trimString(turn && turn.userMessageFingerprint),
1069
+ };
1070
+ });
1071
+ return {
1072
+ byTurnId,
1073
+ replayCandidateTurnIds,
1074
+ };
1075
+ }
1076
+
1077
+ function computeMemoryPolicySummary() {
1078
+ const byLayer = createEmptyMemoryPolicyLayerCounts();
1079
+ const byOperation = createEmptyMemoryPolicyOperationCounts();
1080
+ let executionCount = 0;
1081
+ let readonlyExecutions = 0;
1082
+ let mutatingExecutions = 0;
1083
+ let successCount = 0;
1084
+ let failureCount = 0;
1085
+ let lastEvent = null;
1086
+
1087
+ diagnostics.capabilityEvents.forEach((event) => {
1088
+ if (trimString(event.operationId) !== 'apply_memory_policy') {
1089
+ return;
1090
+ }
1091
+ if (trimString(event.phase) !== 'result') {
1092
+ return;
1093
+ }
1094
+
1095
+ executionCount += 1;
1096
+ const memoryLayer = normalizeMemoryPolicyLayerForDiagnostics(event.memoryLayer);
1097
+ const memoryOperation = normalizeMemoryPolicyOperationForDiagnostics(event.memoryOperation);
1098
+ const status = trimString(event.status);
1099
+
1100
+ if (memoryLayer) {
1101
+ byLayer[memoryLayer] += 1;
1102
+ }
1103
+ if (memoryOperation) {
1104
+ byOperation[memoryOperation] += 1;
1105
+ if (isReadonlyMemoryPolicyOperation(memoryOperation)) {
1106
+ readonlyExecutions += 1;
1107
+ } else {
1108
+ mutatingExecutions += 1;
1109
+ }
1110
+ }
1111
+ if (status === 'success') {
1112
+ successCount += 1;
1113
+ } else if (status === 'failure' || status === 'failed' || status === 'error') {
1114
+ failureCount += 1;
1115
+ }
1116
+
1117
+ lastEvent = { ...event };
1118
+ });
1119
+
1120
+ return {
1121
+ executionCount,
1122
+ readonlyExecutions,
1123
+ mutatingExecutions,
1124
+ successCount,
1125
+ failureCount,
1126
+ byLayer,
1127
+ byOperation,
1128
+ lastEvent,
1129
+ };
1130
+ }
1131
+
1132
+ function createEmptyManagedConversationActionCounts() {
1133
+ return {
1134
+ inspect_managed_memory_state: 0,
1135
+ write_memory_note: 0,
1136
+ record_memory_correction: 0,
1137
+ evict_memory_note: 0,
1138
+ };
1139
+ }
1140
+
1141
+ function createEmptyManagedConversationKeyCounts() {
1142
+ return {
1143
+ note: 0,
1144
+ correction: 0,
1145
+ };
1146
+ }
1147
+
1148
+ function createEmptyManagedConversationContinuitySummary() {
1149
+ return {
1150
+ atomIds: [],
1151
+ atomCount: 0,
1152
+ readCount: 0,
1153
+ transitionCount: 0,
1154
+ resolvedKeyCounts: createEmptyManagedConversationKeyCounts(),
1155
+ resolvedFollowUpActionIds: [],
1156
+ resolvedFollowUpActionLabels: [],
1157
+ persistentKeyCounts: createEmptyManagedConversationKeyCounts(),
1158
+ persistentFollowUpActionIds: [],
1159
+ persistentFollowUpActionLabels: [],
1160
+ lastTransition: null,
1161
+ };
1162
+ }
1163
+
1164
+ function incrementManagedConversationKeyCounts(counts, labels) {
1165
+ resolveStringArray(labels).forEach((label) => {
1166
+ if (label === 'note' || label === 'correction') {
1167
+ counts[label] += 1;
1168
+ }
1169
+ });
1170
+ }
1171
+
1172
+ function isManagedConversationActionId(actionId) {
1173
+ return MANAGED_CONVERSATION_ACTION_IDS.includes(trimString(actionId));
1174
+ }
1175
+
1176
+ function hasManagedConversationKeyCounts(counts) {
1177
+ return Boolean(counts) && (
1178
+ Number(counts.note) > 0
1179
+ || Number(counts.correction) > 0
1180
+ );
1181
+ }
1182
+
1183
+ function computeManagedConversationContinuitySummary(events, newestFirst = true) {
1184
+ const summary = createEmptyManagedConversationContinuitySummary();
1185
+ const sourceEvents = Array.isArray(events)
1186
+ ? (newestFirst ? events.slice() : events.slice().reverse())
1187
+ : [];
1188
+ const eventsByAtomId = Object.create(null);
1189
+
1190
+ sourceEvents.forEach((event) => {
1191
+ const atomId = trimString(event && event.atomId);
1192
+ if (!atomId) {
1193
+ return;
1194
+ }
1195
+ if (trimString(event && event.actionId) !== 'inspect_managed_memory_state') {
1196
+ return;
1197
+ }
1198
+ if (trimString(event && event.phase) === 'request' || trimString(event && event.phase) === 'plan') {
1199
+ return;
1200
+ }
1201
+ if (trimString(event && event.status) !== 'success') {
1202
+ return;
1203
+ }
1204
+ if (!eventsByAtomId[atomId]) {
1205
+ eventsByAtomId[atomId] = [];
1206
+ }
1207
+ eventsByAtomId[atomId].push(event);
1208
+ });
1209
+
1210
+ summary.atomIds = Object.keys(eventsByAtomId);
1211
+ summary.atomCount = summary.atomIds.length;
1212
+
1213
+ summary.atomIds.forEach((atomId) => {
1214
+ const atomEvents = eventsByAtomId[atomId];
1215
+ summary.readCount += atomEvents.length;
1216
+ for (let index = 0; index < atomEvents.length - 1; index += 1) {
1217
+ const newerEvent = atomEvents[index];
1218
+ const olderEvent = atomEvents[index + 1];
1219
+ const newerMissingManagedKeyLabels = resolveManagedConversationKeyLabels(
1220
+ newerEvent && newerEvent.missingManagedKeyLabels
1221
+ );
1222
+ const olderMissingManagedKeyLabels = resolveManagedConversationKeyLabels(
1223
+ olderEvent && olderEvent.missingManagedKeyLabels
1224
+ );
1225
+ const persistentKeyLabels = [];
1226
+ const resolvedKeyLabels = [];
1227
+ olderMissingManagedKeyLabels.forEach((label) => {
1228
+ if (newerMissingManagedKeyLabels.includes(label)) {
1229
+ persistentKeyLabels.push(label);
1230
+ summary.persistentKeyCounts[label] += 1;
1231
+ summary.transitionCount += 1;
1232
+ return;
1233
+ }
1234
+ resolvedKeyLabels.push(label);
1235
+ summary.resolvedKeyCounts[label] += 1;
1236
+ summary.transitionCount += 1;
1237
+ });
1238
+ if (!summary.lastTransition && (resolvedKeyLabels.length > 0 || persistentKeyLabels.length > 0)) {
1239
+ summary.lastTransition = buildManagedConversationLastTransition(
1240
+ atomId,
1241
+ newerEvent,
1242
+ olderEvent,
1243
+ resolvedKeyLabels,
1244
+ persistentKeyLabels
1245
+ );
1246
+ }
1247
+ }
1248
+ });
1249
+
1250
+ summary.resolvedFollowUpActionIds = resolveManagedConversationFollowUpActionIdsFromKeyCounts(
1251
+ summary.resolvedKeyCounts
1252
+ );
1253
+ summary.resolvedFollowUpActionLabels = resolveManagedConversationFollowUpActionLabels(
1254
+ summary.resolvedFollowUpActionIds
1255
+ );
1256
+ summary.persistentFollowUpActionIds = resolveManagedConversationFollowUpActionIdsFromKeyCounts(
1257
+ summary.persistentKeyCounts
1258
+ );
1259
+ summary.persistentFollowUpActionLabels = resolveManagedConversationFollowUpActionLabels(
1260
+ summary.persistentFollowUpActionIds
1261
+ );
1262
+
1263
+ return summary;
1264
+ }
1265
+
1266
+ function computeManagedConversationSummary() {
1267
+ const byActionId = createEmptyManagedConversationActionCounts();
1268
+ const matchedKeyCounts = createEmptyManagedConversationKeyCounts();
1269
+ const missingKeyCounts = createEmptyManagedConversationKeyCounts();
1270
+ const continuitySummary = computeManagedConversationContinuitySummary(diagnostics.capabilityEvents, false);
1271
+ let executionCount = 0;
1272
+ let successCount = 0;
1273
+ let failureCount = 0;
1274
+ let lastEvent = null;
1275
+
1276
+ diagnostics.capabilityEvents.forEach((event) => {
1277
+ const actionId = trimString(event && event.actionId);
1278
+ if (!isManagedConversationActionId(actionId) || trimString(event && event.phase) !== 'result') {
1279
+ return;
1280
+ }
1281
+
1282
+ executionCount += 1;
1283
+ byActionId[actionId] += 1;
1284
+ incrementManagedConversationKeyCounts(matchedKeyCounts, event && event.matchedManagedKeyLabels);
1285
+ incrementManagedConversationKeyCounts(missingKeyCounts, event && event.missingManagedKeyLabels);
1286
+
1287
+ const status = trimString(event && event.status);
1288
+ if (status === 'success') {
1289
+ successCount += 1;
1290
+ } else if (status === 'failure' || status === 'failed' || status === 'error') {
1291
+ failureCount += 1;
1292
+ }
1293
+
1294
+ lastEvent = { ...event };
1295
+ });
1296
+
1297
+ return {
1298
+ executionCount,
1299
+ successCount,
1300
+ failureCount,
1301
+ byActionId,
1302
+ matchedKeyCounts,
1303
+ missingKeyCounts,
1304
+ continuitySummary,
1305
+ lastEvent,
1306
+ };
1307
+ }
1308
+
1309
+ function computeManagedConversationActionStats() {
1310
+ const statsByActionId = Object.create(null);
1311
+ MANAGED_CONVERSATION_ACTION_IDS.forEach((actionId) => {
1312
+ statsByActionId[actionId] = {
1313
+ actionId,
1314
+ requestCount: 0,
1315
+ resultCount: 0,
1316
+ successCount: 0,
1317
+ failureCount: 0,
1318
+ pendingCount: 0,
1319
+ unknownCount: 0,
1320
+ targetKeyCounts: createEmptyManagedConversationKeyCounts(),
1321
+ matchedKeyCounts: createEmptyManagedConversationKeyCounts(),
1322
+ missingKeyCounts: createEmptyManagedConversationKeyCounts(),
1323
+ latestStatus: '',
1324
+ latestAt: '',
1325
+ };
1326
+ });
1327
+
1328
+ diagnostics.capabilityEvents.forEach((event) => {
1329
+ const actionId = trimString(event && event.actionId);
1330
+ if (!isManagedConversationActionId(actionId)) {
1331
+ return;
1332
+ }
1333
+
1334
+ const phase = trimString(event && event.phase) || 'event';
1335
+ const status = trimString(event && event.status) || 'unknown';
1336
+ const stats = statsByActionId[actionId];
1337
+
1338
+ if (phase === 'request') {
1339
+ stats.requestCount += 1;
1340
+ } else if (phase === 'result') {
1341
+ stats.resultCount += 1;
1342
+ }
1343
+
1344
+ if (status === 'success') {
1345
+ stats.successCount += 1;
1346
+ } else if (status === 'failure' || status === 'failed' || status === 'error') {
1347
+ stats.failureCount += 1;
1348
+ } else if (status === 'pending') {
1349
+ stats.pendingCount += 1;
1350
+ } else {
1351
+ stats.unknownCount += 1;
1352
+ }
1353
+
1354
+ incrementManagedConversationKeyCounts(stats.targetKeyCounts, event && event.managedKeyLabels);
1355
+ incrementManagedConversationKeyCounts(stats.matchedKeyCounts, event && event.matchedManagedKeyLabels);
1356
+ incrementManagedConversationKeyCounts(stats.missingKeyCounts, event && event.missingManagedKeyLabels);
1357
+ stats.latestStatus = status;
1358
+ stats.latestAt = trimString(event && event.at);
1359
+ });
1360
+
1361
+ const actionIds = MANAGED_CONVERSATION_ACTION_IDS.filter((actionId) => {
1362
+ const stats = statsByActionId[actionId];
1363
+ return stats.requestCount > 0 || stats.resultCount > 0;
1364
+ });
1365
+
1366
+ return {
1367
+ actionIds,
1368
+ byActionId: actionIds.reduce((accumulator, actionId) => {
1369
+ const stats = statsByActionId[actionId];
1370
+ accumulator[actionId] = {
1371
+ actionId: stats.actionId,
1372
+ requestCount: stats.requestCount,
1373
+ resultCount: stats.resultCount,
1374
+ successCount: stats.successCount,
1375
+ failureCount: stats.failureCount,
1376
+ pendingCount: stats.pendingCount,
1377
+ unknownCount: stats.unknownCount,
1378
+ targetKeyCounts: { ...stats.targetKeyCounts },
1379
+ matchedKeyCounts: { ...stats.matchedKeyCounts },
1380
+ missingKeyCounts: { ...stats.missingKeyCounts },
1381
+ latestStatus: stats.latestStatus,
1382
+ latestAt: stats.latestAt,
1383
+ };
1384
+ return accumulator;
1385
+ }, Object.create(null)),
1386
+ };
1387
+ }
1388
+
1389
+ function getDiagnosticsSnapshot() {
1390
+ const turnCounts = diagnostics.turnHistory.reduce(
1391
+ (accumulator, turn) => {
1392
+ if (turn.role === 'user') {
1393
+ accumulator.user += 1;
1394
+ } else if (turn.role === 'assistant') {
1395
+ accumulator.assistant += 1;
1396
+ } else if (turn.role === 'system') {
1397
+ accumulator.system += 1;
1398
+ }
1399
+ return accumulator;
1400
+ },
1401
+ { user: 0, assistant: 0, system: 0 }
1402
+ );
1403
+ const latestCapabilityEvent = diagnostics.capabilityEvents.length > 0
1404
+ ? diagnostics.capabilityEvents[diagnostics.capabilityEvents.length - 1]
1405
+ : null;
1406
+ const memoryPolicySummary = computeMemoryPolicySummary();
1407
+ const managedConversationSummary = computeManagedConversationSummary();
1408
+ return {
1409
+ generatedAt: nowIso(),
1410
+ conversationRequests: diagnostics.conversationRequests,
1411
+ replayCandidateTurns: diagnostics.replayCandidateTurns,
1412
+ turnCounts: {
1413
+ ...turnCounts,
1414
+ total: diagnostics.turnHistory.length,
1415
+ },
1416
+ turns: diagnostics.turnHistory.map((turn) => ({ ...turn })),
1417
+ capabilityEvents: diagnostics.capabilityEvents.map((event) => cloneCapabilityEvent(event)),
1418
+ lastCapabilityEvent: latestCapabilityEvent ? cloneCapabilityEvent(latestCapabilityEvent) : null,
1419
+ lastConversation: diagnostics.lastConversation
1420
+ ? { ...diagnostics.lastConversation }
1421
+ : null,
1422
+ lastFoundationReadiness: diagnostics.lastFoundationReadiness
1423
+ ? { ...diagnostics.lastFoundationReadiness }
1424
+ : null,
1425
+ lastFailure: diagnostics.lastFailure
1426
+ ? { ...diagnostics.lastFailure }
1427
+ : null,
1428
+ memoryPolicySummary: {
1429
+ ...memoryPolicySummary,
1430
+ byLayer: { ...memoryPolicySummary.byLayer },
1431
+ byOperation: { ...memoryPolicySummary.byOperation },
1432
+ lastEvent: memoryPolicySummary.lastEvent
1433
+ ? { ...memoryPolicySummary.lastEvent }
1434
+ : null,
1435
+ },
1436
+ managedConversationSummary: {
1437
+ ...managedConversationSummary,
1438
+ byActionId: { ...managedConversationSummary.byActionId },
1439
+ matchedKeyCounts: { ...managedConversationSummary.matchedKeyCounts },
1440
+ missingKeyCounts: { ...managedConversationSummary.missingKeyCounts },
1441
+ continuitySummary: {
1442
+ ...managedConversationSummary.continuitySummary,
1443
+ atomIds: managedConversationSummary.continuitySummary.atomIds.slice(),
1444
+ resolvedKeyCounts: { ...managedConversationSummary.continuitySummary.resolvedKeyCounts },
1445
+ resolvedFollowUpActionIds: managedConversationSummary.continuitySummary.resolvedFollowUpActionIds.slice(),
1446
+ resolvedFollowUpActionLabels: managedConversationSummary.continuitySummary.resolvedFollowUpActionLabels.slice(),
1447
+ persistentKeyCounts: { ...managedConversationSummary.continuitySummary.persistentKeyCounts },
1448
+ persistentFollowUpActionIds: managedConversationSummary.continuitySummary.persistentFollowUpActionIds.slice(),
1449
+ persistentFollowUpActionLabels: managedConversationSummary.continuitySummary.persistentFollowUpActionLabels.slice(),
1450
+ lastTransition: managedConversationSummary.continuitySummary.lastTransition
1451
+ ? {
1452
+ ...managedConversationSummary.continuitySummary.lastTransition,
1453
+ keyLabels: managedConversationSummary.continuitySummary.lastTransition.keyLabels.slice(),
1454
+ resolvedKeyLabels: managedConversationSummary.continuitySummary.lastTransition.resolvedKeyLabels.slice(),
1455
+ persistentKeyLabels: managedConversationSummary.continuitySummary.lastTransition.persistentKeyLabels.slice(),
1456
+ followUpActionIds: managedConversationSummary.continuitySummary.lastTransition.followUpActionIds.slice(),
1457
+ followUpActionLabels: managedConversationSummary.continuitySummary.lastTransition.followUpActionLabels.slice(),
1458
+ resolvedFollowUpActionIds: managedConversationSummary.continuitySummary.lastTransition.resolvedFollowUpActionIds.slice(),
1459
+ resolvedFollowUpActionLabels: managedConversationSummary.continuitySummary.lastTransition.resolvedFollowUpActionLabels.slice(),
1460
+ persistentFollowUpActionIds: managedConversationSummary.continuitySummary.lastTransition.persistentFollowUpActionIds.slice(),
1461
+ persistentFollowUpActionLabels: managedConversationSummary.continuitySummary.lastTransition.persistentFollowUpActionLabels.slice(),
1462
+ }
1463
+ : null,
1464
+ },
1465
+ lastEvent: managedConversationSummary.lastEvent
1466
+ ? { ...managedConversationSummary.lastEvent }
1467
+ : null,
1468
+ },
1469
+ pathState: {
1470
+ visible: state.pathVisible,
1471
+ fullscreen: state.pathFullscreen,
1472
+ atomId: resolveCurrentPathAtomId(),
1473
+ targetId: trimString(state.latestPathRuntimeNodeId),
1474
+ targetLabel: trimString(state.latestPathRuntimeNodeLabel),
1475
+ },
1476
+ latestFocusAtomId: state.latestFocusAtomId,
1477
+ latestKnowledgePoints: Array.isArray(state.latestKnowledgePoints)
1478
+ ? state.latestKnowledgePoints.length
1479
+ : 0,
1480
+ };
1481
+ }
1482
+
1483
+ function getDiagnosticsTrendSnapshot() {
1484
+ const operationStats = computeCapabilityOperationStats();
1485
+ const userTurnCount = diagnostics.turnHistory.reduce(
1486
+ (count, turn) => (turn.role === 'user' ? count + 1 : count),
1487
+ 0
1488
+ );
1489
+ const replayCandidateRate = userTurnCount > 0
1490
+ ? Number((diagnostics.replayCandidateTurns / userTurnCount).toFixed(4))
1491
+ : 0;
1492
+ return {
1493
+ generatedAt: nowIso(),
1494
+ window: {
1495
+ turns: diagnostics.turnHistory.length,
1496
+ capabilityEvents: diagnostics.capabilityEvents.length,
1497
+ },
1498
+ conversationRequests: diagnostics.conversationRequests,
1499
+ userTurns: userTurnCount,
1500
+ replayCandidateTurns: diagnostics.replayCandidateTurns,
1501
+ replayCandidateRate,
1502
+ operationStats: operationStats.map((stats) => ({
1503
+ operationId: stats.operationId,
1504
+ requestCount: stats.requestCount,
1505
+ resultCount: stats.resultCount,
1506
+ successCount: stats.successCount,
1507
+ failureCount: stats.failureCount,
1508
+ pendingCount: stats.pendingCount,
1509
+ unknownCount: stats.unknownCount,
1510
+ averageDurationMs: stats.averageDurationMs,
1511
+ latestStatus: stats.latestStatus,
1512
+ latestAt: stats.latestAt,
1513
+ })),
1514
+ lastFailure: diagnostics.lastFailure
1515
+ ? { ...diagnostics.lastFailure }
1516
+ : null,
1517
+ };
1518
+ }
1519
+
1520
+ function getDiagnosticsIndexSnapshot() {
1521
+ const capabilityOperationStats = computeCapabilityOperationStats();
1522
+ const capabilityOperationIds = capabilityOperationStats.map((stats) => stats.operationId);
1523
+ const byOperationId = capabilityOperationStats.reduce((accumulator, stats) => {
1524
+ accumulator[stats.operationId] = {
1525
+ operationId: stats.operationId,
1526
+ eventIds: stats.eventIds.slice(),
1527
+ requestCount: stats.requestCount,
1528
+ resultCount: stats.resultCount,
1529
+ successCount: stats.successCount,
1530
+ failureCount: stats.failureCount,
1531
+ pendingCount: stats.pendingCount,
1532
+ unknownCount: stats.unknownCount,
1533
+ averageDurationMs: stats.averageDurationMs,
1534
+ latestStatus: stats.latestStatus,
1535
+ latestAt: stats.latestAt,
1536
+ };
1537
+ return accumulator;
1538
+ }, Object.create(null));
1539
+ const managedConversationActionStats = computeManagedConversationActionStats();
1540
+ const managedConversationSummary = computeManagedConversationSummary();
1541
+
1542
+ return {
1543
+ generatedAt: nowIso(),
1544
+ turnIndex: computeTurnIndex(),
1545
+ capabilityIndex: {
1546
+ operationIds: capabilityOperationIds,
1547
+ byOperationId,
1548
+ },
1549
+ managedConversationIndex: {
1550
+ actionIds: managedConversationActionStats.actionIds.slice(),
1551
+ byActionId: managedConversationActionStats.actionIds.reduce((accumulator, actionId) => {
1552
+ const stats = managedConversationActionStats.byActionId[actionId];
1553
+ accumulator[actionId] = {
1554
+ actionId: stats.actionId,
1555
+ requestCount: stats.requestCount,
1556
+ resultCount: stats.resultCount,
1557
+ successCount: stats.successCount,
1558
+ failureCount: stats.failureCount,
1559
+ pendingCount: stats.pendingCount,
1560
+ unknownCount: stats.unknownCount,
1561
+ targetKeyCounts: { ...stats.targetKeyCounts },
1562
+ matchedKeyCounts: { ...stats.matchedKeyCounts },
1563
+ missingKeyCounts: { ...stats.missingKeyCounts },
1564
+ latestStatus: stats.latestStatus,
1565
+ latestAt: stats.latestAt,
1566
+ };
1567
+ return accumulator;
1568
+ }, Object.create(null)),
1569
+ continuitySummary: {
1570
+ ...managedConversationSummary.continuitySummary,
1571
+ atomIds: managedConversationSummary.continuitySummary.atomIds.slice(),
1572
+ resolvedKeyCounts: { ...managedConversationSummary.continuitySummary.resolvedKeyCounts },
1573
+ resolvedFollowUpActionIds: managedConversationSummary.continuitySummary.resolvedFollowUpActionIds.slice(),
1574
+ resolvedFollowUpActionLabels: managedConversationSummary.continuitySummary.resolvedFollowUpActionLabels.slice(),
1575
+ persistentKeyCounts: { ...managedConversationSummary.continuitySummary.persistentKeyCounts },
1576
+ persistentFollowUpActionIds: managedConversationSummary.continuitySummary.persistentFollowUpActionIds.slice(),
1577
+ persistentFollowUpActionLabels: managedConversationSummary.continuitySummary.persistentFollowUpActionLabels.slice(),
1578
+ lastTransition: managedConversationSummary.continuitySummary.lastTransition
1579
+ ? {
1580
+ ...managedConversationSummary.continuitySummary.lastTransition,
1581
+ keyLabels: managedConversationSummary.continuitySummary.lastTransition.keyLabels.slice(),
1582
+ resolvedKeyLabels: managedConversationSummary.continuitySummary.lastTransition.resolvedKeyLabels.slice(),
1583
+ persistentKeyLabels: managedConversationSummary.continuitySummary.lastTransition.persistentKeyLabels.slice(),
1584
+ followUpActionIds: managedConversationSummary.continuitySummary.lastTransition.followUpActionIds.slice(),
1585
+ followUpActionLabels: managedConversationSummary.continuitySummary.lastTransition.followUpActionLabels.slice(),
1586
+ resolvedFollowUpActionIds: managedConversationSummary.continuitySummary.lastTransition.resolvedFollowUpActionIds.slice(),
1587
+ resolvedFollowUpActionLabels: managedConversationSummary.continuitySummary.lastTransition.resolvedFollowUpActionLabels.slice(),
1588
+ persistentFollowUpActionIds: managedConversationSummary.continuitySummary.lastTransition.persistentFollowUpActionIds.slice(),
1589
+ persistentFollowUpActionLabels: managedConversationSummary.continuitySummary.lastTransition.persistentFollowUpActionLabels.slice(),
1590
+ }
1591
+ : null,
1592
+ },
1593
+ },
1594
+ };
1595
+ }
1596
+
1597
+ function exportDiagnosticsReport(options = {}) {
1598
+ const format = trimString(options && options.format).toLowerCase();
1599
+ const runbookResolution = resolvePrimaryRunbookAction();
1600
+ const report = {
1601
+ generatedAt: nowIso(),
1602
+ snapshot: getDiagnosticsSnapshot(),
1603
+ trend: getDiagnosticsTrendSnapshot(),
1604
+ index: getDiagnosticsIndexSnapshot(),
1605
+ runbookLinks: cloneRunbookLinks(),
1606
+ primaryRunbookAction: runbookResolution.primaryRunbookAction,
1607
+ runbookActions: runbookResolution.runbookActions,
1608
+ };
1609
+ if (format === 'json') {
1610
+ return JSON.stringify(report, null, 2);
1611
+ }
1612
+ return report;
1613
+ }
1614
+
1615
+ function resolveCapabilityResultPreviewData(presentation, result) {
1616
+ const normalizedPresentation = trimString(presentation);
1617
+ if (!normalizedPresentation) {
1618
+ return null;
1619
+ }
1620
+ if (normalizedPresentation === 'learning_path_card') {
1621
+ return {
1622
+ kind: 'learning_path',
1623
+ masteryPathCount: Array.isArray(result && result.masteryPaths) ? result.masteryPaths.length : 0,
1624
+ divergencePathCount: Array.isArray(result && result.divergencePaths) ? result.divergencePaths.length : 0,
1625
+ };
1626
+ }
1627
+ if (normalizedPresentation === 'study_session_card') {
1628
+ const summary = result && result.summary && typeof result.summary === 'object'
1629
+ ? result.summary
1630
+ : {};
1631
+ return {
1632
+ kind: 'study_session',
1633
+ totalActions: Number.isFinite(Number(summary.totalActions)) ? Number(summary.totalActions) : 0,
1634
+ totalEstimatedMinutes: Number.isFinite(Number(summary.totalEstimatedMinutes))
1635
+ ? Number(summary.totalEstimatedMinutes)
1636
+ : 0,
1637
+ };
1638
+ }
1639
+ if (normalizedPresentation === 'query_trace_card') {
1640
+ const retrievalModes = Array.isArray(result && result.trace && result.trace.retrievalModes)
1641
+ ? result.trace.retrievalModes
1642
+ : [];
1643
+ return {
1644
+ kind: 'query_trace',
1645
+ itemsCount: Array.isArray(result && result.items) ? result.items.length : 0,
1646
+ retrievalModesCount: retrievalModes.length,
1647
+ };
1648
+ }
1649
+ if (normalizedPresentation === 'ingest_guardrail_card') {
1650
+ const summary = result && result.summary && typeof result.summary === 'object'
1651
+ ? result.summary
1652
+ : {};
1653
+ return {
1654
+ kind: 'ingest_guardrail',
1655
+ status: trimString(result && result.status) || trimString(summary.status) || 'unknown',
1656
+ failedGates: Number.isFinite(Number(summary.failedGates)) ? Number(summary.failedGates) : 0,
1657
+ totalGates: Number.isFinite(Number(summary.totalGates)) ? Number(summary.totalGates) : 0,
1658
+ };
1659
+ }
1660
+ if (normalizedPresentation === 'session_history_card') {
1661
+ const summary = result && result.summary && typeof result.summary === 'object'
1662
+ ? result.summary
1663
+ : {};
1664
+ return {
1665
+ kind: 'session_history',
1666
+ totalRecords: Number.isFinite(Number(summary.totalRecords)) ? Number(summary.totalRecords) : 0,
1667
+ totalExecutedActions: Number.isFinite(Number(summary.totalExecutedActions))
1668
+ ? Number(summary.totalExecutedActions)
1669
+ : 0,
1670
+ };
1671
+ }
1672
+ if (normalizedPresentation === 'mastery_misconceptions_card') {
1673
+ const summary = result && result.summary && typeof result.summary === 'object'
1674
+ ? result.summary
1675
+ : {};
1676
+ return {
1677
+ kind: 'mastery_misconceptions',
1678
+ trackedTags: Number.isFinite(Number(summary.trackedTags)) ? Number(summary.trackedTags) : 0,
1679
+ totalObservations: Number.isFinite(Number(summary.totalObservations))
1680
+ ? Number(summary.totalObservations)
1681
+ : 0,
1682
+ };
1683
+ }
1684
+ if (normalizedPresentation === 'learning_quality_snapshot_card') {
1685
+ const snapshot = result && result.snapshot && typeof result.snapshot === 'object'
1686
+ ? result.snapshot
1687
+ : {};
1688
+ return {
1689
+ kind: 'learning_quality',
1690
+ retestPassRatePct: Number.isFinite(Number(snapshot.retestPassRatePct))
1691
+ ? Number(Number(snapshot.retestPassRatePct).toFixed(1))
1692
+ : 0,
1693
+ misconceptionRecurrenceRatePct: Number.isFinite(Number(snapshot.misconceptionRecurrenceRatePct))
1694
+ ? Number(Number(snapshot.misconceptionRecurrenceRatePct).toFixed(1))
1695
+ : 0,
1696
+ };
1697
+ }
1698
+ if (normalizedPresentation === 'memory_policy_card') {
1699
+ return {
1700
+ kind: 'memory_policy',
1701
+ layer: trimString(result && result.layer) || 'session',
1702
+ operation: trimString(result && result.operation) || 'snapshot',
1703
+ entriesCount: Array.isArray(result && result.entries) ? result.entries.length : 0,
1704
+ mutatedCount: Number.isFinite(Number(result && result.mutatedCount))
1705
+ ? Number(result && result.mutatedCount)
1706
+ : Array.isArray(result && result.entries) ? result.entries.length : 0,
1707
+ evictedCount: Number.isFinite(Number(result && result.evictedCount)) ? Number(result && result.evictedCount) : 0,
1708
+ };
1709
+ }
1710
+ if (normalizedPresentation === 'tutor_action_card' || normalizedPresentation === 'assistant_message') {
1711
+ return {
1712
+ kind: 'message',
1713
+ message: trimString(result && result.message),
1714
+ };
1715
+ }
1716
+ return null;
1717
+ }
1718
+
1719
+ async function persistDiagnosticsReport(options = {}) {
1720
+ const endpoint = trimString(options && options.endpoint)
1721
+ || '/api/knowledge/operator/agent-workspace-diagnostics/report';
1722
+ const source = trimString(options && options.source) || 'agent-workspace-runtime';
1723
+ const report = exportDiagnosticsReport();
1724
+ return requestJson(endpoint, {
1725
+ source,
1726
+ report,
1727
+ });
1728
+ }
1729
+
1730
+ function resolveUserId() {
1731
+ const fallback = trimString(options.defaultUserId) || 'agent_user_default';
1732
+ const candidate = trimString(dom.userIdInput && dom.userIdInput.value);
1733
+ return candidate || fallback;
1734
+ }
1735
+
1736
+ function appendMessage(role, text, meta = {}) {
1737
+ if (!dom.messages) {
1738
+ return;
1739
+ }
1740
+ const content = trimString(text);
1741
+ if (!content) {
1742
+ return;
1743
+ }
1744
+ const item = document.createElement('div');
1745
+ item.className = `agent-workspace-message agent-workspace-message--${role}`;
1746
+ item.textContent = content;
1747
+ dom.messages.appendChild(item);
1748
+ dom.messages.scrollTop = dom.messages.scrollHeight;
1749
+ recordTurn(role, content, meta);
1750
+ }
1751
+
1752
+ function setBusy(nextBusy) {
1753
+ state.busy = Boolean(nextBusy);
1754
+ if (dom.sendButton) {
1755
+ dom.sendButton.disabled = state.busy;
1756
+ }
1757
+ if (dom.form) {
1758
+ dom.form.classList.toggle('is-busy', state.busy);
1759
+ }
1760
+ }
1761
+
1762
+ function findKnowledgePoint(atomId) {
1763
+ const normalized = trimString(atomId);
1764
+ if (!normalized) {
1765
+ return null;
1766
+ }
1767
+ return state.latestKnowledgePoints.find((point) => trimString(point.atomId) === normalized) || null;
1768
+ }
1769
+
1770
+ function normalizeRuntimeGraphNode(node) {
1771
+ if (!node || typeof node !== 'object') {
1772
+ return null;
1773
+ }
1774
+ const id = trimString(node.id) || trimString(node.nodeId) || trimString(node.key);
1775
+ if (!id) {
1776
+ return null;
1777
+ }
1778
+ return {
1779
+ id,
1780
+ label: trimString(node.label) || trimString(node.title) || trimString(node.name) || id,
1781
+ };
1782
+ }
1783
+
1784
+ function resolveGraphViewApi() {
1785
+ if (globalScope.NoteConnectionGraphView && typeof globalScope.NoteConnectionGraphView === 'object') {
1786
+ return globalScope.NoteConnectionGraphView;
1787
+ }
1788
+ if (
1789
+ globalScope.window
1790
+ && globalScope.window.NoteConnectionGraphView
1791
+ && typeof globalScope.window.NoteConnectionGraphView === 'object'
1792
+ ) {
1793
+ return globalScope.window.NoteConnectionGraphView;
1794
+ }
1795
+ return null;
1796
+ }
1797
+
1798
+ function resolveGraphTargetForKnowledgePoint(point, capability, preferredAtomId) {
1799
+ const request = capability && capability.request && typeof capability.request === 'object'
1800
+ ? capability.request
1801
+ : {};
1802
+ const atomId = trimString(preferredAtomId) || trimString(request.atomId) || trimString(point && point.atomId);
1803
+ const title = trimString(point && point.title)
1804
+ || trimString(point && point.label)
1805
+ || trimString(point && point.name)
1806
+ || trimString(request.title)
1807
+ || atomId;
1808
+ const explicitGraphNodeId = trimString(point && point.graphNodeId)
1809
+ || trimString(point && point.graphTargetId)
1810
+ || trimString(request.graphNodeId)
1811
+ || trimString(request.graphTargetId);
1812
+ const graphView = resolveGraphViewApi();
1813
+ if (graphView && typeof graphView.resolveNodeByKnowledgePoint === 'function') {
1814
+ try {
1815
+ const resolvedNode = normalizeRuntimeGraphNode(graphView.resolveNodeByKnowledgePoint({
1816
+ atomId,
1817
+ title,
1818
+ label: title,
1819
+ documentId: trimString(point && point.documentId) || trimString(request.documentId),
1820
+ sourcePath: trimString(point && point.sourcePath) || trimString(request.sourcePath),
1821
+ relationPath: Array.isArray(point && point.relationPath)
1822
+ ? point.relationPath.map((edge) => ({ ...edge }))
1823
+ : [],
1824
+ capability: capability && typeof capability === 'object' ? { ...capability } : null,
1825
+ }));
1826
+ if (resolvedNode) {
1827
+ return {
1828
+ atomId,
1829
+ graphNodeId: resolvedNode.id,
1830
+ graphNodeLabel: resolvedNode.label,
1831
+ runtimeResolved: true,
1832
+ };
1833
+ }
1834
+ } catch (error) {
1835
+ console.warn('[AgentWorkspace] graph target resolver rejected runtime knowledge point:', error);
1836
+ }
1837
+ }
1838
+
1839
+ if (graphView && typeof graphView.resolveNodeById === 'function') {
1840
+ const candidates = [explicitGraphNodeId, atomId, title].map(trimString).filter(Boolean);
1841
+ for (const candidate of candidates) {
1842
+ try {
1843
+ const resolvedNode = normalizeRuntimeGraphNode(graphView.resolveNodeById(candidate));
1844
+ if (resolvedNode) {
1845
+ return {
1846
+ atomId,
1847
+ graphNodeId: resolvedNode.id,
1848
+ graphNodeLabel: resolvedNode.label,
1849
+ runtimeResolved: true,
1850
+ };
1851
+ }
1852
+ } catch (error) {
1853
+ console.warn('[AgentWorkspace] graph node id resolver rejected runtime candidate:', error);
1854
+ }
1855
+ }
1856
+ }
1857
+
1858
+ const fallbackGraphNodeId = explicitGraphNodeId || atomId;
1859
+ return {
1860
+ atomId,
1861
+ graphNodeId: fallbackGraphNodeId,
1862
+ graphNodeLabel: title || fallbackGraphNodeId,
1863
+ runtimeResolved: false,
1864
+ };
1865
+ }
1866
+
1867
+ function focusAtom(atomId, point, capability) {
1868
+ const normalizedAtomId = trimString(atomId);
1869
+ if (!normalizedAtomId) {
1870
+ throw new Error('Missing atomId for focus action.');
1871
+ }
1872
+
1873
+ const graphTarget = resolveGraphTargetForKnowledgePoint(
1874
+ point || findKnowledgePoint(normalizedAtomId),
1875
+ capability,
1876
+ normalizedAtomId
1877
+ );
1878
+ const runtimeNodeId = trimString(graphTarget.graphNodeId) || normalizedAtomId;
1879
+ const graphView = resolveGraphViewApi();
1880
+ let opened = false;
1881
+ if (
1882
+ graphTarget.runtimeResolved
1883
+ && graphView
1884
+ && typeof graphView.openFocusModeById === 'function'
1885
+ ) {
1886
+ opened = graphView.openFocusModeById(runtimeNodeId) === true;
1887
+ }
1888
+ if (!opened && typeof globalScope.focusOnNode === 'function') {
1889
+ globalScope.focusOnNode(runtimeNodeId);
1890
+ opened = true;
1891
+ } else if (typeof globalScope.enterFocusMode === 'function') {
1892
+ globalScope.enterFocusMode(runtimeNodeId);
1893
+ opened = true;
1894
+ }
1895
+ if (!opened) {
1896
+ throw new Error('Focus runtime is unavailable. Ensure app.js is loaded.');
1897
+ }
1898
+ state.latestFocusAtomId = normalizedAtomId;
1899
+ appendMessage(
1900
+ 'system',
1901
+ getI18nText('agentWorkspace.messages.focusModeOpened', `Focus mode opened for ${graphTarget.graphNodeLabel || normalizedAtomId}.`, {
1902
+ atomId: graphTarget.graphNodeLabel || normalizedAtomId,
1903
+ })
1904
+ );
1905
+ renderKnowledgePoints(state.latestKnowledgePoints);
1906
+ }
1907
+
1908
+ function resolveCurrentPathAtomId() {
1909
+ return trimString(state.latestPathAtomId);
1910
+ }
1911
+
1912
+ function hidePathDock() {
1913
+ const body = document.body;
1914
+ const pathContainer = document.getElementById('path-container');
1915
+ body.classList.remove(BODY_CLASS_PATH_VISIBLE);
1916
+ body.classList.remove(BODY_CLASS_PATH_FULLSCREEN);
1917
+ if (pathContainer) {
1918
+ pathContainer.classList.remove(PATH_DOCK_CLASS);
1919
+ pathContainer.style.display = 'none';
1920
+ }
1921
+ state.pathVisible = false;
1922
+ state.pathFullscreen = false;
1923
+ state.latestPathAtomId = '';
1924
+ state.latestPathRuntimeNodeId = '';
1925
+ state.latestPathRuntimeNodeLabel = '';
1926
+ refreshToolbarButtons();
1927
+ renderKnowledgePoints(state.latestKnowledgePoints);
1928
+ if (globalScope.pathApp && typeof globalScope.pathApp.requestBridgeWindowVisibility === 'function') {
1929
+ void globalScope.pathApp.requestBridgeWindowVisibility(false, {
1930
+ waitMs: 900,
1931
+ reason: 'agent-workspace-hide-godot-future-path',
1932
+ });
1933
+ }
1934
+ globalScope.dispatchEvent(new Event('resize'));
1935
+ }
1936
+
1937
+ function refreshToolbarButtons() {
1938
+ if (dom.closePathButton) {
1939
+ dom.closePathButton.disabled = !state.pathVisible;
1940
+ }
1941
+ if (dom.pathFullscreenButton) {
1942
+ dom.pathFullscreenButton.disabled = !state.pathVisible;
1943
+ dom.pathFullscreenButton.textContent = getI18nText(
1944
+ 'agentWorkspace.actions.focusGodotFuturePath',
1945
+ 'Focus Godot Future Path'
1946
+ );
1947
+ }
1948
+ if (dom.foundationReadinessButton) {
1949
+ dom.foundationReadinessButton.textContent = getI18nText(
1950
+ 'agentWorkspace.actions.openFoundationReadiness',
1951
+ 'Foundation Readiness'
1952
+ );
1953
+ }
1954
+ }
1955
+
1956
+ function togglePathFullscreen() {
1957
+ if (!state.pathVisible) {
1958
+ return;
1959
+ }
1960
+ state.pathFullscreen = false;
1961
+ document.body.classList.remove(BODY_CLASS_PATH_FULLSCREEN);
1962
+ if (globalScope.pathApp && typeof globalScope.pathApp.requestBridgeWindowVisibility === 'function') {
1963
+ void globalScope.pathApp.requestBridgeWindowVisibility(true, {
1964
+ waitMs: 900,
1965
+ reason: 'agent-workspace-focus-godot-future-path',
1966
+ });
1967
+ }
1968
+ refreshToolbarButtons();
1969
+ renderKnowledgePoints(state.latestKnowledgePoints);
1970
+ globalScope.dispatchEvent(new Event('resize'));
1971
+ }
1972
+
1973
+ function resolvePathAtomId(preferredAtomId) {
1974
+ const preferred = trimString(preferredAtomId);
1975
+ if (preferred) {
1976
+ return preferred;
1977
+ }
1978
+ if (trimString(state.latestFocusAtomId)) {
1979
+ return state.latestFocusAtomId;
1980
+ }
1981
+ const firstPoint = state.latestKnowledgePoints[0];
1982
+ return trimString(firstPoint && firstPoint.atomId);
1983
+ }
1984
+
1985
+ function openLearningPathDock(preferredAtomId, point, capability) {
1986
+ const atomId = resolvePathAtomId(preferredAtomId);
1987
+ const graphTarget = resolveGraphTargetForKnowledgePoint(point || findKnowledgePoint(atomId), capability, atomId);
1988
+ const runtimeNodeId = trimString(graphTarget.graphNodeId) || atomId;
1989
+ const futurePathConfig = {
1990
+ mode: 'diffusion',
1991
+ strategy: 'core',
1992
+ layout: 'orbital',
1993
+ targetId: runtimeNodeId,
1994
+ target_id: runtimeNodeId,
1995
+ targetIds: runtimeNodeId ? [runtimeNodeId] : [],
1996
+ focus_mode: true,
1997
+ };
1998
+ const pathApp = globalScope.pathApp && typeof globalScope.pathApp === 'object'
1999
+ ? globalScope.pathApp
2000
+ : null;
2001
+ if (pathApp) {
2002
+ if (!pathApp.runtimeConfig || typeof pathApp.runtimeConfig !== 'object') {
2003
+ pathApp.runtimeConfig = {};
2004
+ }
2005
+ pathApp.runtimeConfig.mode = 'diffusion';
2006
+ pathApp.runtimeConfig.strategy = 'core';
2007
+ pathApp.runtimeConfig.layout = 'orbital';
2008
+ pathApp.runtimeConfig.targetId = runtimeNodeId;
2009
+ pathApp.runtimeConfig.targetIds = runtimeNodeId ? [runtimeNodeId] : [];
2010
+ pathApp.currentTargetId = runtimeNodeId;
2011
+ pathApp.currentTargetIds = runtimeNodeId ? [runtimeNodeId] : [];
2012
+ pathApp.centralNodeId = runtimeNodeId;
2013
+ }
2014
+ const pathModeApi = globalScope.NoteConnectionPathMode;
2015
+ if (pathModeApi && typeof pathModeApi.openGodotFuturePathById === 'function') {
2016
+ void pathModeApi.openGodotFuturePathById(runtimeNodeId, {
2017
+ config: futurePathConfig,
2018
+ source: 'agent-workspace-runtime',
2019
+ });
2020
+ } else if (pathApp) {
2021
+ if (!globalScope.__NC_AGENT_GODOT_FUTURE_PATH_INITIALIZED && typeof pathApp.init === 'function') {
2022
+ pathApp.init(runtimeNodeId || null);
2023
+ globalScope.__NC_AGENT_GODOT_FUTURE_PATH_INITIALIZED = true;
2024
+ }
2025
+ if (typeof pathApp.applyRemoteConfigure === 'function') {
2026
+ pathApp.applyRemoteConfigure(futurePathConfig);
2027
+ }
2028
+ if (typeof pathApp._sendBridgeMessage === 'function') {
2029
+ pathApp._sendBridgeMessage('configure', futurePathConfig);
2030
+ }
2031
+ if (typeof pathApp.triggerUpdate === 'function') {
2032
+ pathApp.triggerUpdate();
2033
+ }
2034
+ if (typeof pathApp.requestBridgeWindowVisibility === 'function') {
2035
+ void pathApp.requestBridgeWindowVisibility(true, {
2036
+ waitMs: 1200,
2037
+ reason: 'agent-workspace-open-godot-future-path',
2038
+ });
2039
+ }
2040
+ } else {
2041
+ throw new Error('Godot Path Mode runtime is unavailable. Ensure path_app.js and app.js are loaded.');
2042
+ }
2043
+ if (atomId) {
2044
+ state.latestFocusAtomId = atomId;
2045
+ state.latestPathAtomId = atomId;
2046
+ state.latestPathRuntimeNodeId = runtimeNodeId || atomId;
2047
+ state.latestPathRuntimeNodeLabel = trimString(graphTarget.graphNodeLabel) || runtimeNodeId || atomId;
2048
+ }
2049
+ state.pathVisible = true;
2050
+ state.pathFullscreen = false;
2051
+ document.body.classList.remove(BODY_CLASS_PATH_VISIBLE);
2052
+ document.body.classList.remove(BODY_CLASS_PATH_FULLSCREEN);
2053
+ appendMessage(
2054
+ 'system',
2055
+ getI18nText('agentWorkspace.messages.learningPathOpened', `Godot Future Path opened${runtimeNodeId ? ` for ${graphTarget.graphNodeLabel || runtimeNodeId}` : ''}.`, {
2056
+ atomId: graphTarget.graphNodeLabel || runtimeNodeId || '',
2057
+ })
2058
+ );
2059
+ refreshToolbarButtons();
2060
+ renderKnowledgePoints(state.latestKnowledgePoints);
2061
+ globalScope.dispatchEvent(new Event('resize'));
2062
+ }
2063
+
2064
+ function hydrateTutorOperationPlan(operationPlan) {
2065
+ const normalizedOperationId = trimString(operationPlan && operationPlan.operationId);
2066
+ if (normalizedOperationId !== 'execute_tutor_action') {
2067
+ return operationPlan;
2068
+ }
2069
+ const body = operationPlan && operationPlan.body && typeof operationPlan.body === 'object'
2070
+ ? operationPlan.body
2071
+ : {};
2072
+ const actionKind = trimString(body.actionKind);
2073
+ if (actionKind !== 'analyze_answer') {
2074
+ return operationPlan;
2075
+ }
2076
+ const existingAnswer = trimString(body.answer);
2077
+ if (existingAnswer) {
2078
+ return operationPlan;
2079
+ }
2080
+ const atomId = trimString(body.atomId);
2081
+ const promptText = getI18nText(
2082
+ 'agentWorkspace.messages.analyzeAnswerPrompt',
2083
+ `Enter learner answer to analyze for ${atomId || 'target atom'}:`,
2084
+ { atomId: atomId || 'target atom' }
2085
+ );
2086
+ const promptFn = globalScope && typeof globalScope.prompt === 'function'
2087
+ ? globalScope.prompt.bind(globalScope)
2088
+ : null;
2089
+ const collectedAnswer = promptFn ? trimString(promptFn(promptText) || '') : '';
2090
+ if (!collectedAnswer) {
2091
+ throw new Error(
2092
+ getI18nText(
2093
+ 'agentWorkspace.messages.analyzeAnswerRequired',
2094
+ 'Analyze answer requires a non-empty learner answer.'
2095
+ )
2096
+ );
2097
+ }
2098
+ return {
2099
+ ...operationPlan,
2100
+ body: {
2101
+ ...body,
2102
+ answer: collectedAnswer,
2103
+ },
2104
+ };
2105
+ }
2106
+
2107
+ function hydrateMemoryPolicyOperationPlan(operationPlan) {
2108
+ const normalizedOperationId = trimString(operationPlan && operationPlan.operationId);
2109
+ if (normalizedOperationId !== 'apply_memory_policy') {
2110
+ return operationPlan;
2111
+ }
2112
+ const body = operationPlan && operationPlan.body && typeof operationPlan.body === 'object'
2113
+ ? operationPlan.body
2114
+ : {};
2115
+ const operation = trimString(body.operation);
2116
+ if (operation !== 'write') {
2117
+ return operationPlan;
2118
+ }
2119
+ const existingEntries = Array.isArray(body.entries) ? body.entries : [];
2120
+ if (existingEntries.length > 0) {
2121
+ return operationPlan;
2122
+ }
2123
+ const promptConfig = operationPlan && operationPlan.promptConfig && typeof operationPlan.promptConfig === 'object'
2124
+ ? operationPlan.promptConfig
2125
+ : {};
2126
+ if (trimString(promptConfig.kind) !== 'memory_write') {
2127
+ throw new Error('Memory write operation requires prompt configuration or request.memoryValue.');
2128
+ }
2129
+ const promptFn = globalScope && typeof globalScope.prompt === 'function'
2130
+ ? globalScope.prompt.bind(globalScope)
2131
+ : null;
2132
+ const promptMessage = trimString(promptConfig.message)
2133
+ || getI18nText(
2134
+ 'agentWorkspace.messages.memoryWritePrompt',
2135
+ 'Enter memory note to store:',
2136
+ {}
2137
+ );
2138
+ const collectedValue = promptFn ? trimString(promptFn(promptMessage) || '') : '';
2139
+ if (!collectedValue) {
2140
+ throw new Error(
2141
+ getI18nText(
2142
+ 'agentWorkspace.messages.memoryWriteRequired',
2143
+ 'Memory write requires a non-empty note.'
2144
+ )
2145
+ );
2146
+ }
2147
+ const entryTemplate = promptConfig.entryTemplate && typeof promptConfig.entryTemplate === 'object'
2148
+ ? promptConfig.entryTemplate
2149
+ : {};
2150
+ const nowIso = new Date().toISOString();
2151
+ return {
2152
+ ...operationPlan,
2153
+ body: {
2154
+ ...body,
2155
+ entries: [
2156
+ {
2157
+ key: trimString(entryTemplate.key) || `conversation_note:${Date.now()}`,
2158
+ value: collectedValue,
2159
+ tags: resolveStringArray(entryTemplate.tags),
2160
+ references: resolveStringArray(entryTemplate.references),
2161
+ createdAt: nowIso,
2162
+ updatedAt: nowIso,
2163
+ },
2164
+ ],
2165
+ },
2166
+ };
2167
+ }
2168
+
2169
+ async function executeCapability(capability, knowledgePoint) {
2170
+ const actionId = trimString(capability && capability.actionId);
2171
+ const request = capability && capability.request && typeof capability.request === 'object'
2172
+ ? capability.request
2173
+ : {};
2174
+ const atomId = trimString(request.atomId) || trimString(knowledgePoint && knowledgePoint.atomId);
2175
+ if (actionId === 'open_focus_mode') {
2176
+ focusAtom(atomId, knowledgePoint, capability);
2177
+ recordCapabilityEvent({
2178
+ phase: 'frontend',
2179
+ status: 'success',
2180
+ actionId,
2181
+ atomId,
2182
+ });
2183
+ return;
2184
+ }
2185
+
2186
+ let operationPlan;
2187
+ try {
2188
+ operationPlan = resolveOperationPlan(capability);
2189
+ operationPlan = hydrateTutorOperationPlan(operationPlan);
2190
+ operationPlan = hydrateMemoryPolicyOperationPlan(operationPlan);
2191
+ } catch (error) {
2192
+ const errorMessage = trimString(error && error.message);
2193
+ recordCapabilityEvent({
2194
+ phase: 'plan',
2195
+ status: 'failed',
2196
+ actionId,
2197
+ atomId,
2198
+ error: errorMessage,
2199
+ });
2200
+ recordFailure('executeCapability:plan', errorMessage);
2201
+ appendMessage(
2202
+ 'assistant',
2203
+ getI18nText('agentWorkspace.messages.operationPlanFailed', 'Unable to build operation request.', {})
2204
+ );
2205
+ throw error;
2206
+ }
2207
+
2208
+ const operationStartedAt = Date.now();
2209
+ const plannedMemoryLayer = operationPlan.operationId === 'apply_memory_policy'
2210
+ ? normalizeMemoryPolicyLayerForDiagnostics(operationPlan.body && operationPlan.body.layer)
2211
+ : '';
2212
+ const plannedMemoryOperation = operationPlan.operationId === 'apply_memory_policy'
2213
+ ? normalizeMemoryPolicyOperationForDiagnostics(operationPlan.body && operationPlan.body.operation)
2214
+ : '';
2215
+ const plannedManagedKeyLabels = operationPlan.operationId === 'apply_memory_policy'
2216
+ ? resolveManagedConversationKeyLabels([
2217
+ ...resolveStringArray(operationPlan.body && operationPlan.body.matchKeys),
2218
+ ...resolveStringArray(operationPlan.body && operationPlan.body.keys),
2219
+ ...(
2220
+ Array.isArray(operationPlan.body && operationPlan.body.entries)
2221
+ ? operationPlan.body.entries
2222
+ .map((entry) => trimString(entry && entry.key))
2223
+ .filter((key) => key.length > 0)
2224
+ : []
2225
+ ),
2226
+ ])
2227
+ : [];
2228
+ recordCapabilityEvent({
2229
+ phase: 'request',
2230
+ status: 'pending',
2231
+ actionId,
2232
+ atomId,
2233
+ operationId: operationPlan.operationId,
2234
+ resultPresentation: operationPlan.resultPresentation,
2235
+ memoryLayer: plannedMemoryLayer,
2236
+ memoryOperation: plannedMemoryOperation,
2237
+ managedKeyLabels: plannedManagedKeyLabels,
2238
+ });
2239
+
2240
+ let result;
2241
+ try {
2242
+ result = await requestJson(operationPlan.endpoint, operationPlan.body);
2243
+ } catch (error) {
2244
+ const errorMessage = trimString(error && error.message);
2245
+ recordCapabilityEvent({
2246
+ phase: 'result',
2247
+ status: 'failed',
2248
+ actionId,
2249
+ atomId,
2250
+ operationId: operationPlan.operationId,
2251
+ resultPresentation: operationPlan.resultPresentation,
2252
+ durationMs: Date.now() - operationStartedAt,
2253
+ error: errorMessage,
2254
+ memoryLayer: plannedMemoryLayer,
2255
+ memoryOperation: plannedMemoryOperation,
2256
+ managedKeyLabels: plannedManagedKeyLabels,
2257
+ });
2258
+ recordFailure('executeCapability:request', errorMessage);
2259
+ throw error;
2260
+ }
2261
+ const resultFilter = result && result.filter && typeof result.filter === 'object'
2262
+ ? result.filter
2263
+ : {};
2264
+ recordCapabilityEvent({
2265
+ phase: 'result',
2266
+ status: 'success',
2267
+ actionId,
2268
+ atomId,
2269
+ operationId: operationPlan.operationId,
2270
+ resultPresentation: operationPlan.resultPresentation,
2271
+ durationMs: Date.now() - operationStartedAt,
2272
+ memoryLayer: operationPlan.operationId === 'apply_memory_policy'
2273
+ ? normalizeMemoryPolicyLayerForDiagnostics(result && result.layer) || plannedMemoryLayer
2274
+ : '',
2275
+ memoryOperation: operationPlan.operationId === 'apply_memory_policy'
2276
+ ? normalizeMemoryPolicyOperationForDiagnostics(result && result.operation) || plannedMemoryOperation
2277
+ : '',
2278
+ managedKeyLabels: plannedManagedKeyLabels,
2279
+ matchedManagedKeyLabels: resolveManagedConversationKeyLabels(resultFilter && resultFilter.matchedKeys),
2280
+ missingManagedKeyLabels: resolveManagedConversationKeyLabels(resultFilter && resultFilter.missingKeys),
2281
+ resultPreview: resolveCapabilityResultPreviewData(operationPlan.resultPresentation, result),
2282
+ });
2283
+ const presentation = trimString(operationPlan.resultPresentation);
2284
+
2285
+ if (presentation === 'learning_path_card') {
2286
+ const masteryPathCount = Array.isArray(result.masteryPaths) ? result.masteryPaths.length : 0;
2287
+ const divergencePathCount = Array.isArray(result.divergencePaths) ? result.divergencePaths.length : 0;
2288
+ appendMessage(
2289
+ 'assistant',
2290
+ getI18nText(
2291
+ 'agentWorkspace.messages.learningPathBuilt',
2292
+ `Learning path generated (mastery ${masteryPathCount}, divergence ${divergencePathCount}).`,
2293
+ { masteryPathCount, divergencePathCount }
2294
+ )
2295
+ );
2296
+ openLearningPathDock(atomId, knowledgePoint, capability);
2297
+ return;
2298
+ }
2299
+
2300
+ if (presentation === 'tutor_action_card') {
2301
+ const tutorMessage = trimString(result && result.message);
2302
+ appendMessage(
2303
+ 'assistant',
2304
+ tutorMessage || getI18nText('agentWorkspace.messages.tutorActionCompleted', 'Tutor action completed.', {})
2305
+ );
2306
+ return;
2307
+ }
2308
+
2309
+ if (presentation === 'study_session_card') {
2310
+ const summary = result && result.summary && typeof result.summary === 'object'
2311
+ ? result.summary
2312
+ : {};
2313
+ const totalActions = Number(summary.totalActions);
2314
+ const totalEstimatedMinutes = Number(summary.totalEstimatedMinutes);
2315
+ appendMessage(
2316
+ 'assistant',
2317
+ getI18nText(
2318
+ 'agentWorkspace.messages.studySessionBuilt',
2319
+ `Study session built (${Number.isFinite(totalActions) ? totalActions : 0} actions, ${Number.isFinite(totalEstimatedMinutes) ? totalEstimatedMinutes : 0} min).`,
2320
+ {
2321
+ totalActions: Number.isFinite(totalActions) ? totalActions : 0,
2322
+ totalEstimatedMinutes: Number.isFinite(totalEstimatedMinutes) ? totalEstimatedMinutes : 0,
2323
+ }
2324
+ )
2325
+ );
2326
+ return;
2327
+ }
2328
+
2329
+ if (presentation === 'query_trace_card') {
2330
+ const trace = result && result.trace && typeof result.trace === 'object'
2331
+ ? result.trace
2332
+ : {};
2333
+ const itemsCount = Array.isArray(result && result.items) ? result.items.length : 0;
2334
+ const retrievalModes = Array.isArray(trace.retrievalModes) ? trace.retrievalModes.join(', ') : '';
2335
+ const vectorAcceleration = trace.vectorAcceleration && typeof trace.vectorAcceleration === 'object'
2336
+ ? trace.vectorAcceleration
2337
+ : null;
2338
+ const vectorMode = vectorAcceleration ? trimString(vectorAcceleration.mode) : '';
2339
+ const vectorStatus = vectorAcceleration ? trimString(vectorAcceleration.status) : '';
2340
+ const vectorCandidateCount = vectorAcceleration ? Number(vectorAcceleration.candidateCount) : NaN;
2341
+ const vectorAccelerationSummary = vectorMode && vectorStatus
2342
+ ? `${vectorMode}/${vectorStatus}${Number.isFinite(vectorCandidateCount) ? `/${vectorCandidateCount}` : ''}`
2343
+ : 'n/a';
2344
+ const latencyMs = Number(trace.latencyMs);
2345
+ const evidenceCoverageRatio = Number(trace.evidenceCoverageRatio);
2346
+ appendMessage(
2347
+ 'assistant',
2348
+ getI18nText(
2349
+ 'agentWorkspace.messages.queryTraceLoaded',
2350
+ `Query trace loaded (${itemsCount} hits, modes ${retrievalModes || 'n/a'}, vector ${vectorAccelerationSummary}, latency ${Number.isFinite(latencyMs) ? latencyMs : 0} ms, evidence ${Number.isFinite(evidenceCoverageRatio) ? evidenceCoverageRatio.toFixed(2) : '0.00'}).`,
2351
+ {
2352
+ itemsCount,
2353
+ retrievalModes: retrievalModes || 'n/a',
2354
+ vectorAcceleration: vectorAccelerationSummary,
2355
+ latencyMs: Number.isFinite(latencyMs) ? latencyMs : 0,
2356
+ evidenceCoverageRatio: Number.isFinite(evidenceCoverageRatio) ? evidenceCoverageRatio.toFixed(2) : '0.00',
2357
+ }
2358
+ )
2359
+ );
2360
+ return;
2361
+ }
2362
+
2363
+ if (presentation === 'ingest_guardrail_card') {
2364
+ const gates = Array.isArray(result && result.gates) ? result.gates : [];
2365
+ const totalGates = gates.length;
2366
+ const failedGates = gates.filter((gate) => gate && gate.passed === false).length;
2367
+ const overallPassed = Boolean(result && result.overallPassed);
2368
+ appendMessage(
2369
+ 'assistant',
2370
+ getI18nText(
2371
+ 'agentWorkspace.messages.ingestGuardrailLoaded',
2372
+ `Ingest guardrails evaluated (${overallPassed ? 'pass' : 'fail'}): ${failedGates}/${totalGates} gates failed.`,
2373
+ {
2374
+ status: overallPassed ? 'pass' : 'fail',
2375
+ failedGates,
2376
+ totalGates,
2377
+ }
2378
+ )
2379
+ );
2380
+ return;
2381
+ }
2382
+
2383
+ if (presentation === 'session_history_card') {
2384
+ const summary = result && result.summary && typeof result.summary === 'object'
2385
+ ? result.summary
2386
+ : {};
2387
+ const totalRecords = Number(summary.totalRecords);
2388
+ const totalExecutedActions = Number(summary.totalExecutedActions);
2389
+ appendMessage(
2390
+ 'assistant',
2391
+ getI18nText(
2392
+ 'agentWorkspace.messages.sessionHistoryLoaded',
2393
+ `Session history loaded (${Number.isFinite(totalRecords) ? totalRecords : 0} sessions, ${Number.isFinite(totalExecutedActions) ? totalExecutedActions : 0} actions).`,
2394
+ {
2395
+ totalRecords: Number.isFinite(totalRecords) ? totalRecords : 0,
2396
+ totalExecutedActions: Number.isFinite(totalExecutedActions) ? totalExecutedActions : 0,
2397
+ }
2398
+ )
2399
+ );
2400
+ return;
2401
+ }
2402
+
2403
+ if (presentation === 'mastery_misconceptions_card') {
2404
+ const summary = result && result.summary && typeof result.summary === 'object'
2405
+ ? result.summary
2406
+ : {};
2407
+ const trackedTags = Number(summary.trackedTags);
2408
+ const totalObservations = Number(summary.totalObservations);
2409
+ const topItem = Array.isArray(result && result.items) && result.items.length > 0
2410
+ ? result.items[0]
2411
+ : null;
2412
+ const topErrorTag = trimString(topItem && topItem.errorTag);
2413
+ appendMessage(
2414
+ 'assistant',
2415
+ getI18nText(
2416
+ 'agentWorkspace.messages.masteryMisconceptionsLoaded',
2417
+ `Mastery misconceptions loaded (${Number.isFinite(trackedTags) ? trackedTags : 0} tags, ${Number.isFinite(totalObservations) ? totalObservations : 0} observations, top "${topErrorTag || 'n/a'}").`,
2418
+ {
2419
+ trackedTags: Number.isFinite(trackedTags) ? trackedTags : 0,
2420
+ totalObservations: Number.isFinite(totalObservations) ? totalObservations : 0,
2421
+ topErrorTag: topErrorTag || 'n/a',
2422
+ }
2423
+ )
2424
+ );
2425
+ return;
2426
+ }
2427
+
2428
+ if (presentation === 'learning_quality_snapshot_card') {
2429
+ const snapshot = result && result.snapshot && typeof result.snapshot === 'object'
2430
+ ? result.snapshot
2431
+ : {};
2432
+ const retestPassRatePct = Number(snapshot.retestPassRatePct);
2433
+ const misconceptionRecurrenceRatePct = Number(snapshot.misconceptionRecurrenceRatePct);
2434
+ appendMessage(
2435
+ 'assistant',
2436
+ getI18nText(
2437
+ 'agentWorkspace.messages.learningQualitySnapshotLoaded',
2438
+ `Learning quality snapshot loaded (retest ${Number.isFinite(retestPassRatePct) ? retestPassRatePct.toFixed(1) : '0.0'}%, recurrence ${Number.isFinite(misconceptionRecurrenceRatePct) ? misconceptionRecurrenceRatePct.toFixed(1) : '0.0'}%).`,
2439
+ {
2440
+ retestPassRatePct: Number.isFinite(retestPassRatePct) ? retestPassRatePct.toFixed(1) : '0.0',
2441
+ misconceptionRecurrenceRatePct: Number.isFinite(misconceptionRecurrenceRatePct)
2442
+ ? misconceptionRecurrenceRatePct.toFixed(1)
2443
+ : '0.0',
2444
+ }
2445
+ )
2446
+ );
2447
+ return;
2448
+ }
2449
+
2450
+ if (presentation === 'memory_policy_card') {
2451
+ const stats = result && result.stats && typeof result.stats === 'object'
2452
+ ? result.stats
2453
+ : {};
2454
+ const filter = result && result.filter && typeof result.filter === 'object'
2455
+ ? result.filter
2456
+ : {};
2457
+ const entriesCount = Array.isArray(result && result.entries) ? result.entries.length : 0;
2458
+ const recommendedActionsCount = Array.isArray(result && result.recommendedActions)
2459
+ ? result.recommendedActions.length
2460
+ : 0;
2461
+ const evictedCount = Number(result && result.evictedCount);
2462
+ const mutatedCount = Number(result && result.mutatedCount);
2463
+ const removedKeysCount = Array.isArray(result && result.removedKeys)
2464
+ ? result.removedKeys.length
2465
+ : 0;
2466
+ const sessionCount = Number(stats.session);
2467
+ const unitCount = Number(stats.unit);
2468
+ const longTermCount = Number(stats.longTerm);
2469
+ const layer = trimString(result && result.layer) || 'session';
2470
+ const operation = trimString(result && result.operation) || 'snapshot';
2471
+ const targetedMatchKeys = resolveStringArray(filter && filter.matchKeys);
2472
+ const matchedKeys = resolveStringArray(filter && filter.matchedKeys);
2473
+ const missingKeys = resolveStringArray(filter && filter.missingKeys);
2474
+ const followUpActionsSummary = summarizeManagedMemoryFollowUpActions(missingKeys);
2475
+ if (operation === 'write') {
2476
+ appendMessage(
2477
+ 'assistant',
2478
+ getI18nText(
2479
+ 'agentWorkspace.messages.memoryWriteLoaded',
2480
+ `Memory updated (${layer}/${operation}): wrote ${Number.isFinite(mutatedCount) ? mutatedCount : entriesCount} entry, totals session ${Number.isFinite(sessionCount) ? sessionCount : 0}, unit ${Number.isFinite(unitCount) ? unitCount : 0}, long-term ${Number.isFinite(longTermCount) ? longTermCount : 0}.`,
2481
+ {
2482
+ layer,
2483
+ operation,
2484
+ mutatedCount: Number.isFinite(mutatedCount) ? mutatedCount : entriesCount,
2485
+ sessionCount: Number.isFinite(sessionCount) ? sessionCount : 0,
2486
+ unitCount: Number.isFinite(unitCount) ? unitCount : 0,
2487
+ longTermCount: Number.isFinite(longTermCount) ? longTermCount : 0,
2488
+ }
2489
+ )
2490
+ );
2491
+ return;
2492
+ }
2493
+ if (operation === 'evict') {
2494
+ appendMessage(
2495
+ 'assistant',
2496
+ getI18nText(
2497
+ 'agentWorkspace.messages.memoryEvictLoaded',
2498
+ `Memory eviction completed (${layer}/${operation}): removed ${Number.isFinite(evictedCount) ? evictedCount : 0} entries, targeted keys ${removedKeysCount}, totals session ${Number.isFinite(sessionCount) ? sessionCount : 0}, unit ${Number.isFinite(unitCount) ? unitCount : 0}, long-term ${Number.isFinite(longTermCount) ? longTermCount : 0}.`,
2499
+ {
2500
+ layer,
2501
+ operation,
2502
+ evictedCount: Number.isFinite(evictedCount) ? evictedCount : 0,
2503
+ removedKeysCount,
2504
+ sessionCount: Number.isFinite(sessionCount) ? sessionCount : 0,
2505
+ unitCount: Number.isFinite(unitCount) ? unitCount : 0,
2506
+ longTermCount: Number.isFinite(longTermCount) ? longTermCount : 0,
2507
+ }
2508
+ )
2509
+ );
2510
+ return;
2511
+ }
2512
+ if (operation === 'read' && targetedMatchKeys.length > 0) {
2513
+ appendMessage(
2514
+ 'assistant',
2515
+ getI18nText(
2516
+ followUpActionsSummary
2517
+ ? 'agentWorkspace.messages.memoryTargetedReadLoadedWithNext'
2518
+ : 'agentWorkspace.messages.memoryTargetedReadLoaded',
2519
+ followUpActionsSummary
2520
+ ? `Memory targeted read loaded (${layer}/${operation}): present ${summarizeMemoryKeyLabels(matchedKeys)}, missing ${summarizeMemoryKeyLabels(missingKeys)}, next ${followUpActionsSummary}, totals session ${Number.isFinite(sessionCount) ? sessionCount : 0}, unit ${Number.isFinite(unitCount) ? unitCount : 0}, long-term ${Number.isFinite(longTermCount) ? longTermCount : 0}.`
2521
+ : `Memory targeted read loaded (${layer}/${operation}): present ${summarizeMemoryKeyLabels(matchedKeys)}, missing ${summarizeMemoryKeyLabels(missingKeys)}, totals session ${Number.isFinite(sessionCount) ? sessionCount : 0}, unit ${Number.isFinite(unitCount) ? unitCount : 0}, long-term ${Number.isFinite(longTermCount) ? longTermCount : 0}.`,
2522
+ {
2523
+ layer,
2524
+ operation,
2525
+ matchedKeysSummary: summarizeMemoryKeyLabels(matchedKeys),
2526
+ missingKeysSummary: summarizeMemoryKeyLabels(missingKeys),
2527
+ nextActionsSummary: followUpActionsSummary,
2528
+ sessionCount: Number.isFinite(sessionCount) ? sessionCount : 0,
2529
+ unitCount: Number.isFinite(unitCount) ? unitCount : 0,
2530
+ longTermCount: Number.isFinite(longTermCount) ? longTermCount : 0,
2531
+ }
2532
+ )
2533
+ );
2534
+ return;
2535
+ }
2536
+ appendMessage(
2537
+ 'assistant',
2538
+ getI18nText(
2539
+ 'agentWorkspace.messages.memoryPolicyLoaded',
2540
+ `Memory snapshot loaded (${layer}/${operation}): ${entriesCount} entries, evicted ${Number.isFinite(evictedCount) ? evictedCount : 0}, totals session ${Number.isFinite(sessionCount) ? sessionCount : 0}, unit ${Number.isFinite(unitCount) ? unitCount : 0}, long-term ${Number.isFinite(longTermCount) ? longTermCount : 0}, recommended ${recommendedActionsCount} actions.`,
2541
+ {
2542
+ layer,
2543
+ operation,
2544
+ entriesCount,
2545
+ recommendedActionsCount,
2546
+ evictedCount: Number.isFinite(evictedCount) ? evictedCount : 0,
2547
+ sessionCount: Number.isFinite(sessionCount) ? sessionCount : 0,
2548
+ unitCount: Number.isFinite(unitCount) ? unitCount : 0,
2549
+ longTermCount: Number.isFinite(longTermCount) ? longTermCount : 0,
2550
+ }
2551
+ )
2552
+ );
2553
+ return;
2554
+ }
2555
+
2556
+ appendMessage(
2557
+ 'assistant',
2558
+ trimString(result && result.message)
2559
+ || getI18nText('agentWorkspace.messages.operationCompleted', 'Operation completed.', {})
2560
+ );
2561
+ }
2562
+
2563
+ function resolveCapabilityLabel(capability) {
2564
+ if (!capability || typeof capability !== 'object') {
2565
+ return getI18nText('agentWorkspace.actions.unknown', 'Action');
2566
+ }
2567
+ const fallback = trimString(capability.label) || getI18nText('agentWorkspace.actions.unknown', 'Action');
2568
+ const labelKey = trimString(capability.labelKey);
2569
+ if (!labelKey) {
2570
+ return fallback;
2571
+ }
2572
+ return getI18nText(labelKey, fallback);
2573
+ }
2574
+
2575
+ function findCapabilityByActionId(point, actionId) {
2576
+ const normalizedActionId = trimString(actionId);
2577
+ if (!normalizedActionId) {
2578
+ return null;
2579
+ }
2580
+ const capabilities = Array.isArray(point && point.capabilities) ? point.capabilities : [];
2581
+ return capabilities.find((capability) => trimString(capability && capability.actionId) === normalizedActionId) || null;
2582
+ }
2583
+
2584
+ function appendPointActionFailure(capability, point, error) {
2585
+ const fallback = trimString(capability && capability.failure && capability.failure.fallbackMessage)
2586
+ || getI18nText('agentWorkspace.messages.operationFailed', 'Operation failed.');
2587
+ appendMessage('assistant', fallback.replace('{title}', trimString(point && point.title) || trimString(point && point.atomId)));
2588
+ console.error('[AgentWorkspace] capability execution failed:', error);
2589
+ }
2590
+
2591
+ async function runCapabilityAction(capability, point) {
2592
+ try {
2593
+ setBusy(true);
2594
+ await executeCapability(capability, point);
2595
+ } catch (error) {
2596
+ appendPointActionFailure(capability, point, error);
2597
+ } finally {
2598
+ setBusy(false);
2599
+ renderKnowledgePoints(state.latestKnowledgePoints);
2600
+ }
2601
+ }
2602
+
2603
+ async function runFallbackPointAction(actionId, point) {
2604
+ const normalizedActionId = trimString(actionId);
2605
+ try {
2606
+ setBusy(true);
2607
+ if (normalizedActionId === 'open_focus_mode') {
2608
+ focusKnowledgePoint(point);
2609
+ return;
2610
+ }
2611
+ if (normalizedActionId === 'open_learning_path') {
2612
+ openLearningPathDock(trimString(point && point.atomId), point);
2613
+ return;
2614
+ }
2615
+ throw new Error(`Unsupported point action "${normalizedActionId || '<empty>'}".`);
2616
+ } catch (error) {
2617
+ appendPointActionFailure(null, point, error);
2618
+ } finally {
2619
+ setBusy(false);
2620
+ }
2621
+ }
2622
+
2623
+ function triggerPointAction(actionId, point) {
2624
+ const capability = findCapabilityByActionId(point, actionId);
2625
+ if (capability) {
2626
+ void runCapabilityAction(capability, point);
2627
+ return;
2628
+ }
2629
+ void runFallbackPointAction(actionId, point);
2630
+ }
2631
+
2632
+ function createPointActionButton(label, onClick) {
2633
+ const button = document.createElement('button');
2634
+ button.type = 'button';
2635
+ button.className = 'agent-workspace-action-button';
2636
+ button.textContent = trimString(label) || getI18nText('agentWorkspace.actions.unknown', 'Action');
2637
+ button.addEventListener('click', (event) => {
2638
+ event.preventDefault();
2639
+ event.stopPropagation();
2640
+ onClick();
2641
+ });
2642
+ return button;
2643
+ }
2644
+
2645
+ function appendPointIdentity(container, point) {
2646
+ const title = document.createElement('h4');
2647
+ title.className = 'agent-workspace-point-title';
2648
+ title.textContent = trimString(point && point.title) || trimString(point && point.atomId) || 'Untitled';
2649
+ container.appendChild(title);
2650
+
2651
+ const snippetText = trimString(point && point.snippet);
2652
+ if (snippetText) {
2653
+ const snippet = document.createElement('p');
2654
+ snippet.className = 'agent-workspace-point-snippet';
2655
+ snippet.textContent = snippetText;
2656
+ container.appendChild(snippet);
2657
+ }
2658
+
2659
+ const meta = document.createElement('div');
2660
+ meta.className = 'agent-workspace-point-meta';
2661
+ const score = Number(point && point.score);
2662
+ meta.textContent = Number.isFinite(score)
2663
+ ? `${getI18nText('agentWorkspace.labels.score', 'Score')}: ${score.toFixed(3)}`
2664
+ : '';
2665
+ if (meta.textContent) {
2666
+ container.appendChild(meta);
2667
+ }
2668
+ }
2669
+
2670
+ function createStatusBadge(label, extraClassName = '') {
2671
+ const badge = document.createElement('span');
2672
+ badge.className = extraClassName
2673
+ ? `agent-workspace-status-badge ${extraClassName}`
2674
+ : 'agent-workspace-status-badge';
2675
+ badge.textContent = label;
2676
+ return badge;
2677
+ }
2678
+
2679
+ function resolveActivePointContinuity(point) {
2680
+ const activeAtomId = trimString(point && point.atomId);
2681
+ const pathAtomId = resolveCurrentPathAtomId();
2682
+ if (!activeAtomId) {
2683
+ return {
2684
+ message: '',
2685
+ showPathBadge: false,
2686
+ pathBadgeLabel: '',
2687
+ };
2688
+ }
2689
+ if (!state.pathVisible || !pathAtomId) {
2690
+ return {
2691
+ message: getI18nText(
2692
+ 'agentWorkspace.messages.activeAtomPathInactive',
2693
+ 'Focus is ready for {atomId}. Open Learning Path to launch Godot Future Path for this atom.',
2694
+ { atomId: activeAtomId }
2695
+ ),
2696
+ showPathBadge: false,
2697
+ pathBadgeLabel: '',
2698
+ };
2699
+ }
2700
+ if (pathAtomId === activeAtomId) {
2701
+ return {
2702
+ message: getI18nText(
2703
+ 'agentWorkspace.messages.activeAtomFuturePathAligned',
2704
+ 'Focus and Godot Future Path target are aligned on {atomId}.',
2705
+ { atomId: activeAtomId }
2706
+ ),
2707
+ showPathBadge: true,
2708
+ pathBadgeLabel: getI18nText('agentWorkspace.labels.godotFuturePathActive', 'Future Path'),
2709
+ };
2710
+ }
2711
+ return {
2712
+ message: getI18nText(
2713
+ 'agentWorkspace.messages.activeAtomFuturePathDrifted',
2714
+ 'Focus is on {atomId}. Godot Future Path is still targeted at {pathAtomId}. Reopen Learning Path to realign.',
2715
+ {
2716
+ atomId: activeAtomId,
2717
+ pathAtomId,
2718
+ }
2719
+ ),
2720
+ showPathBadge: true,
2721
+ pathBadgeLabel: getI18nText('agentWorkspace.labels.godotFuturePathPinned', 'Future Path Pinned'),
2722
+ };
2723
+ }
2724
+
2725
+ function appendActivePointStatus(container, point) {
2726
+ const normalizedAtomId = trimString(point && point.atomId);
2727
+ if (!normalizedAtomId) {
2728
+ return;
2729
+ }
2730
+ const continuity = resolveActivePointContinuity(point);
2731
+ const badges = document.createElement('div');
2732
+ badges.className = 'agent-workspace-status-badges';
2733
+ badges.appendChild(createStatusBadge(
2734
+ getI18nText('agentWorkspace.labels.active', 'Active')
2735
+ ));
2736
+ badges.appendChild(createStatusBadge(
2737
+ getI18nText('agentWorkspace.labels.focusReady', 'Focus Ready')
2738
+ ));
2739
+ if (continuity.showPathBadge && continuity.pathBadgeLabel) {
2740
+ badges.appendChild(createStatusBadge(
2741
+ continuity.pathBadgeLabel
2742
+ ));
2743
+ }
2744
+ container.appendChild(badges);
2745
+ if (continuity.message) {
2746
+ const note = document.createElement('p');
2747
+ note.className = 'agent-workspace-active-point-note';
2748
+ note.textContent = continuity.message;
2749
+ container.appendChild(note);
2750
+ }
2751
+ }
2752
+
2753
+ function resolveActivePointCapabilities(point) {
2754
+ const capabilities = Array.isArray(point && point.capabilities) ? point.capabilities : [];
2755
+ return capabilities
2756
+ .filter((capability) => {
2757
+ const actionId = trimString(capability && capability.actionId);
2758
+ return actionId && actionId !== 'open_focus_mode' && actionId !== 'open_learning_path';
2759
+ })
2760
+ .slice(0, MAX_ACTIVE_POINT_CONTEXTUAL_ACTIONS);
2761
+ }
2762
+
2763
+ function resolveCapabilityCategory(capability) {
2764
+ const actionId = trimString(capability && capability.actionId);
2765
+ if (!actionId) {
2766
+ return 'other';
2767
+ }
2768
+ if (STUDY_LOOP_ACTION_IDS.includes(actionId)) {
2769
+ return 'study';
2770
+ }
2771
+ if (MEMORY_ACTION_IDS.includes(actionId)) {
2772
+ return 'memory';
2773
+ }
2774
+ if (actionId.startsWith('inspect_') || actionId.startsWith('compare_')) {
2775
+ return 'diagnostic';
2776
+ }
2777
+ return 'other';
2778
+ }
2779
+
2780
+ function resolveActivePointCapabilitySummary(point) {
2781
+ const capabilities = Array.isArray(point && point.capabilities) ? point.capabilities : [];
2782
+ return capabilities.reduce((summary, capability) => {
2783
+ const category = resolveCapabilityCategory(capability);
2784
+ if (category === 'study') {
2785
+ summary.study.push(capability);
2786
+ } else if (category === 'memory') {
2787
+ summary.memory.push(capability);
2788
+ } else if (category === 'diagnostic') {
2789
+ summary.diagnostic.push(capability);
2790
+ } else {
2791
+ summary.other.push(capability);
2792
+ }
2793
+ return summary;
2794
+ }, {
2795
+ study: [],
2796
+ memory: [],
2797
+ diagnostic: [],
2798
+ other: [],
2799
+ });
2800
+ }
2801
+
2802
+ function resolveLatestCapabilityEventForAtom(atomId, category) {
2803
+ const normalizedAtomId = trimString(atomId);
2804
+ if (!normalizedAtomId) {
2805
+ return null;
2806
+ }
2807
+ for (let index = diagnostics.capabilityEvents.length - 1; index >= 0; index -= 1) {
2808
+ const event = diagnostics.capabilityEvents[index];
2809
+ if (trimString(event && event.atomId) !== normalizedAtomId) {
2810
+ continue;
2811
+ }
2812
+ if (trimString(event && event.status) !== 'success') {
2813
+ continue;
2814
+ }
2815
+ if (trimString(event && event.phase) === 'request') {
2816
+ continue;
2817
+ }
2818
+ if (category && resolveCapabilityCategory({ actionId: event && event.actionId }) !== category) {
2819
+ continue;
2820
+ }
2821
+ return event;
2822
+ }
2823
+ return null;
2824
+ }
2825
+
2826
+ function resolveRecentCapabilityEventsForAtom(atomId, limit = 3) {
2827
+ const normalizedAtomId = trimString(atomId);
2828
+ const boundedLimit = Math.max(1, Math.floor(Number(limit) || 3));
2829
+ if (!normalizedAtomId) {
2830
+ return [];
2831
+ }
2832
+ const events = [];
2833
+ for (let index = diagnostics.capabilityEvents.length - 1; index >= 0; index -= 1) {
2834
+ const event = diagnostics.capabilityEvents[index];
2835
+ const actionId = trimString(event && event.actionId);
2836
+ const phase = trimString(event && event.phase);
2837
+ const operationId = trimString(event && event.operationId);
2838
+ if (trimString(event && event.atomId) !== normalizedAtomId) {
2839
+ continue;
2840
+ }
2841
+ if (trimString(event && event.status) !== 'success') {
2842
+ continue;
2843
+ }
2844
+ if (!actionId || actionId === 'open_focus_mode') {
2845
+ continue;
2846
+ }
2847
+ if (actionId === 'open_learning_path' && !operationId) {
2848
+ continue;
2849
+ }
2850
+ if (phase === 'request' || phase === 'plan') {
2851
+ continue;
2852
+ }
2853
+ events.push(event);
2854
+ if (events.length >= boundedLimit) {
2855
+ break;
2856
+ }
2857
+ }
2858
+ return events;
2859
+ }
2860
+
2861
+ function resolveCapabilityEventPreviewText(event) {
2862
+ const preview = event && event.resultPreview && typeof event.resultPreview === 'object'
2863
+ ? event.resultPreview
2864
+ : null;
2865
+ if (!preview) {
2866
+ return '';
2867
+ }
2868
+ const kind = trimString(preview.kind);
2869
+ if (kind === 'learning_path') {
2870
+ return getI18nText(
2871
+ 'agentWorkspace.messages.currentAtomHistoryPreviewLearningPath',
2872
+ 'mastery {masteryPathCount} divergence {divergencePathCount}',
2873
+ {
2874
+ masteryPathCount: Number.isFinite(Number(preview.masteryPathCount))
2875
+ ? Number(preview.masteryPathCount)
2876
+ : 0,
2877
+ divergencePathCount: Number.isFinite(Number(preview.divergencePathCount))
2878
+ ? Number(preview.divergencePathCount)
2879
+ : 0,
2880
+ }
2881
+ );
2882
+ }
2883
+ if (kind === 'study_session') {
2884
+ return getI18nText(
2885
+ 'agentWorkspace.messages.currentAtomHistoryPreviewStudySession',
2886
+ '{totalActions} actions {totalEstimatedMinutes} min',
2887
+ {
2888
+ totalActions: Number.isFinite(Number(preview.totalActions)) ? Number(preview.totalActions) : 0,
2889
+ totalEstimatedMinutes: Number.isFinite(Number(preview.totalEstimatedMinutes))
2890
+ ? Number(preview.totalEstimatedMinutes)
2891
+ : 0,
2892
+ }
2893
+ );
2894
+ }
2895
+ if (kind === 'memory_policy') {
2896
+ const operation = trimString(preview.operation) || 'snapshot';
2897
+ if (operation === 'write') {
2898
+ return getI18nText(
2899
+ 'agentWorkspace.messages.currentAtomHistoryPreviewMemoryWrite',
2900
+ '{layer}/{operation} {mutatedCount} written',
2901
+ {
2902
+ layer: trimString(preview.layer) || 'session',
2903
+ operation,
2904
+ mutatedCount: Number.isFinite(Number(preview.mutatedCount)) ? Number(preview.mutatedCount) : 0,
2905
+ }
2906
+ );
2907
+ }
2908
+ if (operation === 'evict') {
2909
+ return getI18nText(
2910
+ 'agentWorkspace.messages.currentAtomHistoryPreviewMemoryEvict',
2911
+ '{layer}/{operation} {evictedCount} evicted',
2912
+ {
2913
+ layer: trimString(preview.layer) || 'session',
2914
+ operation,
2915
+ evictedCount: Number.isFinite(Number(preview.evictedCount)) ? Number(preview.evictedCount) : 0,
2916
+ }
2917
+ );
2918
+ }
2919
+ return getI18nText(
2920
+ 'agentWorkspace.messages.currentAtomHistoryPreviewMemoryPolicy',
2921
+ '{layer}/{operation} {entriesCount} entry',
2922
+ {
2923
+ layer: trimString(preview.layer) || 'session',
2924
+ operation,
2925
+ entriesCount: Number.isFinite(Number(preview.entriesCount)) ? Number(preview.entriesCount) : 0,
2926
+ }
2927
+ );
2928
+ }
2929
+ if (kind === 'query_trace') {
2930
+ return getI18nText(
2931
+ 'agentWorkspace.messages.currentAtomHistoryPreviewQueryTrace',
2932
+ '{itemsCount} hits {retrievalModesCount} modes',
2933
+ {
2934
+ itemsCount: Number.isFinite(Number(preview.itemsCount)) ? Number(preview.itemsCount) : 0,
2935
+ retrievalModesCount: Number.isFinite(Number(preview.retrievalModesCount))
2936
+ ? Number(preview.retrievalModesCount)
2937
+ : 0,
2938
+ }
2939
+ );
2940
+ }
2941
+ if (kind === 'ingest_guardrail') {
2942
+ return getI18nText(
2943
+ 'agentWorkspace.messages.currentAtomHistoryPreviewIngestGuardrail',
2944
+ '{status} {failedGates}/{totalGates} failed',
2945
+ {
2946
+ status: trimString(preview.status) || 'unknown',
2947
+ failedGates: Number.isFinite(Number(preview.failedGates)) ? Number(preview.failedGates) : 0,
2948
+ totalGates: Number.isFinite(Number(preview.totalGates)) ? Number(preview.totalGates) : 0,
2949
+ }
2950
+ );
2951
+ }
2952
+ if (kind === 'session_history') {
2953
+ return getI18nText(
2954
+ 'agentWorkspace.messages.currentAtomHistoryPreviewSessionHistory',
2955
+ '{totalRecords} sessions {totalExecutedActions} actions',
2956
+ {
2957
+ totalRecords: Number.isFinite(Number(preview.totalRecords)) ? Number(preview.totalRecords) : 0,
2958
+ totalExecutedActions: Number.isFinite(Number(preview.totalExecutedActions))
2959
+ ? Number(preview.totalExecutedActions)
2960
+ : 0,
2961
+ }
2962
+ );
2963
+ }
2964
+ if (kind === 'mastery_misconceptions') {
2965
+ return getI18nText(
2966
+ 'agentWorkspace.messages.currentAtomHistoryPreviewMasteryMisconceptions',
2967
+ '{trackedTags} tags {totalObservations} observations',
2968
+ {
2969
+ trackedTags: Number.isFinite(Number(preview.trackedTags)) ? Number(preview.trackedTags) : 0,
2970
+ totalObservations: Number.isFinite(Number(preview.totalObservations))
2971
+ ? Number(preview.totalObservations)
2972
+ : 0,
2973
+ }
2974
+ );
2975
+ }
2976
+ if (kind === 'learning_quality') {
2977
+ return getI18nText(
2978
+ 'agentWorkspace.messages.currentAtomHistoryPreviewLearningQuality',
2979
+ 'retest {retestPassRatePct}% recurrence {misconceptionRecurrenceRatePct}%',
2980
+ {
2981
+ retestPassRatePct: Number.isFinite(Number(preview.retestPassRatePct))
2982
+ ? Number(preview.retestPassRatePct).toFixed(1)
2983
+ : '0.0',
2984
+ misconceptionRecurrenceRatePct: Number.isFinite(Number(preview.misconceptionRecurrenceRatePct))
2985
+ ? Number(preview.misconceptionRecurrenceRatePct).toFixed(1)
2986
+ : '0.0',
2987
+ }
2988
+ );
2989
+ }
2990
+ if (kind === 'message') {
2991
+ return trimString(preview.message);
2992
+ }
2993
+ return '';
2994
+ }
2995
+
2996
+ function resolveExpandedHistoryEventIdForAtom(atomId) {
2997
+ const normalizedAtomId = trimString(atomId);
2998
+ if (!normalizedAtomId) {
2999
+ return '';
3000
+ }
3001
+ return trimString(state.expandedHistoryEventIdsByAtom[normalizedAtomId]);
3002
+ }
3003
+
3004
+ function toggleHistoryEventDetails(atomId, eventId) {
3005
+ const normalizedAtomId = trimString(atomId);
3006
+ const normalizedEventId = trimString(eventId);
3007
+ if (!normalizedAtomId || !normalizedEventId) {
3008
+ return;
3009
+ }
3010
+ const currentExpandedEventId = resolveExpandedHistoryEventIdForAtom(normalizedAtomId);
3011
+ if (currentExpandedEventId === normalizedEventId) {
3012
+ delete state.expandedHistoryEventIdsByAtom[normalizedAtomId];
3013
+ } else {
3014
+ state.expandedHistoryEventIdsByAtom[normalizedAtomId] = normalizedEventId;
3015
+ }
3016
+ renderKnowledgePoints(state.latestKnowledgePoints);
3017
+ }
3018
+
3019
+ function appendHistoryDetailLine(container, label, value) {
3020
+ const normalizedValue = trimString(value);
3021
+ if (!container || !label || !normalizedValue) {
3022
+ return;
3023
+ }
3024
+ const line = document.createElement('p');
3025
+ line.className = 'agent-workspace-history-detail-line';
3026
+ line.textContent = `${label}: ${normalizedValue}`;
3027
+ container.appendChild(line);
3028
+ }
3029
+
3030
+ function resolvePointActionLabelByActionId(point, actionId) {
3031
+ const capability = findCapabilityByActionId(point, actionId);
3032
+ if (capability) {
3033
+ return resolveCapabilityLabel(capability);
3034
+ }
3035
+ if (trimString(actionId) === 'open_focus_mode') {
3036
+ return getI18nText('agentWorkspace.actions.openFocusMode', 'Focus');
3037
+ }
3038
+ if (trimString(actionId) === 'open_learning_path') {
3039
+ return getI18nText('agentWorkspace.actions.openLearningPath', 'Learning Path');
3040
+ }
3041
+ return trimString(actionId) || getI18nText('agentWorkspace.actions.unknown', 'Action');
3042
+ }
3043
+
3044
+ function isFallbackPointActionId(actionId) {
3045
+ const normalizedActionId = trimString(actionId);
3046
+ return normalizedActionId === 'open_focus_mode' || normalizedActionId === 'open_learning_path';
3047
+ }
3048
+
3049
+ function canTriggerPointActionByActionId(point, actionId) {
3050
+ const normalizedActionId = trimString(actionId);
3051
+ if (!normalizedActionId) {
3052
+ return false;
3053
+ }
3054
+ if (findCapabilityByActionId(point, normalizedActionId)) {
3055
+ return true;
3056
+ }
3057
+ return isFallbackPointActionId(normalizedActionId);
3058
+ }
3059
+
3060
+ function resolvePointActionAvailabilityKind(point, actionId) {
3061
+ const normalizedActionId = trimString(actionId);
3062
+ if (!normalizedActionId) {
3063
+ return '';
3064
+ }
3065
+ if (findCapabilityByActionId(point, normalizedActionId)) {
3066
+ return 'typed';
3067
+ }
3068
+ if (isFallbackPointActionId(normalizedActionId)) {
3069
+ return 'fallback';
3070
+ }
3071
+ return '';
3072
+ }
3073
+
3074
+ function resolveManagedMemoryMissingFollowUpActionIds(event) {
3075
+ const labels = resolveManagedConversationKeyLabels(event && event.missingManagedKeyLabels);
3076
+ const actionIds = [];
3077
+ labels.forEach((label) => {
3078
+ if (label === 'note') {
3079
+ actionIds.push('write_memory_note');
3080
+ } else if (label === 'correction') {
3081
+ actionIds.push('record_memory_correction');
3082
+ }
3083
+ });
3084
+ return actionIds;
3085
+ }
3086
+
3087
+ function resolveHistoryFollowUpDescriptor(point, event) {
3088
+ const eventActionId = trimString(event && event.actionId);
3089
+ const eventIsManagedMemoryState = isManagedMemoryStateActionId(eventActionId);
3090
+ const missingManagedKeyLabels = resolveManagedConversationKeyLabels(event && event.missingManagedKeyLabels);
3091
+ const managedMemoryMissingActionIds = resolveManagedMemoryMissingFollowUpActionIds(event);
3092
+ const preferredCandidates = HISTORY_FOLLOW_UP_CANDIDATES_BY_ACTION_ID[eventActionId]
3093
+ || DEFAULT_HISTORY_FOLLOW_UP_CANDIDATES;
3094
+ const actionIds = managedMemoryMissingActionIds.concat(preferredCandidates, DEFAULT_HISTORY_FOLLOW_UP_CANDIDATES);
3095
+ const moreRecentActionIds = new Set();
3096
+ const recentHistoryEvents = resolveRecentCapabilityEventsForAtom(point && point.atomId, 3);
3097
+ const targetEventId = trimString(event && event.eventId);
3098
+ for (let index = 0; index < recentHistoryEvents.length; index += 1) {
3099
+ const historyEvent = recentHistoryEvents[index];
3100
+ if (trimString(historyEvent && historyEvent.eventId) === targetEventId) {
3101
+ break;
3102
+ }
3103
+ const historyActionId = trimString(historyEvent && historyEvent.actionId);
3104
+ if (historyActionId) {
3105
+ moreRecentActionIds.add(historyActionId);
3106
+ }
3107
+ }
3108
+ const visitedActionIds = new Set();
3109
+ const orderedValidActionIds = [];
3110
+ for (let index = 0; index < actionIds.length; index += 1) {
3111
+ const candidateActionId = trimString(actionIds[index]);
3112
+ if (!candidateActionId || candidateActionId === eventActionId) {
3113
+ continue;
3114
+ }
3115
+ if (visitedActionIds.has(candidateActionId)) {
3116
+ continue;
3117
+ }
3118
+ visitedActionIds.add(candidateActionId);
3119
+ if (!canTriggerPointActionByActionId(point, candidateActionId)) {
3120
+ continue;
3121
+ }
3122
+ orderedValidActionIds.push(candidateActionId);
3123
+ }
3124
+ const primaryActionId = orderedValidActionIds.find((actionId) => !moreRecentActionIds.has(actionId))
3125
+ || orderedValidActionIds[0]
3126
+ || '';
3127
+ const primaryIndex = orderedValidActionIds.indexOf(primaryActionId);
3128
+ const skippedRecentActionId = primaryIndex > 0
3129
+ ? orderedValidActionIds
3130
+ .slice(0, primaryIndex)
3131
+ .find((actionId) => moreRecentActionIds.has(actionId))
3132
+ : '';
3133
+ const primaryRecentCoverage = Boolean(primaryActionId) && moreRecentActionIds.has(primaryActionId);
3134
+ const secondaryActionId = orderedValidActionIds.find(
3135
+ (actionId, index) => index !== primaryIndex && actionId !== primaryActionId && !moreRecentActionIds.has(actionId)
3136
+ ) || orderedValidActionIds.find(
3137
+ (actionId, index) => index !== primaryIndex && actionId !== primaryActionId
3138
+ ) || '';
3139
+ const primaryRank = primaryIndex >= 0 ? primaryIndex + 1 : 0;
3140
+ const secondaryIndex = orderedValidActionIds.indexOf(secondaryActionId);
3141
+ const secondaryRank = secondaryIndex >= 0 ? secondaryIndex + 1 : 0;
3142
+ const totalCandidates = orderedValidActionIds.length;
3143
+ const primaryIsFresh = Boolean(primaryActionId) && !primaryRecentCoverage;
3144
+ const secondaryRecentCoverage = Boolean(secondaryActionId) && moreRecentActionIds.has(secondaryActionId);
3145
+ const secondaryAvailabilityKind = resolvePointActionAvailabilityKind(point, secondaryActionId);
3146
+
3147
+ let rationaleText = '';
3148
+ if (primaryActionId) {
3149
+ if (managedMemoryMissingActionIds.includes(primaryActionId) && missingManagedKeyLabels.length > 0) {
3150
+ rationaleText = getI18nText(
3151
+ 'agentWorkspace.messages.historyFollowUpReasonMissingManagedKey',
3152
+ 'Managed state still misses {missingKey}, so {action} becomes next step.',
3153
+ {
3154
+ missingKey: summarizeMemoryKeyLabels(missingManagedKeyLabels),
3155
+ action: resolvePointActionLabelByActionId(point, primaryActionId),
3156
+ }
3157
+ );
3158
+ } else if (skippedRecentActionId) {
3159
+ rationaleText = getI18nText(
3160
+ 'agentWorkspace.messages.historyFollowUpReasonSkippedRecent',
3161
+ 'Recent activity already covered {action}.',
3162
+ { action: resolvePointActionLabelByActionId(point, skippedRecentActionId) }
3163
+ );
3164
+ } else if (moreRecentActionIds.has(primaryActionId)) {
3165
+ rationaleText = getI18nText(
3166
+ 'agentWorkspace.messages.historyFollowUpReasonBestAvailable',
3167
+ 'Reusing {action} because no fresher alternative is available.',
3168
+ { action: resolvePointActionLabelByActionId(point, primaryActionId) }
3169
+ );
3170
+ } else {
3171
+ rationaleText = getI18nText(
3172
+ 'agentWorkspace.messages.historyFollowUpReasonTopRanked',
3173
+ 'Top ranked next step after {action}.',
3174
+ { action: resolvePointActionLabelByActionId(point, eventActionId) }
3175
+ );
3176
+ }
3177
+ }
3178
+
3179
+ let tradeoffText = '';
3180
+ if (primaryActionId && secondaryActionId) {
3181
+ const primaryLabel = resolvePointActionLabelByActionId(point, primaryActionId);
3182
+ const secondaryLabel = resolvePointActionLabelByActionId(point, secondaryActionId);
3183
+ const eventLabel = resolvePointActionLabelByActionId(point, eventActionId);
3184
+ if (secondaryRecentCoverage) {
3185
+ tradeoffText = getI18nText(
3186
+ 'agentWorkspace.messages.historyFollowUpTradeoffCoveredAlternative',
3187
+ '{primary} stays primary because recent activity already covered {secondary}.',
3188
+ {
3189
+ primary: primaryLabel,
3190
+ secondary: secondaryLabel,
3191
+ }
3192
+ );
3193
+ } else if (primaryRank > 0 && secondaryRank > 0) {
3194
+ const availabilityText = secondaryAvailabilityKind === 'typed'
3195
+ ? getI18nText(
3196
+ 'agentWorkspace.messages.historyFollowUpTradeoffTypedAlternative',
3197
+ '{secondary} remains available as a typed alternative.',
3198
+ { secondary: secondaryLabel }
3199
+ )
3200
+ : secondaryAvailabilityKind === 'fallback'
3201
+ ? getI18nText(
3202
+ 'agentWorkspace.messages.historyFollowUpTradeoffFallbackAlternative',
3203
+ '{secondary} remains available as the fallback alternative.',
3204
+ { secondary: secondaryLabel }
3205
+ )
3206
+ : '';
3207
+ tradeoffText = getI18nText(
3208
+ 'agentWorkspace.messages.historyFollowUpTradeoffRankedAhead',
3209
+ '{primary} stays primary because it ranks ahead of {secondary} after {action}.',
3210
+ {
3211
+ primary: primaryLabel,
3212
+ secondary: secondaryLabel,
3213
+ action: eventLabel,
3214
+ }
3215
+ );
3216
+ if (availabilityText) {
3217
+ const joiner = /[\u4e00-\u9fff]/.test(`${tradeoffText}${availabilityText}`) ? '' : ' ';
3218
+ tradeoffText = `${tradeoffText}${joiner}${availabilityText}`;
3219
+ }
3220
+ }
3221
+ }
3222
+
3223
+ const signalBadges = [];
3224
+ let confidenceText = '';
3225
+ if (primaryActionId) {
3226
+ if (primaryRank > 0 && totalCandidates > 0) {
3227
+ signalBadges.push(getI18nText(
3228
+ 'agentWorkspace.labels.historyFollowUpSignalRank',
3229
+ 'Rank {rank}/{total}',
3230
+ { rank: primaryRank, total: totalCandidates }
3231
+ ));
3232
+ }
3233
+ signalBadges.push(getI18nText(
3234
+ primaryIsFresh
3235
+ ? 'agentWorkspace.labels.historyFollowUpSignalFresh'
3236
+ : 'agentWorkspace.labels.historyFollowUpSignalReused',
3237
+ primaryIsFresh ? 'Fresh' : 'Reused'
3238
+ ));
3239
+ if (skippedRecentActionId) {
3240
+ signalBadges.push(getI18nText(
3241
+ 'agentWorkspace.labels.historyFollowUpSignalSkippedRecent',
3242
+ 'Skipped recent'
3243
+ ));
3244
+ }
3245
+ missingManagedKeyLabels.forEach((label) => {
3246
+ signalBadges.push(getI18nText(
3247
+ label === 'note'
3248
+ ? 'agentWorkspace.labels.historyFollowUpSignalMissingNote'
3249
+ : 'agentWorkspace.labels.historyFollowUpSignalMissingCorrection',
3250
+ label === 'note' ? 'Missing note' : 'Missing correction'
3251
+ ));
3252
+ });
3253
+ if (secondaryActionId) {
3254
+ signalBadges.push(getI18nText(
3255
+ 'agentWorkspace.labels.historyFollowUpSignalAlternativeReady',
3256
+ 'Alt ready'
3257
+ ));
3258
+ }
3259
+
3260
+ if (skippedRecentActionId) {
3261
+ confidenceText = getI18nText(
3262
+ 'agentWorkspace.messages.historyFollowUpConfidenceSkippedRecent',
3263
+ 'Fresh candidate after skipping the more recent {action} repeat.',
3264
+ { action: resolvePointActionLabelByActionId(point, skippedRecentActionId) }
3265
+ );
3266
+ } else if (primaryIsFresh) {
3267
+ confidenceText = getI18nText(
3268
+ 'agentWorkspace.messages.historyFollowUpConfidenceFresh',
3269
+ 'Fresh deterministic candidate with no newer overlap.'
3270
+ );
3271
+ } else {
3272
+ confidenceText = getI18nText(
3273
+ 'agentWorkspace.messages.historyFollowUpConfidenceReused',
3274
+ 'No fresh candidate remained, so the rail reused {action}.',
3275
+ { action: resolvePointActionLabelByActionId(point, primaryActionId) }
3276
+ );
3277
+ }
3278
+ if (managedMemoryMissingActionIds.includes(primaryActionId) && missingManagedKeyLabels.length > 0) {
3279
+ const missingManagedKeyText = getI18nText(
3280
+ 'agentWorkspace.messages.historyFollowUpConfidenceMissingManagedKey',
3281
+ 'Targets missing {missingKey}.',
3282
+ { missingKey: summarizeMemoryKeyLabels(missingManagedKeyLabels) }
3283
+ );
3284
+ if (confidenceText) {
3285
+ const joiner = /[\u4e00-\u9fff]/.test(`${confidenceText}${missingManagedKeyText}`) ? '' : ' ';
3286
+ confidenceText = `${confidenceText}${joiner}${missingManagedKeyText}`;
3287
+ } else {
3288
+ confidenceText = missingManagedKeyText;
3289
+ }
3290
+ }
3291
+ }
3292
+
3293
+ let driftText = '';
3294
+ let stabilityText = '';
3295
+ let stabilityEventCount = 0;
3296
+ const targetEventIndex = recentHistoryEvents.findIndex(
3297
+ (historyEvent) => trimString(historyEvent && historyEvent.eventId) === targetEventId
3298
+ );
3299
+ if (primaryActionId && targetEventIndex > 0) {
3300
+ const primaryManagedKeyLabel = resolveManagedMemoryFollowUpKeyLabel(primaryActionId);
3301
+ const adjacentNewerEvent = recentHistoryEvents[targetEventIndex - 1];
3302
+ const adjacentDescriptor = adjacentNewerEvent
3303
+ ? resolveHistoryFollowUpDescriptor(point, adjacentNewerEvent)
3304
+ : null;
3305
+ const adjacentPrimaryActionId = trimString(adjacentDescriptor && adjacentDescriptor.primaryActionId);
3306
+ const adjacentEventActionId = trimString(adjacentNewerEvent && adjacentNewerEvent.actionId);
3307
+ const adjacentIsManagedMemoryState = isManagedMemoryStateActionId(adjacentEventActionId);
3308
+ const adjacentMissingManagedKeyLabels = resolveManagedConversationKeyLabels(
3309
+ adjacentNewerEvent && adjacentNewerEvent.missingManagedKeyLabels
3310
+ );
3311
+ if (adjacentPrimaryActionId && adjacentPrimaryActionId !== primaryActionId) {
3312
+ const currentPrimaryLabel = resolvePointActionLabelByActionId(point, primaryActionId);
3313
+ const adjacentPrimaryLabel = resolvePointActionLabelByActionId(point, adjacentPrimaryActionId);
3314
+ const adjacentEventLabel = resolvePointActionLabelByActionId(point, adjacentNewerEvent && adjacentNewerEvent.actionId);
3315
+ const adjacentMoreRecentCoverage = Array.isArray(adjacentDescriptor && adjacentDescriptor.moreRecentActionIds)
3316
+ ? adjacentDescriptor.moreRecentActionIds.includes(primaryActionId)
3317
+ : false;
3318
+ const managedKeyResolvedInAdjacent = Boolean(
3319
+ eventIsManagedMemoryState
3320
+ && adjacentIsManagedMemoryState
3321
+ && primaryManagedKeyLabel
3322
+ && missingManagedKeyLabels.includes(primaryManagedKeyLabel)
3323
+ && !adjacentMissingManagedKeyLabels.includes(primaryManagedKeyLabel)
3324
+ );
3325
+ driftText = managedKeyResolvedInAdjacent
3326
+ ? getI18nText(
3327
+ 'agentWorkspace.messages.historyFollowUpDriftManagedKeyResolved',
3328
+ 'Newer managed state no longer misses {resolvedKey}, so the next step shifts from {previousPrimary} to {currentPrimary}.',
3329
+ {
3330
+ resolvedKey: primaryManagedKeyLabel,
3331
+ previousPrimary: currentPrimaryLabel,
3332
+ currentPrimary: adjacentPrimaryLabel,
3333
+ }
3334
+ )
3335
+ : adjacentMoreRecentCoverage
3336
+ ? getI18nText(
3337
+ 'agentWorkspace.messages.historyFollowUpDriftCoveredPrimary',
3338
+ 'Newer {action} already covered {previousPrimary}, so the next step shifts to {currentPrimary}.',
3339
+ {
3340
+ action: adjacentEventLabel,
3341
+ previousPrimary: currentPrimaryLabel,
3342
+ currentPrimary: adjacentPrimaryLabel,
3343
+ }
3344
+ )
3345
+ : getI18nText(
3346
+ 'agentWorkspace.messages.historyFollowUpDriftDifferentOrder',
3347
+ 'Newer {action} shifts the next step from {previousPrimary} to {currentPrimary} because its follow-up order ranks {currentPrimary} earlier.',
3348
+ {
3349
+ action: adjacentEventLabel,
3350
+ previousPrimary: currentPrimaryLabel,
3351
+ currentPrimary: adjacentPrimaryLabel,
3352
+ }
3353
+ );
3354
+ }
3355
+ if (adjacentPrimaryActionId && adjacentPrimaryActionId === primaryActionId) {
3356
+ stabilityEventCount = 1;
3357
+ let managedKeyStableAcrossSeries = Boolean(
3358
+ eventIsManagedMemoryState
3359
+ && primaryManagedKeyLabel
3360
+ && missingManagedKeyLabels.includes(primaryManagedKeyLabel)
3361
+ );
3362
+ for (let index = targetEventIndex - 1; index >= 0; index -= 1) {
3363
+ const newerEvent = recentHistoryEvents[index];
3364
+ const newerDescriptor = index === targetEventIndex - 1
3365
+ ? adjacentDescriptor
3366
+ : resolveHistoryFollowUpDescriptor(point, newerEvent);
3367
+ if (trimString(newerDescriptor && newerDescriptor.primaryActionId) !== primaryActionId) {
3368
+ break;
3369
+ }
3370
+ stabilityEventCount += 1;
3371
+ if (managedKeyStableAcrossSeries) {
3372
+ const newerEventActionId = trimString(newerEvent && newerEvent.actionId);
3373
+ const newerMissingManagedKeyLabels = resolveManagedConversationKeyLabels(
3374
+ newerEvent && newerEvent.missingManagedKeyLabels
3375
+ );
3376
+ if (
3377
+ !isManagedMemoryStateActionId(newerEventActionId)
3378
+ || !newerMissingManagedKeyLabels.includes(primaryManagedKeyLabel)
3379
+ ) {
3380
+ managedKeyStableAcrossSeries = false;
3381
+ }
3382
+ }
3383
+ }
3384
+ if (stabilityEventCount > 1) {
3385
+ stabilityText = managedKeyStableAcrossSeries
3386
+ ? getI18nText(
3387
+ 'agentWorkspace.messages.historyFollowUpStabilityMissingManagedKey',
3388
+ 'Newer managed state still misses {missingKey}, so {currentPrimary} stays next across {count} consecutive history events.',
3389
+ {
3390
+ missingKey: primaryManagedKeyLabel,
3391
+ currentPrimary: resolvePointActionLabelByActionId(point, primaryActionId),
3392
+ count: stabilityEventCount,
3393
+ }
3394
+ )
3395
+ : getI18nText(
3396
+ 'agentWorkspace.messages.historyFollowUpStabilityKeptPrimary',
3397
+ 'Newer {action} keeps {currentPrimary} ahead, so the next step stays stable across {count} consecutive history events.',
3398
+ {
3399
+ action: resolvePointActionLabelByActionId(point, adjacentNewerEvent && adjacentNewerEvent.actionId),
3400
+ currentPrimary: resolvePointActionLabelByActionId(point, primaryActionId),
3401
+ count: stabilityEventCount,
3402
+ }
3403
+ );
3404
+ }
3405
+ }
3406
+ }
3407
+ if (primaryActionId && stabilityEventCount > 1) {
3408
+ signalBadges.push(getI18nText(
3409
+ 'agentWorkspace.labels.historyFollowUpSignalStableSeries',
3410
+ 'Stable x{count}',
3411
+ { count: stabilityEventCount }
3412
+ ));
3413
+ const stabilityConfidenceText = getI18nText(
3414
+ 'agentWorkspace.messages.historyFollowUpConfidenceStableSeries',
3415
+ 'Reinforced across {count} consecutive history events.',
3416
+ { count: stabilityEventCount }
3417
+ );
3418
+ if (stabilityConfidenceText) {
3419
+ if (confidenceText) {
3420
+ const joiner = /[\u4e00-\u9fff]/.test(`${confidenceText}${stabilityConfidenceText}`) ? '' : ' ';
3421
+ confidenceText = `${confidenceText}${joiner}${stabilityConfidenceText}`;
3422
+ } else {
3423
+ confidenceText = stabilityConfidenceText;
3424
+ }
3425
+ }
3426
+ }
3427
+
3428
+ return {
3429
+ primaryActionId,
3430
+ secondaryActionId,
3431
+ rationaleText,
3432
+ tradeoffText,
3433
+ stabilityText,
3434
+ signalBadges,
3435
+ confidenceText,
3436
+ driftText,
3437
+ moreRecentActionIds: Array.from(moreRecentActionIds),
3438
+ };
3439
+ }
3440
+
3441
+ function resolveHistoryFollowUpActionId(point, event) {
3442
+ return resolveHistoryFollowUpDescriptor(point, event).primaryActionId;
3443
+ }
3444
+
3445
+ function createActivePointSummaryCard(title) {
3446
+ const card = document.createElement('section');
3447
+ card.className = 'agent-workspace-active-point-summary-card';
3448
+ const heading = document.createElement('h5');
3449
+ heading.className = 'agent-workspace-active-point-summary-heading';
3450
+ heading.textContent = title;
3451
+ card.appendChild(heading);
3452
+ return card;
3453
+ }
3454
+
3455
+ function appendSummaryLine(card, text, extraClassName = '') {
3456
+ const line = document.createElement('p');
3457
+ line.className = extraClassName
3458
+ ? `agent-workspace-active-point-summary-line ${extraClassName}`
3459
+ : 'agent-workspace-active-point-summary-line';
3460
+ line.textContent = text;
3461
+ card.appendChild(line);
3462
+ }
3463
+
3464
+ function createHistoryEventItem(point, event, index) {
3465
+ const atomId = trimString(point && point.atomId);
3466
+ const eventId = trimString(event && event.eventId);
3467
+ const item = document.createElement('div');
3468
+ item.className = 'agent-workspace-history-item';
3469
+ if (!eventId) {
3470
+ return item;
3471
+ }
3472
+
3473
+ appendSummaryLine(
3474
+ item,
3475
+ `${index + 1}. ${resolvePointActionLabelByActionId(point, event.actionId)}`,
3476
+ 'agent-workspace-active-point-summary-line--history-title'
3477
+ );
3478
+
3479
+ const previewText = resolveCapabilityEventPreviewText(event);
3480
+ if (previewText) {
3481
+ appendSummaryLine(
3482
+ item,
3483
+ previewText,
3484
+ 'agent-workspace-active-point-summary-line--detail'
3485
+ );
3486
+ }
3487
+
3488
+ const toggleButton = document.createElement('button');
3489
+ toggleButton.type = 'button';
3490
+ toggleButton.className = 'agent-workspace-history-toggle';
3491
+ const expanded = resolveExpandedHistoryEventIdForAtom(atomId) === eventId;
3492
+ toggleButton.textContent = expanded
3493
+ ? getI18nText('agentWorkspace.actions.hideHistoryDetails', 'Hide details')
3494
+ : getI18nText('agentWorkspace.actions.showHistoryDetails', 'Show details');
3495
+ toggleButton.addEventListener('click', (clickEvent) => {
3496
+ clickEvent.preventDefault();
3497
+ clickEvent.stopPropagation();
3498
+ toggleHistoryEventDetails(atomId, eventId);
3499
+ });
3500
+ item.appendChild(toggleButton);
3501
+
3502
+ if (expanded) {
3503
+ const details = document.createElement('div');
3504
+ details.className = 'agent-workspace-history-details';
3505
+ appendHistoryDetailLine(
3506
+ details,
3507
+ getI18nText('agentWorkspace.labels.historyEventAt', 'At'),
3508
+ trimString(event && event.at)
3509
+ );
3510
+ appendHistoryDetailLine(
3511
+ details,
3512
+ getI18nText('agentWorkspace.labels.historyEventOperation', 'Operation'),
3513
+ trimString(event && event.operationId)
3514
+ );
3515
+ appendHistoryDetailLine(
3516
+ details,
3517
+ getI18nText('agentWorkspace.labels.historyEventSurface', 'Surface'),
3518
+ trimString(event && event.resultPresentation)
3519
+ );
3520
+ const durationMs = Number(event && event.durationMs);
3521
+ if (Number.isFinite(durationMs)) {
3522
+ appendHistoryDetailLine(
3523
+ details,
3524
+ getI18nText('agentWorkspace.labels.historyEventDuration', 'Duration'),
3525
+ `${Math.max(0, Math.floor(durationMs))} ms`
3526
+ );
3527
+ }
3528
+ const followUpDescriptor = resolveHistoryFollowUpDescriptor(point, event);
3529
+ if (followUpDescriptor.rationaleText) {
3530
+ const followUpReason = document.createElement('div');
3531
+ followUpReason.className = 'agent-workspace-history-follow-up-reason';
3532
+ const followUpReasonLabel = document.createElement('p');
3533
+ followUpReasonLabel.className = 'agent-workspace-history-detail-line agent-workspace-history-follow-up-reason-label';
3534
+ followUpReasonLabel.textContent = getI18nText('agentWorkspace.labels.historyFollowUpReason', 'Why this');
3535
+ const followUpReasonCopy = document.createElement('p');
3536
+ followUpReasonCopy.className = 'agent-workspace-history-detail-line agent-workspace-history-follow-up-reason-copy';
3537
+ followUpReasonCopy.textContent = followUpDescriptor.rationaleText;
3538
+ followUpReason.appendChild(followUpReasonLabel);
3539
+ followUpReason.appendChild(followUpReasonCopy);
3540
+ details.appendChild(followUpReason);
3541
+ }
3542
+ if (followUpDescriptor.tradeoffText) {
3543
+ const followUpTradeoff = document.createElement('div');
3544
+ followUpTradeoff.className = 'agent-workspace-history-follow-up-tradeoff';
3545
+ const followUpTradeoffLabel = document.createElement('p');
3546
+ followUpTradeoffLabel.className = 'agent-workspace-history-detail-line agent-workspace-history-follow-up-tradeoff-label';
3547
+ followUpTradeoffLabel.textContent = getI18nText(
3548
+ 'agentWorkspace.labels.historyFollowUpTradeoff',
3549
+ 'Why not alternative'
3550
+ );
3551
+ const followUpTradeoffCopy = document.createElement('p');
3552
+ followUpTradeoffCopy.className = 'agent-workspace-history-detail-line agent-workspace-history-follow-up-tradeoff-copy';
3553
+ followUpTradeoffCopy.textContent = followUpDescriptor.tradeoffText;
3554
+ followUpTradeoff.appendChild(followUpTradeoffLabel);
3555
+ followUpTradeoff.appendChild(followUpTradeoffCopy);
3556
+ details.appendChild(followUpTradeoff);
3557
+ }
3558
+ if (followUpDescriptor.driftText) {
3559
+ const followUpDrift = document.createElement('div');
3560
+ followUpDrift.className = 'agent-workspace-history-follow-up-drift';
3561
+ const followUpDriftLabel = document.createElement('p');
3562
+ followUpDriftLabel.className = 'agent-workspace-history-detail-line agent-workspace-history-follow-up-drift-label';
3563
+ followUpDriftLabel.textContent = getI18nText(
3564
+ 'agentWorkspace.labels.historyFollowUpDrift',
3565
+ 'Why it changed'
3566
+ );
3567
+ const followUpDriftCopy = document.createElement('p');
3568
+ followUpDriftCopy.className = 'agent-workspace-history-detail-line agent-workspace-history-follow-up-drift-copy';
3569
+ followUpDriftCopy.textContent = followUpDescriptor.driftText;
3570
+ followUpDrift.appendChild(followUpDriftLabel);
3571
+ followUpDrift.appendChild(followUpDriftCopy);
3572
+ details.appendChild(followUpDrift);
3573
+ }
3574
+ if (followUpDescriptor.stabilityText) {
3575
+ const followUpStability = document.createElement('div');
3576
+ followUpStability.className = 'agent-workspace-history-follow-up-stability';
3577
+ const followUpStabilityLabel = document.createElement('p');
3578
+ followUpStabilityLabel.className = 'agent-workspace-history-detail-line agent-workspace-history-follow-up-stability-label';
3579
+ followUpStabilityLabel.textContent = getI18nText(
3580
+ 'agentWorkspace.labels.historyFollowUpStability',
3581
+ 'Why it held'
3582
+ );
3583
+ const followUpStabilityCopy = document.createElement('p');
3584
+ followUpStabilityCopy.className = 'agent-workspace-history-detail-line agent-workspace-history-follow-up-stability-copy';
3585
+ followUpStabilityCopy.textContent = followUpDescriptor.stabilityText;
3586
+ followUpStability.appendChild(followUpStabilityLabel);
3587
+ followUpStability.appendChild(followUpStabilityCopy);
3588
+ details.appendChild(followUpStability);
3589
+ }
3590
+ if (followUpDescriptor.signalBadges.length > 0 || followUpDescriptor.confidenceText) {
3591
+ const followUpConfidence = document.createElement('div');
3592
+ followUpConfidence.className = 'agent-workspace-history-follow-up-confidence';
3593
+ const followUpConfidenceLabel = document.createElement('p');
3594
+ followUpConfidenceLabel.className = 'agent-workspace-history-detail-line agent-workspace-history-follow-up-confidence-label';
3595
+ followUpConfidenceLabel.textContent = getI18nText(
3596
+ 'agentWorkspace.labels.historyFollowUpConfidence',
3597
+ 'Confidence'
3598
+ );
3599
+ followUpConfidence.appendChild(followUpConfidenceLabel);
3600
+ if (followUpDescriptor.signalBadges.length > 0) {
3601
+ const signalBadges = document.createElement('div');
3602
+ signalBadges.className = 'agent-workspace-status-badges agent-workspace-history-follow-up-confidence-badges';
3603
+ followUpDescriptor.signalBadges.forEach((badgeLabel) => {
3604
+ signalBadges.appendChild(createStatusBadge(
3605
+ badgeLabel,
3606
+ 'agent-workspace-status-badge--history-confidence'
3607
+ ));
3608
+ });
3609
+ followUpConfidence.appendChild(signalBadges);
3610
+ }
3611
+ if (followUpDescriptor.confidenceText) {
3612
+ const followUpConfidenceCopy = document.createElement('p');
3613
+ followUpConfidenceCopy.className = 'agent-workspace-history-detail-line agent-workspace-history-follow-up-confidence-copy';
3614
+ followUpConfidenceCopy.textContent = followUpDescriptor.confidenceText;
3615
+ followUpConfidence.appendChild(followUpConfidenceCopy);
3616
+ }
3617
+ details.appendChild(followUpConfidence);
3618
+ }
3619
+ if (followUpDescriptor.primaryActionId) {
3620
+ const followUp = document.createElement('div');
3621
+ followUp.className = 'agent-workspace-history-follow-up';
3622
+ const followUpLabel = document.createElement('p');
3623
+ followUpLabel.className = 'agent-workspace-history-detail-line agent-workspace-history-follow-up-label';
3624
+ followUpLabel.textContent = getI18nText('agentWorkspace.labels.historyFollowUp', 'Next step');
3625
+ followUp.appendChild(followUpLabel);
3626
+ followUp.appendChild(createPointActionButton(
3627
+ resolvePointActionLabelByActionId(point, followUpDescriptor.primaryActionId),
3628
+ () => triggerPointAction(followUpDescriptor.primaryActionId, point)
3629
+ ));
3630
+ details.appendChild(followUp);
3631
+ }
3632
+ if (followUpDescriptor.secondaryActionId) {
3633
+ const alternatives = document.createElement('div');
3634
+ alternatives.className = 'agent-workspace-history-follow-up-alternatives';
3635
+ const alternativesLabel = document.createElement('p');
3636
+ alternativesLabel.className = 'agent-workspace-history-detail-line agent-workspace-history-follow-up-alternatives-label';
3637
+ alternativesLabel.textContent = getI18nText(
3638
+ 'agentWorkspace.labels.historyFollowUpAlternatives',
3639
+ 'Also available'
3640
+ );
3641
+ alternatives.appendChild(alternativesLabel);
3642
+ alternatives.appendChild(createPointActionButton(
3643
+ resolvePointActionLabelByActionId(point, followUpDescriptor.secondaryActionId),
3644
+ () => triggerPointAction(followUpDescriptor.secondaryActionId, point)
3645
+ ));
3646
+ details.appendChild(alternatives);
3647
+ }
3648
+ item.appendChild(details);
3649
+ }
3650
+
3651
+ return item;
3652
+ }
3653
+
3654
+ function renderActivePointSummaryCards(point) {
3655
+ const summary = resolveActivePointCapabilitySummary(point);
3656
+ const wrapper = document.createElement('div');
3657
+ wrapper.className = 'agent-workspace-active-point-summaries';
3658
+
3659
+ const studyCard = createActivePointSummaryCard(
3660
+ getI18nText('agentWorkspace.labels.studyLoopSummary', 'Study Loop')
3661
+ );
3662
+ appendSummaryLine(
3663
+ studyCard,
3664
+ getI18nText(
3665
+ 'agentWorkspace.messages.studyLoopReadyCount',
3666
+ '{count} ready',
3667
+ { count: summary.study.length }
3668
+ ),
3669
+ 'agent-workspace-active-point-summary-line--metric'
3670
+ );
3671
+ if (summary.study.length > 0) {
3672
+ appendSummaryLine(
3673
+ studyCard,
3674
+ summary.study
3675
+ .map((capability) => resolveCapabilityLabel(capability))
3676
+ .join(', ')
3677
+ );
3678
+ } else {
3679
+ appendSummaryLine(
3680
+ studyCard,
3681
+ getI18nText('agentWorkspace.labels.noStudyActionYet', 'No study action yet')
3682
+ );
3683
+ }
3684
+ const latestStudyEvent = resolveLatestCapabilityEventForAtom(point && point.atomId, 'study');
3685
+ if (latestStudyEvent) {
3686
+ appendSummaryLine(
3687
+ studyCard,
3688
+ `${getI18nText('agentWorkspace.labels.lastAction', 'Last action')}: ${resolvePointActionLabelByActionId(point, latestStudyEvent.actionId)}`
3689
+ );
3690
+ appendSummaryLine(
3691
+ studyCard,
3692
+ getI18nText('agentWorkspace.labels.success', 'Success')
3693
+ );
3694
+ } else {
3695
+ appendSummaryLine(
3696
+ studyCard,
3697
+ getI18nText('agentWorkspace.labels.noStudyActionYet', 'No study action yet')
3698
+ );
3699
+ }
3700
+ wrapper.appendChild(studyCard);
3701
+
3702
+ const supportCard = createActivePointSummaryCard(
3703
+ getI18nText('agentWorkspace.labels.supportSurfaceSummary', 'Support Surface')
3704
+ );
3705
+ appendSummaryLine(
3706
+ supportCard,
3707
+ getI18nText(
3708
+ 'agentWorkspace.messages.supportSurfaceCounts',
3709
+ 'Memory {memoryCount} Diagnostics {diagnosticCount}',
3710
+ {
3711
+ memoryCount: summary.memory.length,
3712
+ diagnosticCount: summary.diagnostic.length,
3713
+ }
3714
+ ),
3715
+ 'agent-workspace-active-point-summary-line--metric'
3716
+ );
3717
+ wrapper.appendChild(supportCard);
3718
+
3719
+ const historyEvents = resolveRecentCapabilityEventsForAtom(point && point.atomId, 3);
3720
+ const historyCard = createActivePointSummaryCard(
3721
+ getI18nText('agentWorkspace.labels.currentAtomHistorySummary', 'Recent Activity')
3722
+ );
3723
+ appendSummaryLine(
3724
+ historyCard,
3725
+ getI18nText(
3726
+ 'agentWorkspace.messages.currentAtomHistoryCount',
3727
+ '{count} recent results',
3728
+ { count: historyEvents.length }
3729
+ ),
3730
+ 'agent-workspace-active-point-summary-line--metric'
3731
+ );
3732
+ const managedConversationContinuitySummary = computeManagedConversationContinuitySummary(historyEvents);
3733
+ if (managedConversationContinuitySummary.readCount > 1) {
3734
+ appendSummaryLine(
3735
+ historyCard,
3736
+ getI18nText(
3737
+ 'agentWorkspace.messages.currentAtomHistoryManagedContinuityReads',
3738
+ 'Managed continuity {readCount} reads',
3739
+ { readCount: managedConversationContinuitySummary.readCount }
3740
+ ),
3741
+ 'agent-workspace-active-point-summary-line--metric'
3742
+ );
3743
+ if (hasManagedConversationKeyCounts(managedConversationContinuitySummary.resolvedKeyCounts)) {
3744
+ appendSummaryLine(
3745
+ historyCard,
3746
+ getI18nText(
3747
+ 'agentWorkspace.messages.currentAtomHistoryManagedContinuityResolved',
3748
+ 'resolved note {noteCount} correction {correctionCount}',
3749
+ {
3750
+ noteCount: managedConversationContinuitySummary.resolvedKeyCounts.note,
3751
+ correctionCount: managedConversationContinuitySummary.resolvedKeyCounts.correction,
3752
+ }
3753
+ )
3754
+ );
3755
+ if (Array.isArray(managedConversationContinuitySummary.resolvedFollowUpActionLabels)
3756
+ && managedConversationContinuitySummary.resolvedFollowUpActionLabels.length > 0) {
3757
+ appendSummaryLine(
3758
+ historyCard,
3759
+ getI18nText(
3760
+ 'agentWorkspace.messages.currentAtomHistoryManagedContinuityResolvedActions',
3761
+ 'resolved no longer needs {actions}',
3762
+ {
3763
+ actions: managedConversationContinuitySummary.resolvedFollowUpActionLabels.join(', '),
3764
+ }
3765
+ )
3766
+ );
3767
+ }
3768
+ }
3769
+ if (hasManagedConversationKeyCounts(managedConversationContinuitySummary.persistentKeyCounts)) {
3770
+ appendSummaryLine(
3771
+ historyCard,
3772
+ getI18nText(
3773
+ 'agentWorkspace.messages.currentAtomHistoryManagedContinuityPersistent',
3774
+ 'persistent note {noteCount} correction {correctionCount}',
3775
+ {
3776
+ noteCount: managedConversationContinuitySummary.persistentKeyCounts.note,
3777
+ correctionCount: managedConversationContinuitySummary.persistentKeyCounts.correction,
3778
+ }
3779
+ )
3780
+ );
3781
+ }
3782
+ if (Array.isArray(managedConversationContinuitySummary.persistentFollowUpActionIds)
3783
+ && managedConversationContinuitySummary.persistentFollowUpActionIds.length > 0) {
3784
+ appendSummaryLine(
3785
+ historyCard,
3786
+ getI18nText(
3787
+ 'agentWorkspace.messages.currentAtomHistoryManagedContinuityNextActions',
3788
+ 'persistent next {actions}',
3789
+ {
3790
+ actions: managedConversationContinuitySummary.persistentFollowUpActionLabels.join(', '),
3791
+ }
3792
+ )
3793
+ );
3794
+ }
3795
+ if (managedConversationContinuitySummary.lastTransition
3796
+ && trimString(managedConversationContinuitySummary.lastTransition.keyLabel)) {
3797
+ const lastTransition = managedConversationContinuitySummary.lastTransition;
3798
+ const resolvedKeyLabelsSummary = resolveManagedConversationKeyLabels(
3799
+ lastTransition.resolvedKeyLabels
3800
+ ).join(', ');
3801
+ const persistentKeyLabelsSummary = resolveManagedConversationKeyLabels(
3802
+ lastTransition.persistentKeyLabels
3803
+ ).join(', ');
3804
+ const resolvedActionLabelsSummary = Array.isArray(lastTransition.resolvedFollowUpActionLabels)
3805
+ ? lastTransition.resolvedFollowUpActionLabels.join(', ')
3806
+ : trimString(lastTransition.followUpActionLabel);
3807
+ const persistentActionLabelsSummary = Array.isArray(lastTransition.persistentFollowUpActionLabels)
3808
+ ? lastTransition.persistentFollowUpActionLabels.join(', ')
3809
+ : trimString(lastTransition.followUpActionLabel);
3810
+ const lastTransitionActionLabel = trimString(lastTransition.followUpActionLabel);
3811
+ if (lastTransition.kind === 'mixed'
3812
+ && resolvedKeyLabelsSummary
3813
+ && persistentKeyLabelsSummary
3814
+ && resolvedActionLabelsSummary
3815
+ && persistentActionLabelsSummary) {
3816
+ appendSummaryLine(
3817
+ historyCard,
3818
+ getI18nText(
3819
+ 'agentWorkspace.messages.currentAtomHistoryManagedContinuityLatestMixed',
3820
+ 'latest transition resolved {resolvedKeyLabels}, retired {resolvedActions}; persistent {persistentKeyLabels}, next {persistentActions}',
3821
+ {
3822
+ resolvedKeyLabels: resolvedKeyLabelsSummary,
3823
+ resolvedActions: resolvedActionLabelsSummary,
3824
+ persistentKeyLabels: persistentKeyLabelsSummary,
3825
+ persistentActions: persistentActionLabelsSummary,
3826
+ }
3827
+ )
3828
+ );
3829
+ } else if (lastTransition.kind === 'resolved' && lastTransitionActionLabel) {
3830
+ appendSummaryLine(
3831
+ historyCard,
3832
+ getI18nText(
3833
+ 'agentWorkspace.messages.currentAtomHistoryManagedContinuityLatestResolved',
3834
+ 'latest transition resolved {keyLabel}, retired {action}',
3835
+ {
3836
+ keyLabel: resolvedKeyLabelsSummary || trimString(lastTransition.keyLabel),
3837
+ action: lastTransitionActionLabel,
3838
+ }
3839
+ )
3840
+ );
3841
+ } else if (lastTransition.kind === 'persistent' && lastTransitionActionLabel) {
3842
+ appendSummaryLine(
3843
+ historyCard,
3844
+ getI18nText(
3845
+ 'agentWorkspace.messages.currentAtomHistoryManagedContinuityLatestPersistent',
3846
+ 'latest transition persistent {keyLabel}, next {action}',
3847
+ {
3848
+ keyLabel: persistentKeyLabelsSummary || trimString(lastTransition.keyLabel),
3849
+ action: lastTransitionActionLabel,
3850
+ }
3851
+ )
3852
+ );
3853
+ }
3854
+ }
3855
+ }
3856
+ if (historyEvents.length > 0) {
3857
+ historyEvents.forEach((event, index) => {
3858
+ historyCard.appendChild(createHistoryEventItem(point, event, index));
3859
+ });
3860
+ } else {
3861
+ appendSummaryLine(
3862
+ historyCard,
3863
+ getI18nText('agentWorkspace.labels.noCurrentAtomHistoryYet', 'No current-atom history yet')
3864
+ );
3865
+ }
3866
+ wrapper.appendChild(historyCard);
3867
+
3868
+ return wrapper;
3869
+ }
3870
+
3871
+ function renderActivePointRail(point) {
3872
+ const atomId = trimString(point && point.atomId);
3873
+ if (!atomId) {
3874
+ return null;
3875
+ }
3876
+ const rail = document.createElement('section');
3877
+ rail.className = 'agent-workspace-active-point';
3878
+ rail.dataset.atomId = atomId;
3879
+
3880
+ const heading = document.createElement('h4');
3881
+ heading.className = 'agent-workspace-active-point-heading';
3882
+ heading.textContent = getI18nText('agentWorkspace.labels.activeAtom', 'Active Atom');
3883
+ rail.appendChild(heading);
3884
+
3885
+ appendActivePointStatus(rail, point);
3886
+ appendPointIdentity(rail, point);
3887
+ rail.appendChild(renderActivePointSummaryCards(point));
3888
+
3889
+ const actions = document.createElement('div');
3890
+ actions.className = 'agent-workspace-point-actions agent-workspace-active-point-actions';
3891
+ actions.appendChild(createPointActionButton(
3892
+ getI18nText('agentWorkspace.actions.openFocusMode', 'Focus'),
3893
+ () => triggerPointAction('open_focus_mode', point)
3894
+ ));
3895
+ actions.appendChild(createPointActionButton(
3896
+ getI18nText('agentWorkspace.actions.openLearningPath', 'Learning Path'),
3897
+ () => triggerPointAction('open_learning_path', point)
3898
+ ));
3899
+ resolveActivePointCapabilities(point).forEach((capability) => {
3900
+ actions.appendChild(createPointActionButton(
3901
+ resolveCapabilityLabel(capability),
3902
+ () => {
3903
+ void runCapabilityAction(capability, point);
3904
+ }
3905
+ ));
3906
+ });
3907
+ rail.appendChild(actions);
3908
+ return rail;
3909
+ }
3910
+
3911
+ function focusKnowledgePoint(point) {
3912
+ const atomId = trimString(point && point.atomId);
3913
+ if (!atomId) {
3914
+ return;
3915
+ }
3916
+ focusAtom(atomId, point);
3917
+ }
3918
+
3919
+ function renderKnowledgePoints(knowledgePoints) {
3920
+ if (!dom.knowledgeList) {
3921
+ return;
3922
+ }
3923
+ dom.knowledgeList.innerHTML = '';
3924
+ if (!Array.isArray(knowledgePoints) || knowledgePoints.length === 0) {
3925
+ const emptyState = document.createElement('div');
3926
+ emptyState.className = 'agent-workspace-empty';
3927
+ emptyState.textContent = getI18nText(
3928
+ 'agentWorkspace.messages.noKnowledgePoints',
3929
+ 'No local knowledge points found for current query.'
3930
+ );
3931
+ dom.knowledgeList.appendChild(emptyState);
3932
+ return;
3933
+ }
3934
+
3935
+ const activePoint = findKnowledgePoint(state.latestFocusAtomId);
3936
+ const activeRail = renderActivePointRail(activePoint);
3937
+ if (activeRail) {
3938
+ dom.knowledgeList.appendChild(activeRail);
3939
+ }
3940
+
3941
+ knowledgePoints.forEach((point) => {
3942
+ const card = document.createElement('article');
3943
+ card.className = 'agent-workspace-point-card';
3944
+ card.dataset.atomId = trimString(point && point.atomId);
3945
+ if (trimString(point && point.atomId) === trimString(state.latestFocusAtomId)) {
3946
+ card.classList.add('agent-workspace-point-card--active');
3947
+ }
3948
+ card.setAttribute('role', 'button');
3949
+ card.setAttribute('tabindex', '0');
3950
+ card.title = getI18nText(
3951
+ 'agentWorkspace.messages.clickPointToFocus',
3952
+ 'Click to open focus mode'
3953
+ );
3954
+ card.addEventListener('click', () => {
3955
+ try {
3956
+ focusKnowledgePoint(point);
3957
+ } catch (error) {
3958
+ appendMessage(
3959
+ 'assistant',
3960
+ getI18nText('agentWorkspace.messages.operationFailed', 'Operation failed.')
3961
+ );
3962
+ console.error('[AgentWorkspace] point-focus failed:', error);
3963
+ }
3964
+ });
3965
+ card.addEventListener('keydown', (event) => {
3966
+ if (event.key === 'Enter' || event.key === ' ') {
3967
+ event.preventDefault();
3968
+ card.click();
3969
+ }
3970
+ });
3971
+
3972
+ appendPointIdentity(card, point);
3973
+
3974
+ const actions = document.createElement('div');
3975
+ actions.className = 'agent-workspace-point-actions';
3976
+ const capabilities = Array.isArray(point.capabilities) ? point.capabilities : [];
3977
+ capabilities.forEach((capability) => {
3978
+ const button = createPointActionButton(resolveCapabilityLabel(capability), () => {
3979
+ void runCapabilityAction(capability, point);
3980
+ });
3981
+ actions.appendChild(button);
3982
+ });
3983
+ card.appendChild(actions);
3984
+ dom.knowledgeList.appendChild(card);
3985
+ });
3986
+ }
3987
+
3988
+ async function sendConversation() {
3989
+ if (!dom.input) {
3990
+ return;
3991
+ }
3992
+ const message = trimString(dom.input.value);
3993
+ if (!message || state.busy) {
3994
+ return;
3995
+ }
3996
+ const payload = resolveConversationPayload({
3997
+ userId: resolveUserId(),
3998
+ message,
3999
+ topK: options.defaultTopK || 4,
4000
+ });
4001
+ const replayMarker = markUserMessageReplayCandidate(payload.message);
4002
+ diagnostics.conversationRequests += 1;
4003
+ diagnostics.lastConversation = {
4004
+ requestedAt: nowIso(),
4005
+ respondedAt: '',
4006
+ userId: payload.userId,
4007
+ message: payload.message,
4008
+ status: 'pending',
4009
+ replayCandidate: replayMarker.replayCandidate,
4010
+ knowledgePoints: 0,
4011
+ };
4012
+ appendMessage('user', payload.message, {
4013
+ source: 'conversation',
4014
+ replayCandidate: replayMarker.replayCandidate,
4015
+ replaySeenCount: replayMarker.seenCount,
4016
+ fingerprint: replayMarker.fingerprint,
4017
+ });
4018
+ dom.input.value = '';
4019
+
4020
+ setBusy(true);
4021
+ try {
4022
+ const result = await requestJson('/api/knowledge/conversation', payload);
4023
+ const knowledgePoints = Array.isArray(result && result.knowledgePoints)
4024
+ ? result.knowledgePoints
4025
+ : [];
4026
+ diagnostics.lastConversation = {
4027
+ requestedAt: diagnostics.lastConversation && diagnostics.lastConversation.requestedAt
4028
+ ? diagnostics.lastConversation.requestedAt
4029
+ : nowIso(),
4030
+ respondedAt: nowIso(),
4031
+ userId: payload.userId,
4032
+ message: payload.message,
4033
+ status: 'success',
4034
+ replayCandidate: replayMarker.replayCandidate,
4035
+ knowledgePoints: knowledgePoints.length,
4036
+ retrievalModes: Array.isArray(result && result.trace && result.trace.retrievalModes)
4037
+ ? result.trace.retrievalModes.slice()
4038
+ : [],
4039
+ vectorAcceleration: result && result.trace && result.trace.vectorAcceleration
4040
+ && typeof result.trace.vectorAcceleration === 'object'
4041
+ ? { ...result.trace.vectorAcceleration }
4042
+ : null,
4043
+ evidenceCoverageRatio: Number(result && result.trace && result.trace.evidenceCoverageRatio),
4044
+ };
4045
+ appendMessage('assistant', trimString(result && result.message));
4046
+ state.latestKnowledgePoints = knowledgePoints;
4047
+ renderKnowledgePoints(state.latestKnowledgePoints);
4048
+ } catch (error) {
4049
+ const errorMessage = trimString(error && error.message);
4050
+ diagnostics.lastConversation = {
4051
+ requestedAt: diagnostics.lastConversation && diagnostics.lastConversation.requestedAt
4052
+ ? diagnostics.lastConversation.requestedAt
4053
+ : nowIso(),
4054
+ respondedAt: nowIso(),
4055
+ userId: payload.userId,
4056
+ message: payload.message,
4057
+ status: 'failed',
4058
+ replayCandidate: replayMarker.replayCandidate,
4059
+ knowledgePoints: 0,
4060
+ };
4061
+ recordFailure('sendConversation', errorMessage);
4062
+ appendMessage(
4063
+ 'assistant',
4064
+ getI18nText(
4065
+ 'agentWorkspace.messages.conversationFailed',
4066
+ `Conversation failed: ${errorMessage || 'unknown error'}.`
4067
+ )
4068
+ );
4069
+ console.error('[AgentWorkspace] conversation request failed:', error);
4070
+ } finally {
4071
+ setBusy(false);
4072
+ }
4073
+ }
4074
+
4075
+ async function loadFoundationReadiness() {
4076
+ if (state.busy) {
4077
+ return;
4078
+ }
4079
+ const requestedAt = nowIso();
4080
+ diagnostics.lastFoundationReadiness = {
4081
+ requestedAt,
4082
+ respondedAt: '',
4083
+ status: 'pending',
4084
+ decision: '',
4085
+ storeType: '',
4086
+ graphBackendStatus: '',
4087
+ graphBackendSignalKind: '',
4088
+ graphBackendIndependent: false,
4089
+ queryBackendDefaultMode: '',
4090
+ queryBackendScoreSignals: [],
4091
+ vectorAdapterStatus: '',
4092
+ vectorAdapterSignalKind: '',
4093
+ vectorAdapterIndependent: false,
4094
+ vectorAdapterLinkedIntoQueryBackend: false,
4095
+ repoRootSource: '',
4096
+ runtimeProjectRootAligned: false,
4097
+ mandatoryCheckIds: [],
4098
+ mandatoryChecks: [],
4099
+ promotionCriteriaPassed: 0,
4100
+ promotionCriteriaTotal: 0,
4101
+ mandatoryChecksCount: 0,
4102
+ promotionCriteriaSatisfiedIds: [],
4103
+ promotionCriteriaUnsatisfiedIds: [],
4104
+ promotionCriteria: [],
4105
+ promotionBlockerIds: [],
4106
+ promotionBlockers: [],
4107
+ promotionBlockersCount: 0,
4108
+ recommendations: [],
4109
+ recommendationsCount: 0,
4110
+ };
4111
+ setBusy(true);
4112
+ try {
4113
+ const readiness = await requestJson(
4114
+ '/api/knowledge/foundation/readiness',
4115
+ null,
4116
+ {
4117
+ method: 'GET',
4118
+ responseKey: 'readiness',
4119
+ }
4120
+ );
4121
+ const baseline = readiness && readiness.baseline && typeof readiness.baseline === 'object'
4122
+ ? readiness.baseline
4123
+ : {};
4124
+ const recommendationsCount = Array.isArray(readiness && readiness.recommendations)
4125
+ ? readiness.recommendations.length
4126
+ : 0;
4127
+ const mandatoryCheckIds = Array.isArray(readiness && readiness.mandatoryChecks)
4128
+ ? readiness.mandatoryChecks
4129
+ .map((entry) => {
4130
+ const source = entry && typeof entry === 'object' ? entry : {};
4131
+ return trimString(source.gateId);
4132
+ })
4133
+ .filter(Boolean)
4134
+ : [];
4135
+ const mandatoryChecks = Array.isArray(readiness && readiness.mandatoryChecks)
4136
+ ? readiness.mandatoryChecks
4137
+ .map((entry) => {
4138
+ const source = entry && typeof entry === 'object' ? entry : {};
4139
+ return {
4140
+ gateId: trimString(source.gateId),
4141
+ command: trimString(source.command),
4142
+ };
4143
+ })
4144
+ .filter((entry) => entry.gateId)
4145
+ : [];
4146
+ const provenance = readiness && readiness.provenance && typeof readiness.provenance === 'object'
4147
+ ? readiness.provenance
4148
+ : {};
4149
+ const promotionBlockers = Array.isArray(readiness && readiness.promotionBlockers)
4150
+ ? readiness.promotionBlockers
4151
+ .map((entry) => {
4152
+ const source = entry && typeof entry === 'object' ? entry : {};
4153
+ return {
4154
+ blockerId: trimString(source.blockerId),
4155
+ summary: trimString(source.summary),
4156
+ };
4157
+ })
4158
+ .filter((entry) => entry.blockerId)
4159
+ : [];
4160
+ const promotionCriteria = Array.isArray(readiness && readiness.promotionCriteria)
4161
+ ? readiness.promotionCriteria
4162
+ .map((entry) => {
4163
+ const source = entry && typeof entry === 'object' ? entry : {};
4164
+ return {
4165
+ criterionId: trimString(source.criterionId),
4166
+ satisfied: Boolean(source.satisfied),
4167
+ summary: trimString(source.summary),
4168
+ };
4169
+ })
4170
+ .filter((entry) => entry.criterionId)
4171
+ : [];
4172
+ const promotionBlockerIds = promotionBlockers.map((entry) => entry.blockerId);
4173
+ const promotionCriteriaPassed = Number.isFinite(Number(readiness && readiness.promotionCriteriaPassed))
4174
+ ? Number(readiness && readiness.promotionCriteriaPassed)
4175
+ : promotionCriteria.filter((entry) => entry.satisfied).length;
4176
+ const promotionCriteriaTotal = Number.isFinite(Number(readiness && readiness.promotionCriteriaTotal))
4177
+ ? Number(readiness && readiness.promotionCriteriaTotal)
4178
+ : promotionCriteria.length;
4179
+ const promotionCriteriaSatisfiedIds = Array.isArray(readiness && readiness.promotionCriteriaSatisfiedIds)
4180
+ ? readiness.promotionCriteriaSatisfiedIds.map((entry) => trimString(entry)).filter(Boolean)
4181
+ : promotionCriteria.filter((entry) => entry.satisfied).map((entry) => entry.criterionId);
4182
+ const promotionCriteriaUnsatisfiedIds = Array.isArray(readiness && readiness.promotionCriteriaUnsatisfiedIds)
4183
+ ? readiness.promotionCriteriaUnsatisfiedIds.map((entry) => trimString(entry)).filter(Boolean)
4184
+ : promotionCriteria.filter((entry) => !entry.satisfied).map((entry) => entry.criterionId);
4185
+ diagnostics.lastFoundationReadiness = {
4186
+ requestedAt,
4187
+ respondedAt: nowIso(),
4188
+ evaluatedAt: trimString(readiness && readiness.evaluatedAt),
4189
+ status: trimString(readiness && readiness.status),
4190
+ decision: trimString(readiness && readiness.decision),
4191
+ storeType: trimString(baseline.storeType),
4192
+ graphBackendStatus: trimString(baseline.graphBackendStatus),
4193
+ graphBackendSignalKind: trimString(baseline.graphBackendSignalKind),
4194
+ graphBackendIndependent: Boolean(baseline.graphBackendIndependent),
4195
+ queryBackendDefaultMode: trimString(baseline.queryBackendDefaultMode),
4196
+ queryBackendScoreSignals: Array.isArray(baseline.queryBackendScoreSignals)
4197
+ ? baseline.queryBackendScoreSignals.map((entry) => trimString(entry)).filter(Boolean)
4198
+ : [],
4199
+ vectorAdapterStatus: trimString(baseline.vectorAdapterStatus),
4200
+ vectorAdapterSignalKind: trimString(baseline.vectorAdapterSignalKind),
4201
+ vectorAdapterIndependent: Boolean(baseline.vectorAdapterIndependent),
4202
+ vectorAdapterLinkedIntoQueryBackend: Boolean(baseline.vectorAdapterLinkedIntoQueryBackend),
4203
+ repoRootSource: trimString(provenance.repoRootSource),
4204
+ runtimeProjectRootAligned: Boolean(provenance.runtimeProjectRootAligned),
4205
+ mandatoryCheckIds,
4206
+ mandatoryChecks,
4207
+ promotionCriteriaPassed,
4208
+ promotionCriteriaTotal,
4209
+ mandatoryChecksCount: mandatoryCheckIds.length,
4210
+ promotionCriteriaSatisfiedIds,
4211
+ promotionCriteriaUnsatisfiedIds,
4212
+ promotionCriteria,
4213
+ promotionBlockerIds,
4214
+ promotionBlockers,
4215
+ promotionBlockersCount: promotionBlockerIds.length,
4216
+ recommendations: Array.isArray(readiness && readiness.recommendations)
4217
+ ? readiness.recommendations.map((entry) => trimString(entry)).filter(Boolean)
4218
+ : [],
4219
+ recommendationsCount,
4220
+ };
4221
+ const queryBackendScoreSignals = Array.isArray(baseline.queryBackendScoreSignals)
4222
+ ? baseline.queryBackendScoreSignals.map((entry) => trimString(entry)).filter(Boolean)
4223
+ : [];
4224
+ appendMessage(
4225
+ 'assistant',
4226
+ getI18nText(
4227
+ 'agentWorkspace.messages.foundationReadinessLoaded',
4228
+ `Foundation readiness ${trimString(readiness && readiness.status) || 'unknown'} / ${trimString(readiness && readiness.decision) || 'unknown'} (store ${trimString(baseline.storeType) || 'n/a'}, graph ${trimString(baseline.graphBackendStatus) || 'n/a'}/${trimString(baseline.graphBackendSignalKind) || 'n/a'}, graph-independent ${Boolean(baseline.graphBackendIndependent) ? 'yes' : 'no'}, backend ${trimString(baseline.queryBackendDefaultMode) || 'n/a'}, signals ${queryBackendScoreSignals.join(', ') || 'none'}, vector ${trimString(baseline.vectorAdapterStatus) || 'n/a'}, signal-kind ${trimString(baseline.vectorAdapterSignalKind) || 'n/a'}, independent ${Boolean(baseline.vectorAdapterIndependent) ? 'yes' : 'no'}, linked ${Boolean(baseline.vectorAdapterLinkedIntoQueryBackend) ? 'yes' : 'no'}, repo-source ${trimString(provenance.repoRootSource) || 'n/a'}, aligned ${Boolean(provenance.runtimeProjectRootAligned) ? 'yes' : 'no'}, gates ${mandatoryCheckIds.join(', ') || 'none'}, criteria ${promotionCriteriaPassed}/${promotionCriteriaTotal}, criteria-detail ${promotionCriteria.length}, satisfied ${promotionCriteriaSatisfiedIds.join(', ') || 'none'}, unmet ${promotionCriteriaUnsatisfiedIds.join(', ') || 'none'}, blockers ${promotionBlockerIds.join(', ') || 'none'}, recommendations ${recommendationsCount}).`,
4229
+ {
4230
+ status: trimString(readiness && readiness.status) || 'unknown',
4231
+ decision: trimString(readiness && readiness.decision) || 'unknown',
4232
+ storeType: trimString(baseline.storeType) || 'n/a',
4233
+ graphBackendStatus: trimString(baseline.graphBackendStatus) || 'n/a',
4234
+ graphBackendSignalKind: trimString(baseline.graphBackendSignalKind) || 'n/a',
4235
+ graphBackendIndependent: Boolean(baseline.graphBackendIndependent) ? 'yes' : 'no',
4236
+ queryBackendDefaultMode: trimString(baseline.queryBackendDefaultMode) || 'n/a',
4237
+ queryBackendScoreSignals: queryBackendScoreSignals.join(', ') || 'none',
4238
+ vectorAdapterStatus: trimString(baseline.vectorAdapterStatus) || 'n/a',
4239
+ vectorAdapterSignalKind: trimString(baseline.vectorAdapterSignalKind) || 'n/a',
4240
+ vectorAdapterIndependent: Boolean(baseline.vectorAdapterIndependent) ? 'yes' : 'no',
4241
+ vectorAdapterLinkedIntoQueryBackend: Boolean(baseline.vectorAdapterLinkedIntoQueryBackend)
4242
+ ? 'yes'
4243
+ : 'no',
4244
+ repoRootSource: trimString(provenance.repoRootSource) || 'n/a',
4245
+ runtimeProjectRootAligned: Boolean(provenance.runtimeProjectRootAligned) ? 'yes' : 'no',
4246
+ mandatoryCheckIds: mandatoryCheckIds.join(', ') || 'none',
4247
+ promotionCriteriaPassed,
4248
+ promotionCriteriaTotal,
4249
+ promotionCriteriaCount: promotionCriteria.length,
4250
+ promotionCriteriaSatisfiedIds: promotionCriteriaSatisfiedIds.join(', ') || 'none',
4251
+ promotionCriteriaUnsatisfiedIds: promotionCriteriaUnsatisfiedIds.join(', ') || 'none',
4252
+ promotionBlockerIds: promotionBlockerIds.join(', ') || 'none',
4253
+ recommendationsCount,
4254
+ }
4255
+ )
4256
+ );
4257
+ } catch (error) {
4258
+ const errorMessage = trimString(error && error.message);
4259
+ diagnostics.lastFoundationReadiness = {
4260
+ requestedAt,
4261
+ respondedAt: nowIso(),
4262
+ status: 'failed',
4263
+ decision: '',
4264
+ storeType: '',
4265
+ graphBackendStatus: '',
4266
+ graphBackendSignalKind: '',
4267
+ graphBackendIndependent: false,
4268
+ queryBackendDefaultMode: '',
4269
+ queryBackendScoreSignals: [],
4270
+ vectorAdapterStatus: '',
4271
+ vectorAdapterSignalKind: '',
4272
+ vectorAdapterIndependent: false,
4273
+ vectorAdapterLinkedIntoQueryBackend: false,
4274
+ repoRootSource: '',
4275
+ runtimeProjectRootAligned: false,
4276
+ mandatoryCheckIds: [],
4277
+ mandatoryChecks: [],
4278
+ promotionCriteriaPassed: 0,
4279
+ promotionCriteriaTotal: 0,
4280
+ mandatoryChecksCount: 0,
4281
+ promotionCriteriaSatisfiedIds: [],
4282
+ promotionCriteriaUnsatisfiedIds: [],
4283
+ promotionCriteria: [],
4284
+ promotionBlockerIds: [],
4285
+ promotionBlockers: [],
4286
+ promotionBlockersCount: 0,
4287
+ recommendations: [],
4288
+ recommendationsCount: 0,
4289
+ };
4290
+ recordFailure('loadFoundationReadiness', errorMessage);
4291
+ appendMessage(
4292
+ 'assistant',
4293
+ getI18nText(
4294
+ 'agentWorkspace.messages.foundationReadinessFailed',
4295
+ `Foundation readiness request failed: ${errorMessage || 'unknown error'}.`
4296
+ )
4297
+ );
4298
+ console.error('[AgentWorkspace] foundation readiness request failed:', error);
4299
+ } finally {
4300
+ setBusy(false);
4301
+ }
4302
+ }
4303
+
4304
+ function bindDom() {
4305
+ dom.panel = document.getElementById('agent-workspace-panel');
4306
+ dom.form = document.getElementById('agent-workspace-form');
4307
+ dom.userIdInput = document.getElementById('agent-workspace-user-id');
4308
+ dom.input = document.getElementById('agent-workspace-input');
4309
+ dom.sendButton = document.getElementById('agent-workspace-send');
4310
+ dom.messages = document.getElementById('agent-workspace-messages');
4311
+ dom.knowledgeList = document.getElementById('agent-workspace-knowledge-list');
4312
+ dom.openPathButton = document.getElementById('agent-workspace-open-learning-path');
4313
+ dom.closePathButton = document.getElementById('agent-workspace-close-learning-path');
4314
+ dom.pathFullscreenButton = document.getElementById('agent-workspace-path-fullscreen');
4315
+ dom.foundationReadinessButton = document.getElementById('agent-workspace-open-foundation-readiness');
4316
+ if (!dom.panel || !dom.form || !dom.input || !dom.sendButton || !dom.messages || !dom.knowledgeList) {
4317
+ return false;
4318
+ }
4319
+ return true;
4320
+ }
4321
+
4322
+ function applyI18nPlaceholders() {
4323
+ if (dom.input) {
4324
+ dom.input.placeholder = getI18nText(
4325
+ 'agentWorkspace.placeholders.input',
4326
+ 'Ask agent to find local knowledge and run actions...'
4327
+ );
4328
+ }
4329
+ }
4330
+
4331
+ function init() {
4332
+ if (state.initialized || !globalScope.document) {
4333
+ return;
4334
+ }
4335
+ if (!bindDom()) {
4336
+ return;
4337
+ }
4338
+ state.initialized = true;
4339
+ document.body.classList.add(BODY_CLASS_ENABLED);
4340
+ applyI18nPlaceholders();
4341
+
4342
+ if (dom.userIdInput) {
4343
+ dom.userIdInput.value = trimString(options.defaultUserId) || 'agent_user_default';
4344
+ }
4345
+ dom.form.addEventListener('submit', async (event) => {
4346
+ event.preventDefault();
4347
+ await sendConversation();
4348
+ });
4349
+ if (dom.openPathButton) {
4350
+ dom.openPathButton.addEventListener('click', () => {
4351
+ const activePoint = findKnowledgePoint(resolvePathAtomId(''));
4352
+ if (activePoint) {
4353
+ triggerPointAction('open_learning_path', activePoint);
4354
+ return;
4355
+ }
4356
+ openLearningPathDock('');
4357
+ });
4358
+ }
4359
+ if (dom.closePathButton) {
4360
+ dom.closePathButton.addEventListener('click', () => {
4361
+ hidePathDock();
4362
+ });
4363
+ }
4364
+ if (dom.pathFullscreenButton) {
4365
+ dom.pathFullscreenButton.addEventListener('click', () => {
4366
+ togglePathFullscreen();
4367
+ });
4368
+ }
4369
+ if (dom.foundationReadinessButton) {
4370
+ dom.foundationReadinessButton.addEventListener('click', () => {
4371
+ void loadFoundationReadiness();
4372
+ });
4373
+ }
4374
+ globalScope.addEventListener('nc:pathmode:exited', () => {
4375
+ hidePathDock();
4376
+ });
4377
+ if (globalScope.i18n && typeof globalScope.i18n.onLanguageChange === 'function') {
4378
+ globalScope.i18n.onLanguageChange(() => {
4379
+ applyI18nPlaceholders();
4380
+ refreshToolbarButtons();
4381
+ renderKnowledgePoints(state.latestKnowledgePoints);
4382
+ });
4383
+ }
4384
+ appendMessage(
4385
+ 'system',
4386
+ getI18nText(
4387
+ 'agentWorkspace.messages.ready',
4388
+ 'Agent workspace ready. Ask for a topic to retrieve local knowledge points.'
4389
+ )
4390
+ );
4391
+ refreshToolbarButtons();
4392
+ }
4393
+
4394
+ return {
4395
+ init,
4396
+ sendConversation,
4397
+ loadFoundationReadiness,
4398
+ openLearningPathDock,
4399
+ hidePathDock,
4400
+ togglePathFullscreen,
4401
+ getDiagnosticsSnapshot,
4402
+ getDiagnosticsTrendSnapshot,
4403
+ getDiagnosticsIndexSnapshot,
4404
+ exportDiagnosticsReport,
4405
+ persistDiagnosticsReport,
4406
+ _state: state,
4407
+ };
4408
+ }
4409
+
4410
+ function initAgentWorkspaceRuntime(options = {}) {
4411
+ const runtime = createAgentWorkspaceRuntime(options);
4412
+ runtime.init();
4413
+ if (globalScope && typeof globalScope === 'object') {
4414
+ globalScope.__noteConnectionAgentWorkspaceRuntimeInstance = runtime;
4415
+ }
4416
+ return runtime;
4417
+ }
4418
+
4419
+ if (globalScope && globalScope.document) {
4420
+ if (document.readyState === 'loading') {
4421
+ document.addEventListener('DOMContentLoaded', () => {
4422
+ initAgentWorkspaceRuntime();
4423
+ });
4424
+ } else {
4425
+ initAgentWorkspaceRuntime();
4426
+ }
4427
+ }
4428
+
4429
+ return {
4430
+ createAgentWorkspaceRuntime,
4431
+ initAgentWorkspaceRuntime,
4432
+ };
4433
+ }
4434
+ );