@theihtisham/agent-shadow-brain 7.2.0 → 7.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +478 -429
- package/README.md +198 -68
- package/dist/activity-cli.d.ts +3 -0
- package/dist/activity-cli.d.ts.map +1 -0
- package/dist/activity-cli.js +78 -0
- package/dist/activity-cli.js.map +1 -0
- package/dist/adapters/index.d.ts +2 -1
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +3 -2
- package/dist/adapters/index.js.map +1 -1
- package/dist/brain/activity-journal.d.ts +82 -0
- package/dist/brain/activity-journal.d.ts.map +1 -0
- package/dist/brain/activity-journal.js +315 -0
- package/dist/brain/activity-journal.js.map +1 -0
- package/dist/brain/activity-reporting.d.ts +14 -0
- package/dist/brain/activity-reporting.d.ts.map +1 -0
- package/dist/brain/activity-reporting.js +17 -0
- package/dist/brain/activity-reporting.js.map +1 -0
- package/dist/brain/auto-update.js +1 -1
- package/dist/brain/auto-update.js.map +1 -1
- package/dist/brain/brain-portability.js +1 -1
- package/dist/brain/brain-portability.js.map +1 -1
- package/dist/brain/client-config.d.ts +19 -0
- package/dist/brain/client-config.d.ts.map +1 -0
- package/dist/brain/client-config.js +40 -0
- package/dist/brain/client-config.js.map +1 -0
- package/dist/brain/client-integrations.d.ts +63 -0
- package/dist/brain/client-integrations.d.ts.map +1 -0
- package/dist/brain/client-integrations.js +79 -0
- package/dist/brain/client-integrations.js.map +1 -0
- package/dist/brain/context-assembly.d.ts +41 -5
- package/dist/brain/context-assembly.d.ts.map +1 -1
- package/dist/brain/context-assembly.js +366 -35
- package/dist/brain/context-assembly.js.map +1 -1
- package/dist/brain/context-efficiency.d.ts +48 -0
- package/dist/brain/context-efficiency.d.ts.map +1 -0
- package/dist/brain/context-efficiency.js +34 -0
- package/dist/brain/context-efficiency.js.map +1 -0
- package/dist/brain/context-request.d.ts +4 -0
- package/dist/brain/context-request.d.ts.map +1 -0
- package/dist/brain/context-request.js +24 -0
- package/dist/brain/context-request.js.map +1 -0
- package/dist/brain/context-retrieval.d.ts +88 -0
- package/dist/brain/context-retrieval.d.ts.map +1 -0
- package/dist/brain/context-retrieval.js +301 -0
- package/dist/brain/context-retrieval.js.map +1 -0
- package/dist/brain/feature-settings.d.ts +10 -0
- package/dist/brain/feature-settings.d.ts.map +1 -1
- package/dist/brain/feature-settings.js +18 -2
- package/dist/brain/feature-settings.js.map +1 -1
- package/dist/brain/file-identity.d.ts +4 -0
- package/dist/brain/file-identity.d.ts.map +1 -0
- package/dist/brain/file-identity.js +27 -0
- package/dist/brain/file-identity.js.map +1 -0
- package/dist/brain/global-brain.d.ts +18 -2
- package/dist/brain/global-brain.d.ts.map +1 -1
- package/dist/brain/global-brain.js +75 -7
- package/dist/brain/global-brain.js.map +1 -1
- package/dist/brain/managed-project-skills.d.ts +48 -0
- package/dist/brain/managed-project-skills.d.ts.map +1 -0
- package/dist/brain/managed-project-skills.js +230 -0
- package/dist/brain/managed-project-skills.js.map +1 -0
- package/dist/brain/mcp-presence.d.ts +50 -0
- package/dist/brain/mcp-presence.d.ts.map +1 -0
- package/dist/brain/mcp-presence.js +214 -0
- package/dist/brain/mcp-presence.js.map +1 -0
- package/dist/brain/mcp-server.d.ts +40 -1
- package/dist/brain/mcp-server.d.ts.map +1 -1
- package/dist/brain/mcp-server.js +506 -52
- package/dist/brain/mcp-server.js.map +1 -1
- package/dist/brain/mcp-stdio.d.ts +12 -0
- package/dist/brain/mcp-stdio.d.ts.map +1 -0
- package/dist/brain/mcp-stdio.js +158 -0
- package/dist/brain/mcp-stdio.js.map +1 -0
- package/dist/brain/memory-annotations.d.ts +99 -0
- package/dist/brain/memory-annotations.d.ts.map +1 -0
- package/dist/brain/memory-annotations.js +402 -0
- package/dist/brain/memory-annotations.js.map +1 -0
- package/dist/brain/memory-graph.d.ts.map +1 -1
- package/dist/brain/memory-graph.js +12 -23
- package/dist/brain/memory-graph.js.map +1 -1
- package/dist/brain/memory-inspection.d.ts +73 -0
- package/dist/brain/memory-inspection.d.ts.map +1 -0
- package/dist/brain/memory-inspection.js +131 -0
- package/dist/brain/memory-inspection.js.map +1 -0
- package/dist/brain/memory-mcp-content.d.ts +46 -0
- package/dist/brain/memory-mcp-content.d.ts.map +1 -0
- package/dist/brain/memory-mcp-content.js +95 -0
- package/dist/brain/memory-mcp-content.js.map +1 -0
- package/dist/brain/memory-sources.d.ts +8 -0
- package/dist/brain/memory-sources.d.ts.map +1 -0
- package/dist/brain/memory-sources.js +27 -0
- package/dist/brain/memory-sources.js.map +1 -0
- package/dist/brain/model-registry.d.ts +4 -5
- package/dist/brain/model-registry.d.ts.map +1 -1
- package/dist/brain/model-registry.js +22 -207
- package/dist/brain/model-registry.js.map +1 -1
- package/dist/brain/model-usage.d.ts +66 -0
- package/dist/brain/model-usage.d.ts.map +1 -0
- package/dist/brain/model-usage.js +246 -0
- package/dist/brain/model-usage.js.map +1 -0
- package/dist/brain/native-codex.d.ts +105 -0
- package/dist/brain/native-codex.d.ts.map +1 -0
- package/dist/brain/native-codex.js +292 -0
- package/dist/brain/native-codex.js.map +1 -0
- package/dist/brain/native-session-import.d.ts +56 -0
- package/dist/brain/native-session-import.d.ts.map +1 -0
- package/dist/brain/native-session-import.js +307 -0
- package/dist/brain/native-session-import.js.map +1 -0
- package/dist/brain/orchestrator.js +1 -1
- package/dist/brain/orchestrator.js.map +1 -1
- package/dist/brain/project-feedback.d.ts +63 -0
- package/dist/brain/project-feedback.d.ts.map +1 -0
- package/dist/brain/project-feedback.js +300 -0
- package/dist/brain/project-feedback.js.map +1 -0
- package/dist/brain/project-hub-contract.d.ts +332 -0
- package/dist/brain/project-hub-contract.d.ts.map +1 -0
- package/dist/brain/project-hub-contract.js +57 -0
- package/dist/brain/project-hub-contract.js.map +1 -0
- package/dist/brain/project-hub-mcp.d.ts +182 -0
- package/dist/brain/project-hub-mcp.d.ts.map +1 -0
- package/dist/brain/project-hub-mcp.js +46 -0
- package/dist/brain/project-hub-mcp.js.map +1 -0
- package/dist/brain/project-hub-pdf-worker.d.ts +13 -0
- package/dist/brain/project-hub-pdf-worker.d.ts.map +1 -0
- package/dist/brain/project-hub-pdf-worker.js +84 -0
- package/dist/brain/project-hub-pdf-worker.js.map +1 -0
- package/dist/brain/project-hub-store.d.ts +50 -0
- package/dist/brain/project-hub-store.d.ts.map +1 -0
- package/dist/brain/project-hub-store.js +257 -0
- package/dist/brain/project-hub-store.js.map +1 -0
- package/dist/brain/project-hub.d.ts +132 -0
- package/dist/brain/project-hub.d.ts.map +1 -0
- package/dist/brain/project-hub.js +403 -0
- package/dist/brain/project-hub.js.map +1 -0
- package/dist/brain/project-kit-inspection.d.ts +28 -0
- package/dist/brain/project-kit-inspection.d.ts.map +1 -0
- package/dist/brain/project-kit-inspection.js +200 -0
- package/dist/brain/project-kit-inspection.js.map +1 -0
- package/dist/brain/project-kit-paths.d.ts +13 -0
- package/dist/brain/project-kit-paths.d.ts.map +1 -0
- package/dist/brain/project-kit-paths.js +141 -0
- package/dist/brain/project-kit-paths.js.map +1 -0
- package/dist/brain/project-kit.d.ts +51 -0
- package/dist/brain/project-kit.d.ts.map +1 -0
- package/dist/brain/project-kit.js +319 -0
- package/dist/brain/project-kit.js.map +1 -0
- package/dist/brain/project-memory.d.ts +38 -0
- package/dist/brain/project-memory.d.ts.map +1 -0
- package/dist/brain/project-memory.js +76 -0
- package/dist/brain/project-memory.js.map +1 -0
- package/dist/brain/project-policy-delivery.d.ts +8 -0
- package/dist/brain/project-policy-delivery.d.ts.map +1 -0
- package/dist/brain/project-policy-delivery.js +38 -0
- package/dist/brain/project-policy-delivery.js.map +1 -0
- package/dist/brain/project-policy.d.ts +88 -0
- package/dist/brain/project-policy.d.ts.map +1 -0
- package/dist/brain/project-policy.js +267 -0
- package/dist/brain/project-policy.js.map +1 -0
- package/dist/brain/project-session-contract.d.ts +180 -0
- package/dist/brain/project-session-contract.d.ts.map +1 -0
- package/dist/brain/project-session-contract.js +50 -0
- package/dist/brain/project-session-contract.js.map +1 -0
- package/dist/brain/project-session-mcp.d.ts +145 -0
- package/dist/brain/project-session-mcp.d.ts.map +1 -0
- package/dist/brain/project-session-mcp.js +58 -0
- package/dist/brain/project-session-mcp.js.map +1 -0
- package/dist/brain/project-sessions.d.ts +97 -0
- package/dist/brain/project-sessions.d.ts.map +1 -0
- package/dist/brain/project-sessions.js +223 -0
- package/dist/brain/project-sessions.js.map +1 -0
- package/dist/brain/project-source-contract.d.ts +109 -0
- package/dist/brain/project-source-contract.d.ts.map +1 -0
- package/dist/brain/project-source-contract.js +2 -0
- package/dist/brain/project-source-contract.js.map +1 -0
- package/dist/brain/project-source-intake.d.ts +26 -0
- package/dist/brain/project-source-intake.d.ts.map +1 -0
- package/dist/brain/project-source-intake.js +457 -0
- package/dist/brain/project-source-intake.js.map +1 -0
- package/dist/brain/subconscious.d.ts +3 -1
- package/dist/brain/subconscious.d.ts.map +1 -1
- package/dist/brain/subconscious.js +8 -3
- package/dist/brain/subconscious.js.map +1 -1
- package/dist/brain/workflow-skills.d.ts +5 -5
- package/dist/brain/workflow-skills.d.ts.map +1 -1
- package/dist/brain/workflow-skills.js +3 -2
- package/dist/brain/workflow-skills.js.map +1 -1
- package/dist/cli.js +37 -3
- package/dist/cli.js.map +1 -1
- package/dist/context-cli.d.ts.map +1 -1
- package/dist/context-cli.js +49 -7
- package/dist/context-cli.js.map +1 -1
- package/dist/curation-cli.d.ts +4 -0
- package/dist/curation-cli.d.ts.map +1 -0
- package/dist/curation-cli.js +118 -0
- package/dist/curation-cli.js.map +1 -0
- package/dist/dashboard/brain-hub-actions.d.ts +2 -0
- package/dist/dashboard/brain-hub-actions.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-actions.js +234 -0
- package/dist/dashboard/brain-hub-actions.js.map +1 -0
- package/dist/dashboard/brain-hub-agent-dock.d.ts +2 -0
- package/dist/dashboard/brain-hub-agent-dock.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-agent-dock.js +255 -0
- package/dist/dashboard/brain-hub-agent-dock.js.map +1 -0
- package/dist/dashboard/brain-hub-brand-icons.d.ts +2 -0
- package/dist/dashboard/brain-hub-brand-icons.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-brand-icons.js +9 -0
- package/dist/dashboard/brain-hub-brand-icons.js.map +1 -0
- package/dist/dashboard/brain-hub-client.d.ts +2 -0
- package/dist/dashboard/brain-hub-client.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-client.js +435 -0
- package/dist/dashboard/brain-hub-client.js.map +1 -0
- package/dist/dashboard/brain-hub-context.d.ts +2 -0
- package/dist/dashboard/brain-hub-context.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-context.js +37 -0
- package/dist/dashboard/brain-hub-context.js.map +1 -0
- package/dist/dashboard/brain-hub-controls.d.ts +2 -0
- package/dist/dashboard/brain-hub-controls.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-controls.js +63 -0
- package/dist/dashboard/brain-hub-controls.js.map +1 -0
- package/dist/dashboard/brain-hub-exchanges.d.ts +2 -0
- package/dist/dashboard/brain-hub-exchanges.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-exchanges.js +82 -0
- package/dist/dashboard/brain-hub-exchanges.js.map +1 -0
- package/dist/dashboard/brain-hub-graph.d.ts +2 -0
- package/dist/dashboard/brain-hub-graph.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-graph.js +338 -0
- package/dist/dashboard/brain-hub-graph.js.map +1 -0
- package/dist/dashboard/brain-hub-html.d.ts +2 -0
- package/dist/dashboard/brain-hub-html.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-html.js +85 -0
- package/dist/dashboard/brain-hub-html.js.map +1 -0
- package/dist/dashboard/brain-hub-knowledge.d.ts +2 -0
- package/dist/dashboard/brain-hub-knowledge.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-knowledge.js +33 -0
- package/dist/dashboard/brain-hub-knowledge.js.map +1 -0
- package/dist/dashboard/brain-hub-live.d.ts +2 -0
- package/dist/dashboard/brain-hub-live.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-live.js +151 -0
- package/dist/dashboard/brain-hub-live.js.map +1 -0
- package/dist/dashboard/brain-hub-managed-skills.d.ts +2 -0
- package/dist/dashboard/brain-hub-managed-skills.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-managed-skills.js +20 -0
- package/dist/dashboard/brain-hub-managed-skills.js.map +1 -0
- package/dist/dashboard/brain-hub-native.d.ts +2 -0
- package/dist/dashboard/brain-hub-native.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-native.js +44 -0
- package/dist/dashboard/brain-hub-native.js.map +1 -0
- package/dist/dashboard/brain-hub-project-views.d.ts +2 -0
- package/dist/dashboard/brain-hub-project-views.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-project-views.js +80 -0
- package/dist/dashboard/brain-hub-project-views.js.map +1 -0
- package/dist/dashboard/brain-hub-sessions.d.ts +2 -0
- package/dist/dashboard/brain-hub-sessions.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-sessions.js +40 -0
- package/dist/dashboard/brain-hub-sessions.js.map +1 -0
- package/dist/dashboard/brain-hub-usage.d.ts +2 -0
- package/dist/dashboard/brain-hub-usage.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-usage.js +28 -0
- package/dist/dashboard/brain-hub-usage.js.map +1 -0
- package/dist/dashboard/brain-hub-views.d.ts +2 -0
- package/dist/dashboard/brain-hub-views.d.ts.map +1 -0
- package/dist/dashboard/brain-hub-views.js +155 -0
- package/dist/dashboard/brain-hub-views.js.map +1 -0
- package/dist/dashboard/demo-workspace.d.ts +11 -0
- package/dist/dashboard/demo-workspace.d.ts.map +1 -0
- package/dist/dashboard/demo-workspace.js +178 -0
- package/dist/dashboard/demo-workspace.js.map +1 -0
- package/dist/dashboard/live-workspace-html.d.ts +2 -0
- package/dist/dashboard/live-workspace-html.d.ts.map +1 -0
- package/dist/dashboard/live-workspace-html.js +1042 -0
- package/dist/dashboard/live-workspace-html.js.map +1 -0
- package/dist/dashboard/managed-project-skills-api.d.ts +29 -0
- package/dist/dashboard/managed-project-skills-api.d.ts.map +1 -0
- package/dist/dashboard/managed-project-skills-api.js +28 -0
- package/dist/dashboard/managed-project-skills-api.js.map +1 -0
- package/dist/dashboard/native-codex-api.d.ts +83 -0
- package/dist/dashboard/native-codex-api.d.ts.map +1 -0
- package/dist/dashboard/native-codex-api.js +91 -0
- package/dist/dashboard/native-codex-api.js.map +1 -0
- package/dist/dashboard/project-feedback-html.d.ts +5 -0
- package/dist/dashboard/project-feedback-html.d.ts.map +1 -0
- package/dist/dashboard/project-feedback-html.js +216 -0
- package/dist/dashboard/project-feedback-html.js.map +1 -0
- package/dist/dashboard/project-file-watch.d.ts +46 -0
- package/dist/dashboard/project-file-watch.d.ts.map +1 -0
- package/dist/dashboard/project-file-watch.js +221 -0
- package/dist/dashboard/project-file-watch.js.map +1 -0
- package/dist/dashboard/project-hub-api.d.ts +5 -0
- package/dist/dashboard/project-hub-api.d.ts.map +1 -0
- package/dist/dashboard/project-hub-api.js +121 -0
- package/dist/dashboard/project-hub-api.js.map +1 -0
- package/dist/dashboard/project-kit-html.d.ts +4 -0
- package/dist/dashboard/project-kit-html.d.ts.map +1 -0
- package/dist/dashboard/project-kit-html.js +359 -0
- package/dist/dashboard/project-kit-html.js.map +1 -0
- package/dist/dashboard/project-live-stream.d.ts +33 -0
- package/dist/dashboard/project-live-stream.d.ts.map +1 -0
- package/dist/dashboard/project-live-stream.js +233 -0
- package/dist/dashboard/project-live-stream.js.map +1 -0
- package/dist/dashboard/project-policy-api.d.ts +37 -0
- package/dist/dashboard/project-policy-api.d.ts.map +1 -0
- package/dist/dashboard/project-policy-api.js +31 -0
- package/dist/dashboard/project-policy-api.js.map +1 -0
- package/dist/dashboard/project-session-api.d.ts +28 -0
- package/dist/dashboard/project-session-api.d.ts.map +1 -0
- package/dist/dashboard/project-session-api.js +84 -0
- package/dist/dashboard/project-session-api.js.map +1 -0
- package/dist/dashboard/project-source-api.d.ts +11 -0
- package/dist/dashboard/project-source-api.d.ts.map +1 -0
- package/dist/dashboard/project-source-api.js +67 -0
- package/dist/dashboard/project-source-api.js.map +1 -0
- package/dist/dashboard/project-workspace.d.ts +16 -0
- package/dist/dashboard/project-workspace.d.ts.map +1 -0
- package/dist/dashboard/project-workspace.js +124 -0
- package/dist/dashboard/project-workspace.js.map +1 -0
- package/dist/dashboard/server.d.ts +1 -0
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +99 -42
- package/dist/dashboard/server.js.map +1 -1
- package/dist/dashboard/v6-dashboard-html.d.ts +1 -1
- package/dist/dashboard/v6-dashboard-html.d.ts.map +1 -1
- package/dist/dashboard/v6-dashboard-html.js +4261 -4309
- package/dist/dashboard/v6-dashboard-html.js.map +1 -1
- package/dist/dashboard/workspace-api.d.ts +12 -0
- package/dist/dashboard/workspace-api.d.ts.map +1 -1
- package/dist/dashboard/workspace-api.js +359 -41
- package/dist/dashboard/workspace-api.js.map +1 -1
- package/dist/dashboard/workspace-html.d.ts.map +1 -1
- package/dist/dashboard/workspace-html.js +1118 -135
- package/dist/dashboard/workspace-html.js.map +1 -1
- package/dist/dashboard/workspace-status.d.ts +53 -0
- package/dist/dashboard/workspace-status.d.ts.map +1 -0
- package/dist/dashboard/workspace-status.js +23 -0
- package/dist/dashboard/workspace-status.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/integration-cli.d.ts +3 -0
- package/dist/integration-cli.d.ts.map +1 -0
- package/dist/integration-cli.js +16 -0
- package/dist/integration-cli.js.map +1 -0
- package/dist/project-cli.d.ts +3 -0
- package/dist/project-cli.d.ts.map +1 -0
- package/dist/project-cli.js +100 -0
- package/dist/project-cli.js.map +1 -0
- package/dist/types.d.ts +8 -1
- package/dist/types.d.ts.map +1 -1
- package/docs/BRAND_ASSETS.md +36 -0
- package/docs/COMPARISON.md +22 -0
- package/docs/COMPONENTS.md +66 -0
- package/docs/CONTEXT_LAB.md +79 -0
- package/docs/FEATURES_AND_LIMITS.md +29 -0
- package/docs/INTEGRATIONS.md +70 -0
- package/docs/LEARNING.md +46 -0
- package/docs/PROJECT_KIT.md +98 -0
- package/docs/QUICKSTART.md +62 -0
- package/docs/README.md +36 -0
- package/docs/ROADMAP-v7.md +147 -0
- package/docs/SESSIONS.md +77 -0
- package/docs/SHARING.md +13 -0
- package/docs/SHOWCASE.md +87 -0
- package/docs/SOURCE_INTAKE.md +33 -0
- package/docs/STATS.md +21 -0
- package/docs/TROUBLESHOOTING.md +42 -0
- package/docs/UPGRADE_PLAN_v7.0.0.md +203 -0
- package/docs/VIRAL-PLAYBOOK.md +259 -0
- package/docs/VISUAL_GUIDE_v7.1.4.md +70 -0
- package/docs/VISUAL_GUIDE_v7.22.0.md +73 -0
- package/docs/WORKFLOW_INTEGRATION.md +21 -1
- package/docs/WORKSPACE.md +63 -70
- package/docs/assets/7.1.4-collaboration-design-preview.png +0 -0
- package/docs/assets/7.1.4-comparison.png +0 -0
- package/docs/assets/7.1.4-context-lab.png +0 -0
- package/docs/assets/7.1.4-control-room-dense-preview.png +0 -0
- package/docs/assets/7.1.4-control-room-focus-preview.png +0 -0
- package/docs/assets/7.1.4-features.png +0 -0
- package/docs/assets/7.1.4-graph.png +0 -0
- package/docs/assets/7.1.4-hub-control-room.png +0 -0
- package/docs/assets/7.1.4-hub-empty.png +0 -0
- package/docs/assets/7.1.4-hub-knowledge.png +0 -0
- package/docs/assets/7.1.4-hub-learning.png +0 -0
- package/docs/assets/7.1.4-hub-mobile.png +0 -0
- package/docs/assets/7.1.4-hub-public-docs-source.png +0 -0
- package/docs/assets/7.1.4-hub-public-docs.gif +0 -0
- package/docs/assets/7.1.4-hub-public-docs.png +0 -0
- package/docs/assets/7.1.4-hub-setup.png +0 -0
- package/docs/assets/7.1.4-hub-walkthrough.gif +0 -0
- package/docs/assets/7.1.4-integrations.png +0 -0
- package/docs/assets/7.1.4-knowledge-design-preview.png +0 -0
- package/docs/assets/7.1.4-learning-design-preview.png +0 -0
- package/docs/assets/7.1.4-learning.png +0 -0
- package/docs/assets/7.1.4-live.png +0 -0
- package/docs/assets/7.1.4-memory-review.png +0 -0
- package/docs/assets/7.1.4-mobile.png +0 -0
- package/docs/assets/7.1.4-models-design-preview.png +0 -0
- package/docs/assets/7.1.4-outcome-ranking.png +0 -0
- package/docs/assets/7.1.4-overview.png +0 -0
- package/docs/assets/7.1.4-project-kit.png +0 -0
- package/docs/assets/7.1.4-project-skill-preview.png +0 -0
- package/docs/assets/7.1.4-replay.png +0 -0
- package/docs/assets/7.1.4-setup-design-preview.png +0 -0
- package/docs/assets/7.1.4-usage-design-preview.png +0 -0
- package/docs/assets/7.22.0-agent-dock-mobile.png +0 -0
- package/docs/assets/7.22.0-agent-dock.gif +0 -0
- package/docs/assets/7.22.0-agent-dock.png +0 -0
- package/docs/assets/7.22.0-hub-exchange.png +0 -0
- package/docs/assets/7.22.0-hub-exchanges.gif +0 -0
- package/docs/assets/7.22.0-hub-graph.png +0 -0
- package/docs/assets/7.22.0-hub-mobile-empty.png +0 -0
- package/docs/assets/7.22.0-managed-skills.png +0 -0
- package/docs/assets/7.22.0-plain-evidence.png +0 -0
- package/docs/assets/7.22.0-project-controls.png +0 -0
- package/docs/assets/7.22.0-sessions.png +0 -0
- package/docs/assets/7.22.0-teach-brain.png +0 -0
- package/docs/assets/context-v7.1.3.png +0 -0
- package/docs/assets/features-v7.1.3.png +0 -0
- package/docs/assets/graph-v7.1.3.png +0 -0
- package/docs/assets/integrations-v7.1.3.png +0 -0
- package/docs/assets/live-v7.1.3.png +0 -0
- package/docs/assets/live-v7.1.4.gif +0 -0
- package/docs/assets/memory-detail-v7.1.3.png +0 -0
- package/docs/assets/overview-v7.1.3.png +0 -0
- package/docs/evaluation/CONTEXT_EVALUATION_v7.1.4.json +1818 -0
- package/docs/evaluation/CONTEXT_EVALUATION_v7.1.4.md +36 -0
- package/docs/launch/COMPONENT_MAP_v7.1.4.md +307 -0
- package/docs/launch/PROJECT_KIT_PLAN_v7.1.4.md +110 -0
- package/docs/launch/PROJECT_LEARNING_PLAN_v7.1.4.md +63 -0
- package/docs/launch/RELEASE_ACCEPTANCE_v7.22.0.md +48 -0
- package/docs/launch/RELEASE_NOTES_v7.1.3.md +40 -0
- package/docs/launch/RELEASE_NOTES_v7.1.4.md +30 -0
- package/docs/launch/RELEASE_NOTES_v7.22.0.md +53 -0
- package/docs/launch/RESEARCH_AND_PLAN_v7.1.4.md +255 -0
- package/docs/launch/RESEARCH_AND_PLAN_v7.22.0.md +672 -0
- package/package.json +135 -111
- package/skills/shadow-research/SKILL.md +2 -0
- package/skills/shadow-workflow/SKILL.md +8 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// Dependency-free local workspace. The advanced dashboard is served separately.
|
|
2
|
+
import { PROJECT_KIT_MARKUP, PROJECT_KIT_SCRIPT, PROJECT_KIT_STYLE } from './project-kit-html.js';
|
|
2
3
|
export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
3
4
|
<html lang="en" data-theme="light" data-motion="system">
|
|
4
5
|
<head>
|
|
5
6
|
<meta charset="utf-8">
|
|
6
7
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
7
8
|
<meta name="color-scheme" content="light dark">
|
|
8
|
-
<title>Shadow Brain · Workspace</title>
|
|
9
|
+
<title>Agent Shadow Brain · Workspace</title>
|
|
9
10
|
<script>
|
|
10
11
|
try {
|
|
11
12
|
const theme = localStorage.getItem('shadow-brain:workspace:theme');
|
|
@@ -20,7 +21,7 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
20
21
|
--canvas: #f4f6f9; --surface: #fff; --inset: #eef2f6; --hover: #f2f5f8;
|
|
21
22
|
--ink: #182539; --muted: #4e6176; --line: #dbe2e9; --control: #8a9bae;
|
|
22
23
|
--teal: #096459; --teal-fill: #076b5e; --teal-hover: #05584d; --tint: #dff1ec;
|
|
23
|
-
--bad: #a32940; --bad-bg: #fbeaf0; --focus: #167b9d;
|
|
24
|
+
--bad: #a32940; --bad-bg: #fbeaf0; --focus: #167b9d; --amber: #815116; --amber-bg: #f9edd8;
|
|
24
25
|
--rail: #172539; --rail-ink: #f0f5fc; --rail-muted: #b5c5d7; --rail-line: #35475b;
|
|
25
26
|
--rail-selected: #284657; --rail-highlight: #8ed9cb;
|
|
26
27
|
--agent: #57539a; --agent-bg: #eeecfa; --project: var(--teal); --project-bg: var(--tint);
|
|
@@ -36,7 +37,7 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
36
37
|
--canvas: #111b2a; --surface: #172437; --inset: #1e2e43; --hover: #23344a;
|
|
37
38
|
--ink: #edf4fb; --muted: #b3c1d1; --line: #334459; --control: #74889e;
|
|
38
39
|
--teal: #94e1d3; --teal-fill: #9be6d6; --teal-hover: #baf2e6; --tint: #203f43;
|
|
39
|
-
--bad: #ffadbd; --bad-bg: #492a3a; --focus: #8cd6f3;
|
|
40
|
+
--bad: #ffadbd; --bad-bg: #492a3a; --focus: #8cd6f3; --amber: #f4cb89; --amber-bg: #43352a;
|
|
40
41
|
--rail: #101a28; --rail-line: #2e4157; --rail-selected: #233e4f;
|
|
41
42
|
--agent: #c7bffc; --agent-bg: #333451; --memory: #bccfea; --memory-bg: #2a3a50;
|
|
42
43
|
--graph-line: #8295ac; --graph-project: #82bcb1; --graph-agent: #aaa0db; --graph-dot: #34465b; --accent-ink: #15372e;
|
|
@@ -70,7 +71,7 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
70
71
|
.brand { color: var(--rail-ink); display: flex; align-items: center; gap: 11px; padding: 0 7px; font-weight: 710; }
|
|
71
72
|
.brand-mark { width: 33px; height: 33px; display: grid; place-items: center; background: #294354; color: #9ce6d6; border-radius: 8px; }
|
|
72
73
|
.brand-mark .icon { width: 21px; height: 21px; }
|
|
73
|
-
.brand-name { letter-spacing: -.02em;
|
|
74
|
+
.brand-name { letter-spacing: -.02em; line-height: 1.13; }
|
|
74
75
|
.rail-nav { display: grid; gap: 4px; }
|
|
75
76
|
.nav-link { display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: 7px; color: var(--rail-muted); font-weight: 580; }
|
|
76
77
|
.nav-link:hover { color: var(--rail-ink); background: #24384d; }
|
|
@@ -88,6 +89,7 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
88
89
|
.crumb { display: flex; align-items: center; gap: 9px; min-width: 0; }
|
|
89
90
|
.crumb .icon { width: 14px; height: 14px; color: var(--muted); }
|
|
90
91
|
.top-actions { display: flex; align-items: center; gap: 14px; }
|
|
92
|
+
.live-link { white-space: nowrap; }
|
|
91
93
|
.connection { display: inline-flex; gap: 6px; align-items: center; white-space: nowrap; }
|
|
92
94
|
.dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; background: currentColor; }
|
|
93
95
|
.connection[data-state="connected"] .dot { color: var(--teal); }
|
|
@@ -130,7 +132,7 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
130
132
|
.inline-check { display: flex; align-items: flex-start; gap: 7px; min-height: 28px; }
|
|
131
133
|
.inline-check input { accent-color: var(--teal-fill); margin-top: 4px; }
|
|
132
134
|
.form-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
|
|
133
|
-
.filters { display: grid; grid-template-columns: minmax(
|
|
135
|
+
.filters { display: grid; grid-template-columns: minmax(155px,1.45fr) repeat(4,minmax(96px,1fr)) auto; gap: 10px; align-items: end; }
|
|
134
136
|
.filter-bar { display: flex; align-items: end; gap: 10px; flex-wrap: wrap; }
|
|
135
137
|
.filter-bar .label { flex: 1 1 155px; }
|
|
136
138
|
.status { padding: 13px 17px; border: 1px solid var(--line); border-radius: 7px; display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; background: var(--surface); }
|
|
@@ -151,6 +153,10 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
151
153
|
.tag.project { background: var(--project-bg); color: var(--project); }
|
|
152
154
|
.tag.agent { background: var(--agent-bg); color: var(--agent); }
|
|
153
155
|
.tag.memory { background: var(--memory-bg); color: var(--memory); }
|
|
156
|
+
.tag.state-active, .tag.check-ok { background: var(--tint); color: var(--teal); }
|
|
157
|
+
.tag.state-contested, .tag.check-warn { background: var(--amber-bg); color: var(--amber); }
|
|
158
|
+
.tag.state-superseded { background: var(--agent-bg); color: var(--agent); }
|
|
159
|
+
.tag.check-error { background: var(--bad-bg); color: var(--bad); }
|
|
154
160
|
.tags { display: flex; align-items: center; flex-wrap: wrap; gap: 5px 7px; }
|
|
155
161
|
.summary {
|
|
156
162
|
display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); background: var(--surface);
|
|
@@ -182,6 +188,31 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
182
188
|
.memory-row { padding: 14px 19px; display: grid; gap: 8px; }
|
|
183
189
|
.memory-head { display: flex; justify-content: space-between; align-items: start; gap: 10px; }
|
|
184
190
|
.memory-head time { white-space: nowrap; }
|
|
191
|
+
.record-link { color: var(--ink); font-weight: 680; text-decoration: underline; text-decoration-color: var(--control); text-underline-offset: 3px; }
|
|
192
|
+
.record-link:hover { color: var(--teal); text-decoration-color: currentColor; }
|
|
193
|
+
.memory-content { white-space: pre-wrap; overflow-wrap: anywhere; max-width: none; }
|
|
194
|
+
.detail-head { display: grid; gap: 9px; }
|
|
195
|
+
.detail-head > .button { justify-self: start; }
|
|
196
|
+
.detail-layout { grid-template-columns: minmax(0,1.45fr) minmax(265px,1fr); }
|
|
197
|
+
.detail-form, .detail-sources, .check-result { display: grid; gap: 9px; }
|
|
198
|
+
.detail-note { white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
199
|
+
.detail-row { display: grid; gap: 7px; padding: 13px 19px; }
|
|
200
|
+
.link-search { display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: end; gap: 8px; }
|
|
201
|
+
.save-status:empty { display: none; }
|
|
202
|
+
.inline-error { color: var(--bad); }
|
|
203
|
+
.quality-track { display: flex; width: 100%; height: 12px; background: var(--inset); border-radius: 5px; overflow: hidden; }
|
|
204
|
+
.quality-track > span { flex: none; }
|
|
205
|
+
.quality-active { background: var(--teal-fill); }
|
|
206
|
+
.quality-contested { background: var(--amber); }
|
|
207
|
+
.quality-archived { background: var(--control); }
|
|
208
|
+
.quality-superseded { background: var(--agent); }
|
|
209
|
+
.quality-labels { display: flex; flex-wrap: wrap; gap: 8px 13px; }
|
|
210
|
+
.quality-labels a { display: inline-flex; align-items: center; gap: 6px; color: var(--ink); font-size: .82rem; }
|
|
211
|
+
.quality-labels a:hover { text-decoration: underline; }
|
|
212
|
+
.quality-labels i { width: 8px; height: 8px; border-radius: 2px; }
|
|
213
|
+
.quality-source { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 8px; }
|
|
214
|
+
.check-row { display: grid; grid-template-columns: minmax(130px,.9fr) minmax(0,1.7fr) auto; gap: 10px; align-items: start; padding: 13px 19px; }
|
|
215
|
+
.check-row .tag { justify-self: end; }
|
|
185
216
|
.memory-row p { max-width: 90ch; white-space: pre-wrap; }
|
|
186
217
|
.memory-row details summary { color: var(--teal); font-size: .85rem; font-weight: 650; cursor: pointer; width: fit-content; max-width: 100%; }
|
|
187
218
|
.memory-row details ul { padding-left: 19px; margin-top: 4px; }
|
|
@@ -207,12 +238,20 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
207
238
|
.graph-node.dot-node:is(:hover,:focus-visible,[aria-pressed="true"]) .node-surface { fill: var(--memory-bg); }
|
|
208
239
|
.graph-node:focus-visible { outline: none; }
|
|
209
240
|
.graph-node.unrelated { opacity: .34; }
|
|
210
|
-
.graph-edge { fill: none; stroke: var(--graph-line); stroke-width: 1.15; opacity: .53; }
|
|
241
|
+
.graph-edge { fill: none; stroke: var(--graph-line); stroke-width: 1.15; opacity: .53; transition: opacity var(--ease); }
|
|
211
242
|
.graph-edge.project-edge { stroke: var(--graph-project); }
|
|
212
243
|
.graph-edge.agent-edge { stroke: var(--graph-agent); stroke-dasharray: 4 3; opacity: .35; }
|
|
213
244
|
.graph-edge.active { stroke: var(--teal); stroke-width: 2.1; opacity: 1; }
|
|
214
245
|
.graph-edge.agent-edge.active { stroke: var(--agent); }
|
|
246
|
+
.graph-edge.recorded-edge { stroke: var(--agent); stroke-width: 1.8; opacity: .8; }
|
|
247
|
+
.graph-edge.contradicts { stroke: var(--bad); stroke-dasharray: 5 3; }
|
|
248
|
+
.graph-edge.supersedes { stroke: var(--teal); stroke-dasharray: 2 2; }
|
|
249
|
+
.graph-edge.recorded-edge.active { stroke-width: 3; opacity: 1; }
|
|
215
250
|
.graph-edge.unrelated { opacity: .13; }
|
|
251
|
+
.graph-edge-hit { fill: none; stroke: transparent; stroke-width: 14; pointer-events: stroke; cursor: pointer; }
|
|
252
|
+
.graph-link-group:is(:hover,:focus-visible) .graph-edge.recorded-edge { opacity: 1; stroke-width: 3; }
|
|
253
|
+
.graph-link-group:focus-visible { outline: none; }
|
|
254
|
+
.graph-arrow { fill: var(--agent); }
|
|
216
255
|
.graph-tooltip { pointer-events: none; }
|
|
217
256
|
.graph-tooltip rect { fill: var(--surface); stroke: var(--memory); }
|
|
218
257
|
.graph-tooltip text { fill: var(--ink); font-size: 12px; font-weight: 650; }
|
|
@@ -234,6 +273,7 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
234
273
|
.legend-line { display: inline-block; width: 16px; border-top: 2px solid var(--graph-line); }
|
|
235
274
|
.legend-line.project-edge { border-color: var(--graph-project); }
|
|
236
275
|
.legend-line.agent-edge { border-color: var(--graph-agent); border-top-style: dashed; }
|
|
276
|
+
.legend-line.recorded-edge { border-color: var(--agent); }
|
|
237
277
|
.graph-list { min-width: 0; max-height: 480px; overflow-y: auto; }
|
|
238
278
|
.graph-list .graph-row { padding: 9px 12px; }
|
|
239
279
|
.graph-list .button { justify-content: flex-start; text-align: left; width: 100%; }
|
|
@@ -261,8 +301,105 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
261
301
|
.save-status { padding: 8px 19px; min-height: 35px; border-top: 1px solid var(--line); }
|
|
262
302
|
.save-status[data-error="true"] { color: var(--bad); }
|
|
263
303
|
.notice { margin: 0 0 16px; }
|
|
264
|
-
|
|
265
|
-
|
|
304
|
+
:root { --signal: #8fe5d3; --instrument: #142d3b; --instrument-deep: #102333; --instrument-ink: #f3f9fd; --instrument-muted: #b6cbd5; --instrument-line: #395663; }
|
|
305
|
+
.rail { gap: 35px; }
|
|
306
|
+
.nav-link[aria-current="page"] { box-shadow: inset 0 0 0 1px #426371; }
|
|
307
|
+
.rail-live { margin-top: 11px; }
|
|
308
|
+
.rail-live .icon { color: var(--rail-highlight); }
|
|
309
|
+
.page-head h1 { font-size: 2.27rem; }
|
|
310
|
+
.page-head .head-actions { align-items: end; }
|
|
311
|
+
.page-head .head-actions .button { align-self: end; }
|
|
312
|
+
.demo-notice { display: none; }
|
|
313
|
+
html[data-demo="true"] .demo-notice { display: flex; gap: 7px 10px; flex-wrap: wrap; padding: 8px 12px; margin-bottom: 16px; background: var(--amber-bg); color: var(--amber); border-radius: 7px; font-size: .83rem; }
|
|
314
|
+
html[data-demo="true"] .advanced-link { display: none; }
|
|
315
|
+
.overview-summary { display: grid; grid-template-columns: minmax(0,1.1fr) minmax(0,1fr); color: var(--instrument-ink); background: var(--instrument); border-radius: 12px; overflow: hidden; margin-bottom: 17px; }
|
|
316
|
+
.overview-summary .summary { grid-template-columns: repeat(3,minmax(0,1fr)); background: var(--instrument-deep); color: var(--instrument-ink); border: 0; border-radius: 0; margin: 0; }
|
|
317
|
+
.overview-summary .summary-item { flex-direction: column; align-items: start; justify-content: center; gap: 11px; padding: 20px 16px; }
|
|
318
|
+
.overview-summary .summary-item + .summary-item { border-color: var(--instrument-line); }
|
|
319
|
+
.overview-summary .summary dt { color: var(--instrument-muted); font-size: .83rem; }
|
|
320
|
+
.overview-summary .summary dd { font-size: 1.72rem; }
|
|
321
|
+
.overview-summary .tile-icon { color: var(--signal); background: #234550; }
|
|
322
|
+
.overview-lead { display: flex; align-items: center; gap: 19px; padding: 21px; min-width: 0; }
|
|
323
|
+
.overview-lead h2 { font-size: 1.28rem; }
|
|
324
|
+
.overview-lead p { color: var(--instrument-muted); margin-top: 5px; }
|
|
325
|
+
.overview-lead .button { color: var(--signal); border-color: var(--instrument-line); margin-top: 12px; }
|
|
326
|
+
.overview-lead .button:hover { background: #254350; }
|
|
327
|
+
.coverage-ring { flex: none; display: grid; place-items: center; width: 98px; aspect-ratio: 1; border-radius: 50%; background: conic-gradient(var(--signal) var(--coverage), #3b5665 0); }
|
|
328
|
+
.coverage-ring span { display: grid; place-items: center; width: 80px; aspect-ratio: 1; border-radius: 50%; background: var(--instrument); font-size: 1.46rem; font-weight: 720; font-variant-numeric: tabular-nums; }
|
|
329
|
+
.overview-columns { grid-template-columns: minmax(0,1.33fr) minmax(0,1fr); }
|
|
330
|
+
.overview-activity .panel-head { background: var(--inset); }
|
|
331
|
+
.attention-list { max-height: 355px; overflow: auto; }
|
|
332
|
+
.attention-row { display: grid; grid-template-columns: 30px minmax(0,1fr); gap: 11px; align-items: start; padding: 12px 19px; }
|
|
333
|
+
.attention-row .tile-icon { width: 30px; height: 30px; background: var(--amber-bg); color: var(--amber); }
|
|
334
|
+
.attention-row .tags { margin-top: 5px; }
|
|
335
|
+
.graph-stage {
|
|
336
|
+
--canvas: #142534; --surface: #203546; --ink: #f0f7fc; --muted: #bed0db; --line: #4b6679;
|
|
337
|
+
--project: #a3e8d5; --project-bg: #234651; --agent: #d3c5ff; --agent-bg: #3a3e5e; --memory: #c2daf2; --memory-bg: #344d64;
|
|
338
|
+
--graph-line: #8ba9bd; --graph-project: #82b8ab; --graph-agent: #aa9ed0; --graph-dot: #40566a; --teal: #9ce9d8; --bad: #ff9eab;
|
|
339
|
+
height: 520px; color: var(--ink); color-scheme: dark;
|
|
340
|
+
}
|
|
341
|
+
.graph-stage .button:hover { background: #30485a; }
|
|
342
|
+
.graph-stage .cluster-area { fill-opacity: .47; stroke-opacity: .34; }
|
|
343
|
+
.graph-node { transition: opacity var(--ease); }
|
|
344
|
+
.graph-node.selected-memory .node-surface { stroke: var(--teal); }
|
|
345
|
+
.graph-stage .graph-edge.supports { stroke: var(--teal); }
|
|
346
|
+
.graph-inspector, .graph-list { max-height: 520px; }
|
|
347
|
+
.graph-body[data-lens="true"] .graph-stage { height: 390px; }
|
|
348
|
+
.graph-body[data-lens="true"] :is(.graph-inspector,.graph-list) { max-height: 390px; }
|
|
349
|
+
.graph-lens { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 8px; padding: 8px 14px; border-bottom: 1px solid var(--line); }
|
|
350
|
+
.legend-line.supports { border-color: var(--teal); }
|
|
351
|
+
.legend-line.contradicts { border-color: var(--bad); border-top-style: dashed; }
|
|
352
|
+
.legend-line.supersedes { border-color: var(--teal); border-top-style: dotted; }
|
|
353
|
+
.context-layout { grid-template-columns: minmax(255px,.68fr) minmax(0,1.75fr); }
|
|
354
|
+
.context-config { gap: 16px; }
|
|
355
|
+
.context-config .form-actions .primary { width: 100%; }
|
|
356
|
+
.context-settings-pair { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 10px; }
|
|
357
|
+
.context-result-head { background: var(--inset); }
|
|
358
|
+
.context-output-head { display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: start; gap: 10px; }
|
|
359
|
+
.context-output-head .form-actions { justify-content: end; }
|
|
360
|
+
.context-metrics { display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); border-block: 1px solid var(--line); }
|
|
361
|
+
.context-metrics > div { padding: 11px 10px; min-width: 0; }
|
|
362
|
+
.context-metrics > div + div { border-left: 1px solid var(--line); }
|
|
363
|
+
.context-metrics dt { font-size: .78rem; color: var(--muted); }
|
|
364
|
+
.context-metrics dd { font-size: 1.21rem; font-weight: 710; overflow-wrap: anywhere; }
|
|
365
|
+
.context-budget-track { height: 6px; border-radius: 4px; background: var(--line); overflow: hidden; margin-top: 5px; }
|
|
366
|
+
.context-budget-track span { display: block; height: 100%; background: var(--teal-fill); border-radius: inherit; }
|
|
367
|
+
.context-section { display: grid; gap: 11px; min-width: 0; }
|
|
368
|
+
.context-section > summary, .candidate > summary { cursor: pointer; font-weight: 680; }
|
|
369
|
+
.context-section > summary { color: var(--teal); }
|
|
370
|
+
.context-section[open] > summary { margin-bottom: 10px; }
|
|
371
|
+
.retrieval-flow { display: grid; grid-template-columns: repeat(auto-fit,minmax(97px,1fr)); gap: 1px; list-style: none; padding: 1px; border-radius: 8px; background: var(--instrument-line); overflow: hidden; }
|
|
372
|
+
.retrieval-flow li { display: grid; grid-template-rows: auto 1fr auto; gap: 5px; min-width: 0; background: var(--instrument-deep); color: var(--instrument-ink); padding: 10px; }
|
|
373
|
+
.retrieval-flow strong { font-size: 1.28rem; font-variant-numeric: tabular-nums; }
|
|
374
|
+
.retrieval-flow span { font-size: .78rem; color: var(--instrument-muted); overflow-wrap: anywhere; }
|
|
375
|
+
.retrieval-flow li:last-child { background: var(--instrument); }
|
|
376
|
+
.retrieval-flow li:last-child strong { color: var(--signal); }
|
|
377
|
+
.retrieval-stage-bar { height: 3px; background: var(--instrument-line); border-radius: 2px; overflow: hidden; }
|
|
378
|
+
.retrieval-stage-bar i { display: block; height: 100%; background: var(--signal); opacity: .74; }
|
|
379
|
+
.candidate-list { max-height: 415px; overflow: auto; border-block: 1px solid var(--line); }
|
|
380
|
+
.candidate { padding: 9px 9px 9px 12px; }
|
|
381
|
+
.candidate > summary { color: var(--ink); }
|
|
382
|
+
.candidate > summary:hover { color: var(--teal); }
|
|
383
|
+
.candidate-title { display: inline-grid; grid-template-columns: auto minmax(0,1fr) auto; align-items: baseline; gap: 7px; width: calc(100% - 20px); vertical-align: top; }
|
|
384
|
+
.candidate-title .rank { color: var(--muted); font: .8rem Consolas,monospace; }
|
|
385
|
+
.candidate-body { display: grid; gap: 7px; padding: 9px 0 4px; }
|
|
386
|
+
.candidate-signals { display: grid; grid-template-columns: repeat(auto-fit,minmax(110px,1fr)); gap: 5px; }
|
|
387
|
+
.candidate-signals div { background: var(--inset); border-radius: 5px; padding: 6px; }
|
|
388
|
+
.candidate-signals dt { color: var(--muted); font-size: .75rem; }
|
|
389
|
+
.candidate-signals dd { font-size: .89rem; font-variant-numeric: tabular-nums; }
|
|
390
|
+
.context-compare { display: grid; gap: 8px; }
|
|
391
|
+
.compare-grid { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); border: 1px solid var(--line); border-radius: 7px; overflow: hidden; }
|
|
392
|
+
.compare-grid > div { padding: 10px; min-width: 0; }
|
|
393
|
+
.compare-grid > div + div { border-left: 1px solid var(--line); }
|
|
394
|
+
.compare-grid ul { padding-left: 16px; }
|
|
395
|
+
.compare-grid h3 { margin-bottom: 5px; }
|
|
396
|
+
.integration-meta { display: flex; flex-wrap: wrap; gap: 7px; padding: 12px 19px; border-bottom: 1px solid var(--line); }
|
|
397
|
+
.integration-config { display: grid; gap: 8px; }
|
|
398
|
+
.integration-config > summary { cursor: pointer; color: var(--teal); font-weight: 650; }
|
|
399
|
+
.integration-config[open] > summary { margin-bottom: 8px; }
|
|
400
|
+
.integration-config .context-preview { margin-block: 7px; max-height: 280px; }
|
|
401
|
+
.setting .settings-description { display: flex; align-items: start; gap: 11px; }
|
|
402
|
+
.setting .tile-icon { width: 32px; height: 32px; }
|
|
266
403
|
html[data-motion="reduce"] *, html[data-motion="reduce"] *::before, html[data-motion="reduce"] *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
|
|
267
404
|
@media (prefers-reduced-motion: reduce) {
|
|
268
405
|
*, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
|
|
@@ -270,9 +407,9 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
270
407
|
@media (max-width: 1140px) {
|
|
271
408
|
:root { --sidebar: 188px; }
|
|
272
409
|
.top, .content { padding-left: 24px; padding-right: 24px; }
|
|
273
|
-
.filters { grid-template-columns: repeat(
|
|
274
|
-
.filters .search-field { grid-column: span
|
|
275
|
-
.filters .button { grid-column:
|
|
410
|
+
.filters { grid-template-columns: repeat(4,minmax(0,1fr)); }
|
|
411
|
+
.filters .search-field { grid-column: span 3; }
|
|
412
|
+
.filters .button { grid-column: 4; grid-row: 1; }
|
|
276
413
|
.overview-columns { grid-template-columns: minmax(0,1.25fr) minmax(0,1fr); }
|
|
277
414
|
.event { grid-template-columns: 27px minmax(0,1fr); }
|
|
278
415
|
.event time { grid-column: 2; }
|
|
@@ -289,6 +426,7 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
289
426
|
:root { --header: 48px; font-size: 14px; }
|
|
290
427
|
.shell { display: block; }
|
|
291
428
|
.rail { z-index: var(--sticky); height: auto; flex-direction: row; align-items: center; gap: 7px; padding: 5px 10px; }
|
|
429
|
+
.rail-live { display: none; }
|
|
292
430
|
.rail-nav { display: flex; gap: 3px; }
|
|
293
431
|
.nav-link { padding: 7px 9px; min-height: 35px; }
|
|
294
432
|
.rail-foot { margin: 0 0 0 auto; padding: 0; border: 0; }
|
|
@@ -296,46 +434,71 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
296
434
|
.top { padding: 7px 13px; }
|
|
297
435
|
.top-actions { gap: 8px; }
|
|
298
436
|
.content { padding: 22px 12px 32px; }
|
|
299
|
-
h1 { font-size: 1.68rem; }
|
|
437
|
+
h1, .page-head h1 { font-size: 1.68rem; }
|
|
300
438
|
.page-head { gap: 8px; margin-bottom: 17px; flex-wrap: wrap; }
|
|
301
439
|
.summary { margin-bottom: 13px; }
|
|
302
440
|
.summary-item { gap: 6px; padding: 11px 9px; }
|
|
303
441
|
.summary-item .tile-icon { display: none; }
|
|
304
442
|
.split { grid-template-columns: minmax(0,1fr); gap: 13px; }
|
|
443
|
+
.filters { grid-template-columns: repeat(2,minmax(0,1fr)); }
|
|
444
|
+
.filters .search-field { grid-column: 1; }
|
|
445
|
+
.filters .button { grid-column: 2; grid-row: 1; }
|
|
446
|
+
.check-row { grid-template-columns: minmax(0,1fr) auto; padding-left: 12px; padding-right: 12px; }
|
|
447
|
+
.check-row .check-result { grid-column: 1 / -1; grid-row: 2; }
|
|
448
|
+
.check-row .tag { grid-column: 2; grid-row: 1; }
|
|
305
449
|
.graph-body { grid-template-columns: minmax(0,1fr); }
|
|
306
450
|
.graph-stage, .graph-list { height: 365px; }
|
|
451
|
+
.graph-body[data-lens="true"] .graph-stage { height: 310px; }
|
|
307
452
|
.graph-inspector { border-left: 0; border-top: 1px solid var(--line); max-height: 340px; }
|
|
308
453
|
.panel-head, .panel-body, .memory-row, .setting { padding-left: 12px; padding-right: 12px; }
|
|
309
454
|
.project-row, .event { padding-left: 12px; padding-right: 12px; }
|
|
310
455
|
.list-footer { flex-wrap: wrap; }
|
|
311
456
|
.graph-mode { margin-left: 0; }
|
|
457
|
+
.overview-summary { grid-template-columns: minmax(0,1fr); }
|
|
458
|
+
.overview-summary .summary-item { padding: 11px 10px; }
|
|
459
|
+
.overview-summary .summary dd { font-size: 1.42rem; }
|
|
460
|
+
.overview-lead { padding: 15px; gap: 12px; }
|
|
461
|
+
.coverage-ring { width: 77px; }
|
|
462
|
+
.coverage-ring span { width: 63px; font-size: 1.16rem; }
|
|
463
|
+
.context-output-head { grid-template-columns: minmax(0,1fr); }
|
|
464
|
+
.context-output-head .form-actions { justify-content: start; }
|
|
465
|
+
#context-result, #attention-heading { scroll-margin-top: 55px; }
|
|
466
|
+
.context-metrics > div { padding: 8px 6px; }
|
|
467
|
+
.context-metrics dd { font-size: 1rem; }
|
|
468
|
+
.retrieval-flow { grid-template-columns: repeat(3,minmax(0,1fr)); }
|
|
469
|
+
.candidate-title { grid-template-columns: auto minmax(0,1fr); }
|
|
470
|
+
.candidate-title .tag { grid-column: 2; justify-self: start; }
|
|
312
471
|
}
|
|
313
472
|
@media (max-width: 355px) {
|
|
314
473
|
.rail { gap: 3px; }
|
|
315
|
-
.nav-link { padding-left:
|
|
474
|
+
.nav-link { padding-left: 5px; padding-right: 5px; }
|
|
316
475
|
.crumb > .muted, .crumb .icon { display: none; }
|
|
317
476
|
.filters { grid-template-columns: repeat(2,minmax(0,1fr)); }
|
|
318
477
|
.filters .search-field { grid-column: 1 / -1; }
|
|
319
478
|
.filters .button { grid-row: auto; grid-column: auto; }
|
|
320
479
|
}
|
|
480
|
+
${PROJECT_KIT_STYLE}
|
|
321
481
|
</style>
|
|
322
482
|
</head>
|
|
323
483
|
<body>
|
|
324
484
|
<a class="skip button" href="#main-content">Skip to content</a>
|
|
325
485
|
<div class="shell">
|
|
326
486
|
<aside class="rail">
|
|
327
|
-
<a class="brand" href="#overview" aria-label="Shadow Brain overview">
|
|
328
|
-
<span class="brand-mark" data-icon="brand"></span><span class="brand-name">Shadow Brain</span>
|
|
487
|
+
<a class="brand" href="#overview" aria-label="Agent Shadow Brain overview">
|
|
488
|
+
<span class="brand-mark" data-icon="brand"></span><span class="brand-name">Agent Shadow Brain</span>
|
|
329
489
|
</a>
|
|
330
490
|
<nav class="rail-nav" aria-label="Workspace">
|
|
331
491
|
<a class="nav-link" href="#overview" aria-label="Overview" title="Overview" data-view="overview" aria-current="page"><span data-icon="overview"></span><span class="nav-label">Overview</span></a>
|
|
332
492
|
<a class="nav-link" href="#memories" aria-label="Memories" title="Memories" data-view="memories"><span data-icon="memory"></span><span class="nav-label">Memories</span></a>
|
|
333
493
|
<a class="nav-link" href="#graph" aria-label="Graph" title="Graph" data-view="graph"><span data-icon="graph"></span><span class="nav-label">Graph</span></a>
|
|
334
|
-
<a class="nav-link" href="#context" aria-label="Context" title="Context" data-view="context"><span data-icon="context"></span><span class="nav-label">Context</span></a>
|
|
494
|
+
<a class="nav-link" href="#context" aria-label="Context Lab" title="Context Lab" data-view="context"><span data-icon="context"></span><span class="nav-label">Context Lab</span></a>
|
|
495
|
+
<a class="nav-link" href="#project-kit" aria-label="Project Kit" title="Project Kit" data-view="project-kit"><span data-icon="project"></span><span class="nav-label">Project Kit</span></a>
|
|
496
|
+
<a class="nav-link" href="#integrations" aria-label="Integrations and health" title="Integrations and health" data-view="integrations"><span data-icon="integrations"></span><span class="nav-label">Integrations</span></a>
|
|
335
497
|
<a class="nav-link" href="#features" aria-label="Features" title="Features" data-view="features"><span data-icon="settings"></span><span class="nav-label">Features</span></a>
|
|
498
|
+
<a class="nav-link rail-live" href="/live" aria-label="Live operations" title="Live operations"><span data-icon="activity"></span><span class="nav-label">Live operations</span></a>
|
|
336
499
|
</nav>
|
|
337
500
|
<div class="rail-foot">
|
|
338
|
-
<a class="nav-link" href="/advanced" aria-label="Advanced dashboard" title="Advanced dashboard"><span data-icon="advanced"></span><span class="nav-label">Advanced tools</span><span class="arrow" data-icon="diagonal"></span></a>
|
|
501
|
+
<a class="nav-link advanced-link" href="/advanced" aria-label="Advanced dashboard" title="Advanced dashboard"><span data-icon="advanced"></span><span class="nav-label">Advanced tools</span><span class="arrow" data-icon="diagonal"></span></a>
|
|
339
502
|
<p class="rail-build" id="rail-version">Local workspace</p>
|
|
340
503
|
</div>
|
|
341
504
|
</aside>
|
|
@@ -343,16 +506,20 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
343
506
|
<header class="top">
|
|
344
507
|
<div class="crumb"><span class="muted">Workspace</span><span data-icon="chevron"></span><strong id="crumb">Overview</strong></div>
|
|
345
508
|
<div class="top-actions">
|
|
509
|
+
<a class="button quiet live-link" href="/live"><span data-icon="activity"></span>Live</a>
|
|
346
510
|
<span class="connection muted small" id="connection" data-state="loading"><span class="dot"></span><span id="connection-label">Connecting</span></span>
|
|
347
511
|
<button type="button" class="button icon-button quiet" id="theme-toggle" aria-label="Switch to dark theme" title="Switch to dark theme"><span data-icon="moon"></span></button>
|
|
348
512
|
</div>
|
|
349
513
|
</header>
|
|
350
514
|
<main id="main-content" class="content" tabindex="-1">
|
|
515
|
+
<div class="demo-notice"><strong>Synthetic demo</strong><span>Scripted local MCP calls use sample data. Changes affect this temporary demo only.</span></div>
|
|
516
|
+
<div class="project-local-notice"><strong>Project workspace</strong><span>Memory and settings are stored with this project.</span><a href="#project-kit">Open Project Kit</a></div>
|
|
351
517
|
<div class="notice" id="workspace-notice" role="status" hidden></div>
|
|
352
518
|
<section id="page-overview" aria-labelledby="title-overview">
|
|
353
519
|
<div class="page-head">
|
|
354
|
-
<div><h1 id="title-overview" tabindex="-1">
|
|
355
|
-
<
|
|
520
|
+
<div><h1 id="title-overview" tabindex="-1">Memory workspace</h1><p class="muted">Review saved knowledge and prepare a brief for your next task.</p></div>
|
|
521
|
+
<div class="form-actions head-actions"><label class="label" for="overview-quality-project">Project<select id="overview-quality-project"><option value="">All projects</option></select></label>
|
|
522
|
+
<a href="#context" id="overview-context" class="button primary"><span data-icon="context"></span>Open Context Lab</a></div>
|
|
356
523
|
</div>
|
|
357
524
|
<div id="overview-body" aria-busy="true"></div>
|
|
358
525
|
</section>
|
|
@@ -364,18 +531,31 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
364
531
|
<label class="label" for="memory-project">Project<select id="memory-project"><option value="">All projects</option></select></label>
|
|
365
532
|
<label class="label" for="memory-category">Category<select id="memory-category"><option value="">All categories</option></select></label>
|
|
366
533
|
<label class="label" for="memory-agent">Agent<select id="memory-agent"><option value="">All agents</option></select></label>
|
|
534
|
+
<label class="label" for="memory-state">Status<select id="memory-state"><option value="">All statuses</option><option value="active">Active</option><option value="contested">Needs review</option><option value="archived">Archived</option><option value="superseded">Superseded</option></select></label>
|
|
367
535
|
<button class="button primary" id="memory-search" type="submit"><span data-icon="search"></span>Search</button>
|
|
368
536
|
</form>
|
|
369
537
|
<div id="memory-results" class="panel" aria-busy="true"></div>
|
|
370
538
|
</div>
|
|
371
539
|
</section>
|
|
540
|
+
<section id="page-memory" aria-labelledby="title-memory" hidden>
|
|
541
|
+
<div class="page-head">
|
|
542
|
+
<div class="detail-head"><a id="detail-back" href="#memories" class="button quiet"><span data-icon="left"></span>Memories</a><h1 id="title-memory" tabindex="-1">Memory detail</h1><div id="detail-meta"></div></div>
|
|
543
|
+
<div class="form-actions" id="detail-actions" hidden>
|
|
544
|
+
<a id="detail-graph" class="button" href="#graph"><span data-icon="graph"></span>View on graph</a>
|
|
545
|
+
<a id="detail-context" class="button" href="#context"><span data-icon="context"></span>Assemble context</a>
|
|
546
|
+
<a id="detail-outcome" class="button" href="#project-kit" hidden><span data-icon="activity"></span>Record outcome</a>
|
|
547
|
+
</div>
|
|
548
|
+
</div>
|
|
549
|
+
<div id="detail-body" aria-busy="true"></div>
|
|
550
|
+
</section>
|
|
372
551
|
<section id="page-graph" aria-labelledby="title-graph" hidden>
|
|
373
|
-
<div class="page-head"><div><h1 id="title-graph" tabindex="-1">Graph</h1><p class="muted">Explore
|
|
552
|
+
<div class="page-head"><div><h1 id="title-graph" tabindex="-1">Graph</h1><p class="muted">Explore project membership, recording agents, and links between memories.</p></div></div>
|
|
374
553
|
<div class="stack">
|
|
375
554
|
<div class="panel panel-body filter-bar">
|
|
376
555
|
<label class="label search-field" for="graph-query">Find a node<span data-icon="search"></span><input id="graph-query" type="search" maxlength="200" placeholder="Filter by name"></label>
|
|
377
556
|
<label class="label" for="graph-project">Project<select id="graph-project"><option value="">All projects</option></select></label>
|
|
378
557
|
<label class="label" for="graph-kind">Node type<select id="graph-kind"><option value="">All types</option><option value="project">Projects</option><option value="agent">Agents</option><option value="memory">Memories</option></select></label>
|
|
558
|
+
<label class="label" for="graph-relation">Relationship<select id="graph-relation"><option value="">All relationships</option><option value="recorded">Memory links</option><option value="belongs-to">Project membership</option><option value="recorded-by">Recording agent</option><option value="related">Related</option><option value="supports">Supports</option><option value="contradicts">Contradicts</option><option value="supersedes">Supersedes</option></select></label>
|
|
379
559
|
<div class="form-actions toolbar-end graph-mode" role="group" aria-label="Graph view">
|
|
380
560
|
<button type="button" class="button" id="graph-mode-map" aria-pressed="true"><span data-icon="graph"></span>Map</button>
|
|
381
561
|
<button type="button" class="button" id="graph-mode-list" aria-pressed="false"><span data-icon="list"></span>List</button>
|
|
@@ -383,10 +563,11 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
383
563
|
</div>
|
|
384
564
|
<div class="panel" id="graph-panel" aria-busy="true">
|
|
385
565
|
<div class="panel-head"><h2>Memory relationships</h2><span id="graph-count" class="muted small" role="status"></span></div>
|
|
566
|
+
<div id="graph-lens" class="graph-lens" hidden><p class="small muted" id="graph-lens-label"></p><button class="button" type="button" id="graph-lens-clear">Show full graph</button></div>
|
|
386
567
|
<div id="graph-status"></div>
|
|
387
568
|
<div class="graph-body" id="graph-body" hidden>
|
|
388
569
|
<div class="graph-stage" id="graph-stage">
|
|
389
|
-
<svg id="graph-svg" xmlns="http://www.w3.org/2000/svg" role="group" aria-label="Memory relationship map. Use arrow keys to navigate nodes."></svg>
|
|
570
|
+
<svg id="graph-svg" xmlns="http://www.w3.org/2000/svg" role="group" aria-label="Memory relationship map. Use arrow keys to navigate nodes or links."></svg>
|
|
390
571
|
<div class="graph-tools" role="group" aria-label="Map zoom">
|
|
391
572
|
<button class="button icon-button" type="button" id="graph-out" aria-label="Zoom out" title="Zoom out"><span data-icon="minus"></span></button>
|
|
392
573
|
<button class="button icon-button" type="button" id="graph-in" aria-label="Zoom in" title="Zoom in"><span data-icon="plus"></span></button>
|
|
@@ -400,27 +581,42 @@ export const WORKSPACE_HTML = String.raw `<!doctype html>
|
|
|
400
581
|
<div id="graph-legend" class="legend" hidden>
|
|
401
582
|
<span><i class="legend-glyph project"></i> Project</span><span><i class="legend-glyph agent"></i> Agent</span><span><i class="legend-glyph memory"></i> Memory</span>
|
|
402
583
|
<span><i class="legend-line project-edge"></i> Belongs to project</span><span><i class="legend-line agent-edge"></i> Recorded by agent</span>
|
|
584
|
+
<span><i class="legend-line recorded-edge"></i> Related</span><span><i class="legend-line supports"></i> Supports</span>
|
|
585
|
+
<span><i class="legend-line contradicts"></i> Contradicts</span><span><i class="legend-line supersedes"></i> Supersedes</span>
|
|
403
586
|
</div>
|
|
404
587
|
</div>
|
|
405
588
|
</div>
|
|
406
589
|
</section>
|
|
407
590
|
<section id="page-context" aria-labelledby="title-context" hidden>
|
|
408
|
-
<div class="page-head"><div><h1 id="title-context" tabindex="-1">Context</h1><p class="muted">
|
|
591
|
+
<div class="page-head"><div><h1 id="title-context" tabindex="-1">Context Lab</h1><p class="muted">Assemble a task brief, inspect its evidence, and compare retrieval profiles.</p></div></div>
|
|
409
592
|
<div class="split context-layout">
|
|
410
593
|
<div class="panel">
|
|
411
|
-
<div class="panel-head"><h2>
|
|
594
|
+
<div class="panel-head"><h2>Build a brief</h2></div>
|
|
412
595
|
<form class="panel-body context-config" id="context-form">
|
|
413
596
|
<label class="label" for="context-project">Project<select id="context-project" required><option value="">Select a project</option></select></label>
|
|
414
597
|
<label class="label" for="context-task">Task<textarea id="context-task" rows="5" maxlength="4096" required placeholder="Describe what the agent will work on."></textarea></label>
|
|
415
|
-
<
|
|
598
|
+
<div class="context-settings-pair">
|
|
599
|
+
<label class="label" for="context-budget">Token budget<input id="context-budget" type="number" inputmode="numeric" min="0" max="12000" step="1" value="2000" required aria-describedby="context-estimate"></label>
|
|
600
|
+
<label class="label" for="context-profile">Retrieval<select id="context-profile"><option value="adaptive">Adaptive</option><option value="legacy">Legacy</option></select></label>
|
|
601
|
+
</div>
|
|
602
|
+
<p class="small muted" id="context-estimate">Token usage is estimated for the assembled brief.</p>
|
|
603
|
+
<label class="inline-check small" id="context-related-row"><input type="checkbox" id="context-related" checked>Expand through recorded supporting and related links</label>
|
|
416
604
|
<label class="inline-check small" id="context-cross-row" hidden><input type="checkbox" id="context-cross">Include eligible memories from other projects</label>
|
|
417
605
|
<div id="context-gate"></div>
|
|
418
606
|
<div class="form-actions"><button class="button primary" id="context-submit" type="submit" disabled><span data-icon="context"></span>Assemble context</button></div>
|
|
419
607
|
</form>
|
|
420
608
|
</div>
|
|
421
|
-
<div class="panel" id="context-result"><div class="panel-head"><h2>
|
|
609
|
+
<div class="panel" id="context-result"><div class="panel-head context-result-head"><h2>Brief and evidence</h2><span class="small muted" id="context-result-state" role="status">Ready for a task</span></div><div id="context-output"></div></div>
|
|
422
610
|
</div>
|
|
423
611
|
</section>
|
|
612
|
+
${PROJECT_KIT_MARKUP}
|
|
613
|
+
<section id="page-integrations" aria-labelledby="title-integrations" hidden>
|
|
614
|
+
<div class="page-head">
|
|
615
|
+
<div><h1 id="title-integrations" tabindex="-1">Integrations & health</h1><p class="muted">Check local storage and see which capabilities each agent supports.</p></div>
|
|
616
|
+
<button class="button" type="button" id="integrations-refresh"><span data-icon="refresh"></span>Run checks</button>
|
|
617
|
+
</div>
|
|
618
|
+
<div id="integrations-body" aria-busy="true"></div>
|
|
619
|
+
</section>
|
|
424
620
|
<section id="page-features" aria-labelledby="title-features" hidden>
|
|
425
621
|
<div class="page-head"><div><h1 id="title-features" tabindex="-1">Features</h1><p class="muted">Control how Shadow Brain assembles context and updates the workspace.</p></div></div>
|
|
426
622
|
<div class="stack settings">
|
|
@@ -457,6 +653,7 @@ const glyphs = {
|
|
|
457
653
|
project: '<path d="M3.5 7V6A1.5 1.5 0 0 1 5 4.5h4l2 2h8A1.5 1.5 0 0 1 20.5 8v10A1.5 1.5 0 0 1 19 19.5H5A1.5 1.5 0 0 1 3.5 18Z"/>',
|
|
458
654
|
agent: '<rect x="4.5" y="7" width="15" height="12" rx="3"/><path d="M12 7V4m-8 9H2m20 0h-2m-11-.5h.02m5.98 0H15m-5 3h4"/>',
|
|
459
655
|
activity: '<path d="M3 12h4l3-7 4 14 3-7h4"/>',
|
|
656
|
+
integrations: '<path d="M9 3v4m6-4v4m-8 0h10v4a5 5 0 0 1-5 5h0a5 5 0 0 1-5-5zm5 9v5"/>',
|
|
460
657
|
search: '<circle cx="10.5" cy="10.5" r="6"/><path d="m15 15 5 5"/>',
|
|
461
658
|
source: '<path d="M9.5 14.5 14.5 9m-6 8.5-1 1a3.5 3.5 0 0 1-5-5L7 9a3.5 3.5 0 0 1 5 0m3.5-2.5 1-1a3.5 3.5 0 0 1 5 5L17 15a3.5 3.5 0 0 1-5 0"/>',
|
|
462
659
|
list: '<path d="M8 6h12M8 12h12M8 18h12M4 6h.01M4 12h.01M4 18h.01"/>',
|
|
@@ -466,12 +663,15 @@ const glyphs = {
|
|
|
466
663
|
warning: '<path d="m12 3 10 17H2L12 3zm0 6v4m0 3h.01"/>',
|
|
467
664
|
refresh: '<path d="M19.5 9a8 8 0 1 0 .3 5.5M20 4v5h-5"/>'
|
|
468
665
|
};
|
|
469
|
-
const viewNames = { overview: 'Overview', memories: 'Memories', graph: 'Graph', context: 'Context', features: 'Features' };
|
|
666
|
+
const viewNames = { overview: 'Overview', memories: 'Memories', memory: 'Memory detail', graph: 'Graph', context: 'Context Lab', 'project-kit': 'Project Kit', integrations: 'Integrations', features: 'Features' };
|
|
667
|
+
const reviewStates = { active: 'Active', contested: 'Needs review', archived: 'Archived', superseded: 'Superseded' };
|
|
668
|
+
const linkTypes = { related: 'Related', supports: 'Supports', contradicts: 'Contradicts', supersedes: 'Supersedes' };
|
|
470
669
|
const definitions = [
|
|
471
670
|
{ id: 'contextAssembly', label: 'Context assembly', description: 'Build a task-specific brief from saved project memories.' },
|
|
472
671
|
{ id: 'crossProjectContext', label: 'Cross-project context', description: 'Allow context assembly to include eligible memories from other projects when requested.' },
|
|
473
672
|
{ id: 'automaticBriefing', label: 'Automatic briefing', description: 'Include a context brief when a supported agent starts work in a project.' },
|
|
474
|
-
{ id: 'liveActivity', label: 'Live activity', description: 'Refresh the workspace activity feed automatically.' }
|
|
673
|
+
{ id: 'liveActivity', label: 'Live activity', description: 'Refresh the workspace activity feed automatically.' },
|
|
674
|
+
{ id: 'activityRecording', label: 'Record agent activity', description: 'Record bounded tool timing and explicitly reported session phases.' }
|
|
475
675
|
];
|
|
476
676
|
const $ = (id) => document.getElementById(id);
|
|
477
677
|
const esc = (value) => String(value ?? '').replace(/[&<>"']/g, (char) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char]));
|
|
@@ -487,11 +687,14 @@ const tags = (items) => '<div class="tags">' + items.filter(([, value]) => value
|
|
|
487
687
|
const DATE = new Intl.DateTimeFormat(undefined, { month: 'short', day: 'numeric', year: 'numeric' });
|
|
488
688
|
const TIME = new Intl.DateTimeFormat(undefined, { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
|
|
489
689
|
const state = {
|
|
490
|
-
view: '', projects: [], overviewLoaded: false, overviewPending: null,
|
|
690
|
+
view: '', route: '', projects: [], overviewLoaded: false, overviewPending: null, overviewScope: '', overviewRequest: 0, overviewRequestScope: '',
|
|
491
691
|
activity: [], activityEnabled: null, activityPending: false, features: null, featurePending: null, featureSaving: false,
|
|
692
|
+
health: { snapshot: null, project: '', filter: '', request: 0 }, projectKit: null,
|
|
492
693
|
memory: { query: null, request: 0, controller: null, limit: 20, offset: 0 },
|
|
493
|
-
|
|
494
|
-
|
|
694
|
+
detail: { snapshot: null, id: '', project: '', back: '#memories', request: 0, targetRequest: 0, saving: false },
|
|
695
|
+
graph: { snapshot: null, project: null, mode: matchMedia('(max-width: 690px)').matches ? 'list' : 'map', selected: '', selectedEdge: '', focusMemory: '', lens: '', nodes: [], edges: [], layout: null, zoom: 1, panX: 0, panY: 0, request: 0, controller: null, drag: null },
|
|
696
|
+
context: { pack: null, params: null, comparison: null, comparing: false, pending: false, dirty: false, routeProject: '' },
|
|
697
|
+
integrations: { snapshot: null, pending: false }
|
|
495
698
|
};
|
|
496
699
|
|
|
497
700
|
document.querySelectorAll('[data-icon]').forEach((element) => { element.innerHTML = icon(element.dataset.icon); });
|
|
@@ -575,13 +778,40 @@ function routeLink(view, params) {
|
|
|
575
778
|
return '#' + queryPath(view, params);
|
|
576
779
|
}
|
|
577
780
|
|
|
781
|
+
function memoryLink(id, project, label, className = 'record-link') {
|
|
782
|
+
if (!id || !project) return esc(label);
|
|
783
|
+
return '<a class="' + className + '" href="' + esc(routeLink('memory', { id, project })) + '">' + esc(label) + '</a>';
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
function reviewLabel(value) {
|
|
787
|
+
return Object.hasOwn(reviewStates, value) ? reviewStates[value] : '';
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
function reviewTag(value) {
|
|
791
|
+
const label = reviewLabel(value);
|
|
792
|
+
return label ? '<span class="tag state-' + value + '">' + label + '</span>' : '';
|
|
793
|
+
}
|
|
794
|
+
|
|
578
795
|
function renderSummary(totals) {
|
|
579
796
|
return '<dl class="summary">' + [
|
|
580
|
-
['memory', '
|
|
797
|
+
['memory', 'Total stored memories', totals.memories], ['project', 'Projects', totals.projects], ['agent', 'Contributing agents', totals.agents]
|
|
581
798
|
].map(([image, label, value]) => '<div class="summary-item"><span class="tile-icon">' + icon(image) +
|
|
582
799
|
'</span><div><dt>' + label + '</dt><dd>' + number(value) + '</dd></div></div>').join('') + '</dl>';
|
|
583
800
|
}
|
|
584
801
|
|
|
802
|
+
function renderOverviewSummary(data) {
|
|
803
|
+
const counts = data.quality || {};
|
|
804
|
+
const total = Number(counts.total) || 0;
|
|
805
|
+
const sources = Number(counts.sourceBacked) || 0;
|
|
806
|
+
const percent = total ? Math.round(Math.min(1, sources / total) * 100) : 0;
|
|
807
|
+
const project = data.scope?.projectId;
|
|
808
|
+
const scope = project ? state.projects.find((item) => item.id === project)?.name || project : 'All projects';
|
|
809
|
+
return '<div class="overview-summary"><div class="overview-lead"><div class="coverage-ring" aria-hidden="true" style="--coverage:' + percent + '%"><span>' +
|
|
810
|
+
(total ? percent + '%' : '—') + '</span></div><div><h2>Recorded source coverage</h2><p class="small">' +
|
|
811
|
+
esc(scope) + ' · ' + number(sources) + ' of ' + number(total) + (data.truncated ? ' sampled memories' : ' memories') +
|
|
812
|
+
'</p><button class="button quiet" type="button" id="attention-jump">Review attention queue ' + icon('chevron', true) + '</button></div></div>' + renderSummary(data.totals || {}) + '</div>';
|
|
813
|
+
}
|
|
814
|
+
|
|
585
815
|
function renderProjects(projects) {
|
|
586
816
|
if (!projects.length) return blank('project', 'No projects yet', 'Projects appear after an agent stores a memory with a project scope.');
|
|
587
817
|
const max = Math.max(1, ...projects.map((project) => Number(project.memories) || 0));
|
|
@@ -601,8 +831,85 @@ function renderOverviewMemories(memories) {
|
|
|
601
831
|
if (!memories.length) return '';
|
|
602
832
|
return '<div class="panel"><div class="panel-head"><h2>Latest memories</h2><a class="button quiet" href="#memories">Browse ' + icon('chevron', true) +
|
|
603
833
|
'</a></div><ul class="list">' + memories.map((item) => '<li class="overview-memory"><div class="memory-head"><strong>' +
|
|
604
|
-
|
|
605
|
-
tags([['project', item.projectName || item.project], ['agent', item.agent], ['memory', nice(item.category)]]) + '</li>').join('') + '</ul></div>';
|
|
834
|
+
memoryLink(item.id, item.project, item.title || nice(item.category) || 'Memory') + '</strong>' + dateMarkup(item.updatedAt, true) + '</div>' +
|
|
835
|
+
tags([['project', item.projectName || item.project], ['agent', item.agent], ['memory', nice(item.category)], ['state-' + (item.effectiveState || ''), reviewLabel(item.effectiveState)]]) + '</li>').join('') + '</ul></div>';
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
function renderQuality(data) {
|
|
839
|
+
const quality = data.quality;
|
|
840
|
+
if (!quality || !Number.isFinite(quality.total)) return '';
|
|
841
|
+
const project = data.scope?.projectId || '';
|
|
842
|
+
const scope = data.truncated ? 'Based on the latest ' + number(data.inventoryLimit) + ' stored memories.' :
|
|
843
|
+
project ? 'Stored memories in this project.' : 'Stored memories across projects.';
|
|
844
|
+
const statusCounts = Object.entries(reviewStates).map(([key, label]) => ({ key, label, count: Number(quality[key]) || 0 }));
|
|
845
|
+
const coverage = quality.total ? Math.max(0, Math.min(100, Number(quality.sourceBacked || 0) / quality.total * 100)) : 0;
|
|
846
|
+
const statusBars = statusCounts.map(({ key, count }) =>
|
|
847
|
+
'<span class="quality-' + key + '" style="width:' + Math.max(0, Math.min(100, count / quality.total * 100)) + '%"></span>').join('');
|
|
848
|
+
const statusLinks = statusCounts.map(({ key, label, count }) => '<a href="' + esc(routeLink('memories', { project, state: key })) +
|
|
849
|
+
'"><i aria-hidden="true" class="quality-' + key + '"></i>' + esc(label) + ' <strong>' + number(count) + '</strong></a>').join('');
|
|
850
|
+
return '<div class="panel"><div class="panel-head"><div><h2>Review and sources</h2><p class="small muted">' + esc(scope) +
|
|
851
|
+
'</p></div></div>' + (quality.total ? '<div class="panel-body stack"><div class="stack" style="gap:8px"><h3>Memory status</h3>' +
|
|
852
|
+
'<div class="quality-track" aria-hidden="true">' + statusBars + '</div><div class="quality-labels">' + statusLinks + '</div></div>' +
|
|
853
|
+
'<div class="stack" style="gap:8px"><div class="quality-source"><h3>Recorded sources</h3><span class="small muted">' +
|
|
854
|
+
number(quality.sourceBacked) + ' of ' + number(quality.total) + ' memories</span></div>' +
|
|
855
|
+
'<div class="quality-track" aria-hidden="true"><span class="quality-active" style="width:' + coverage + '%"></span></div></div>' +
|
|
856
|
+
(Number.isFinite(quality.reviewed) ? '<p class="small muted">' + number(quality.reviewed) + ' with a recorded review</p>' : '') +
|
|
857
|
+
'</div>' : blank('memory', 'No memories in scope', 'Save a memory to see review and source counts here.')) + '</div>';
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
const healthIssues = {
|
|
861
|
+
contested: 'Needs review', contradiction: 'Contradictory link', 'orphaned-link': 'Missing linked memory',
|
|
862
|
+
duplicate: 'Repeated content', 'no-source': 'No recorded source', 'older-memory': 'Older than 90 days'
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
function renderAttention() {
|
|
866
|
+
const target = $('attention-results');
|
|
867
|
+
const store = state.health;
|
|
868
|
+
if (!target || !store.snapshot || store.project !== state.overviewScope) return;
|
|
869
|
+
const data = store.snapshot;
|
|
870
|
+
const counts = data.counts || {};
|
|
871
|
+
const rows = list(data.queue).filter((item) => !store.filter || list(item.issues).some((issue) => issue.kind === store.filter));
|
|
872
|
+
const options = Object.entries(healthIssues).map(([value, label]) => '<option value="' + value + '">' + label + '</option>').join('');
|
|
873
|
+
const controls = $('attention-filter');
|
|
874
|
+
if (!controls.childElementCount) controls.innerHTML = '<option value="">All issues</option>' + options;
|
|
875
|
+
controls.value = store.filter;
|
|
876
|
+
controls.disabled = !list(data.queue).length;
|
|
877
|
+
const scopeNote = (data.coverage?.truncated ? 'Checked the latest ' + number(data.coverage.limit) + ' memories in scope.' : 'Checked ' + number(counts.total) + ' memories in scope.');
|
|
878
|
+
$('attention-scope').textContent = scopeNote;
|
|
879
|
+
const warnings = list(data.warnings).map((warning) => '<p class="small muted panel-body">' + esc(warning) + '</p>').join('');
|
|
880
|
+
const count = '<p class="small muted panel-body">Showing ' + number(rows.length) + ' of ' + number(data.queueTotal) + ' flagged memories' +
|
|
881
|
+
(store.filter ? ' · filter applied to loaded items' : '') + '.</p>';
|
|
882
|
+
const items = rows.map((item) => '<li class="attention-row"><span class="tile-icon">' + icon('warning', true) + '</span><div>' +
|
|
883
|
+
memoryLink(item.id, item.projectId, item.title || item.id) + '<div class="tags">' +
|
|
884
|
+
tags([['project', state.projects.find((project) => project.id === item.projectId)?.name || item.projectId], ['memory', nice(item.category)]]) +
|
|
885
|
+
list(item.issues).map((issue) => '<span class="tag check-' + (issue.severity === 'review' ? 'warn' : '') + '">' +
|
|
886
|
+
esc(issue.label || healthIssues[issue.kind] || 'Review') + '</span>').join('') + '</div></div></li>').join('');
|
|
887
|
+
target.innerHTML = rows.length ? count + '<ul class="list attention-list" tabindex="0" aria-label="Flagged memories">' + items + '</ul>' + warnings :
|
|
888
|
+
blank('memory', store.filter ? 'No loaded items match' : 'No attention items', store.filter ? 'Choose another issue or show all loaded items.' : 'Review flags will appear here as the memory store changes.') + warnings;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
async function loadHealth(project) {
|
|
892
|
+
const target = $('attention-results');
|
|
893
|
+
if (!target) return;
|
|
894
|
+
const store = state.health;
|
|
895
|
+
const request = ++store.request;
|
|
896
|
+
if (store.project !== project) store.filter = '';
|
|
897
|
+
store.project = project;
|
|
898
|
+
store.snapshot = null;
|
|
899
|
+
$('attention-filter').disabled = true;
|
|
900
|
+
$('attention-scope').textContent = '';
|
|
901
|
+
target.innerHTML = skeleton('Checking memory health…');
|
|
902
|
+
setBusy(target, true);
|
|
903
|
+
try {
|
|
904
|
+
const snapshot = await api(queryPath('memory-health', { project, limit: 30 }));
|
|
905
|
+
if (request !== store.request || target !== $('attention-results')) return;
|
|
906
|
+
store.snapshot = snapshot;
|
|
907
|
+
renderAttention();
|
|
908
|
+
} catch (error) {
|
|
909
|
+
if (request === store.request && target === $('attention-results')) target.innerHTML = errorMarkup(error.message, 'health');
|
|
910
|
+
} finally {
|
|
911
|
+
if (request === store.request && target === $('attention-results')) setBusy(target, false);
|
|
912
|
+
}
|
|
606
913
|
}
|
|
607
914
|
|
|
608
915
|
function renderActivity() {
|
|
@@ -633,32 +940,46 @@ function renderOverview(data) {
|
|
|
633
940
|
number(data.inventoryLimit) + ' stored memories.</p>' : '';
|
|
634
941
|
const pending = data.storageHealth?.status === 'pending' ? '<p class="status small muted notice">The memory store has pending writes.</p>' : '';
|
|
635
942
|
const projectNote = state.projects.length > 8 ? 'Top projects by saved memory count' : 'Saved memories by project';
|
|
636
|
-
|
|
637
|
-
|
|
943
|
+
optionSet('overview-quality-project', 'All projects', state.projects, data.scope?.projectId || '');
|
|
944
|
+
$('overview-context').href = routeLink('context', { project: data.scope?.projectId });
|
|
945
|
+
$('overview-body').innerHTML = '<div id="overview-refresh"></div>' + pending + renderOverviewSummary(data) + sample +
|
|
946
|
+
'<div class="split overview-columns"><div class="stack"><section class="panel" aria-labelledby="attention-heading"><div class="panel-head"><div>' +
|
|
947
|
+
'<h2 id="attention-heading" tabindex="-1">Attention queue</h2><p class="small muted" id="attention-scope"></p></div>' +
|
|
948
|
+
'<label class="label" for="attention-filter">Issue<select id="attention-filter" disabled></select></label></div>' +
|
|
949
|
+
'<div id="attention-results"></div></section>' + renderQuality(data) + '<div class="panel"><div class="panel-head"><div><h2>Project inventory</h2>' +
|
|
638
950
|
'<p class="muted small">' + projectNote + '</p></div><a class="button quiet" href="#memories">Browse ' + icon('chevron', true) +
|
|
639
951
|
'</a></div>' + renderProjects(state.projects) + '</div>' + renderOverviewMemories(list(data.recentMemories)) +
|
|
640
|
-
'</div><div class="panel"><div class="panel-head"><h2>Recent activity</h2><span id="activity-status" class="small muted"></span></div><div id="activity-body"></div></div></div>';
|
|
952
|
+
'</div><div class="panel overview-activity"><div class="panel-head"><div><h2>Recent activity</h2><span id="activity-status" class="small muted"></span></div><a class="button quiet" href="/live">Open live operations ' + icon('chevron', true) + '</a></div><div id="activity-body"></div></div></div>';
|
|
641
953
|
renderActivity();
|
|
954
|
+
if (state.view === 'overview') void loadHealth(data.scope?.projectId || '');
|
|
642
955
|
}
|
|
643
956
|
|
|
644
957
|
function syncProjectOptions() {
|
|
645
|
-
const contextProject = $('context-project').value;
|
|
958
|
+
const contextProject = state.context.routeProject || $('context-project').value;
|
|
646
959
|
const graphProject = state.view === 'graph' ? routeParams().get('project') : null;
|
|
647
|
-
optionSet('context-project', 'Select a project', state.projects, contextProject
|
|
960
|
+
optionSet('context-project', 'Select a project', state.projects, contextProject);
|
|
961
|
+
if ($('context-project').value === state.context.routeProject) state.context.routeProject = '';
|
|
648
962
|
optionSet('graph-project', 'All projects', state.projects, $('graph-project').value || graphProject);
|
|
649
963
|
}
|
|
650
964
|
|
|
651
|
-
async function loadOverview(force = false) {
|
|
652
|
-
if (state.overviewPending) return state.overviewPending;
|
|
653
|
-
if (state.overviewLoaded && !force) return;
|
|
965
|
+
async function loadOverview(force = false, project = '') {
|
|
966
|
+
if (state.overviewPending && state.overviewRequestScope === project) return state.overviewPending;
|
|
967
|
+
if (state.overviewLoaded && !force && state.overviewScope === project) return;
|
|
968
|
+
const request = ++state.overviewRequest;
|
|
969
|
+
state.overviewRequestScope = project;
|
|
654
970
|
const target = $('overview-body');
|
|
655
971
|
if (!state.overviewLoaded) target.innerHTML = '<div class="panel">' + skeleton('Loading workspace…') + '</div>';
|
|
972
|
+
else if (state.overviewScope !== project && $('overview-refresh')) $('overview-refresh').innerHTML =
|
|
973
|
+
'<div class="notice status small muted" role="status">Loading the selected project. Showing the previous scope.</div>';
|
|
656
974
|
setBusy(target, true);
|
|
657
975
|
state.overviewPending = (async () => {
|
|
658
976
|
try {
|
|
659
|
-
const data = await api('overview');
|
|
977
|
+
const data = await api(queryPath('overview', { project }));
|
|
978
|
+
if (request !== state.overviewRequest) return;
|
|
660
979
|
state.overviewLoaded = true;
|
|
980
|
+
state.overviewScope = project;
|
|
661
981
|
state.projects = list(data.projects);
|
|
982
|
+
state.projectKit = data.projectKit ?? null;
|
|
662
983
|
state.activity = list(data.activity);
|
|
663
984
|
if (typeof data.features?.liveActivity === 'boolean') state.activityEnabled = data.features.liveActivity;
|
|
664
985
|
if (data.version) $('rail-version').textContent = 'Version ' + data.version;
|
|
@@ -666,12 +987,15 @@ async function loadOverview(force = false) {
|
|
|
666
987
|
renderOverview(data);
|
|
667
988
|
renderContextControls();
|
|
668
989
|
} catch (error) {
|
|
990
|
+
if (request !== state.overviewRequest) return;
|
|
669
991
|
if (!state.overviewLoaded) target.innerHTML = errorMarkup(error.message, 'overview');
|
|
670
|
-
else $('overview-refresh').innerHTML = '<div class="notice">' + errorMarkup('Could not refresh the workspace. Showing the last loaded overview.', 'overview') + '</div>';
|
|
992
|
+
else if ($('overview-refresh')) $('overview-refresh').innerHTML = '<div class="notice">' + errorMarkup('Could not refresh the workspace. Showing the last loaded overview.', 'overview') + '</div>';
|
|
671
993
|
renderContextControls();
|
|
672
994
|
} finally {
|
|
673
|
-
|
|
674
|
-
|
|
995
|
+
if (request === state.overviewRequest) {
|
|
996
|
+
setBusy(target, false);
|
|
997
|
+
state.overviewPending = null;
|
|
998
|
+
}
|
|
675
999
|
}
|
|
676
1000
|
})();
|
|
677
1001
|
return state.overviewPending;
|
|
@@ -705,7 +1029,8 @@ function memoryQuery() {
|
|
|
705
1029
|
q: $('memory-query').value.trim(),
|
|
706
1030
|
project: $('memory-project').value,
|
|
707
1031
|
category: $('memory-category').value,
|
|
708
|
-
agent: $('memory-agent').value
|
|
1032
|
+
agent: $('memory-agent').value,
|
|
1033
|
+
state: $('memory-state').value
|
|
709
1034
|
};
|
|
710
1035
|
}
|
|
711
1036
|
|
|
@@ -731,8 +1056,8 @@ function renderMemories(data) {
|
|
|
731
1056
|
const detail = reasons.length ? '<details><summary>Why this matched</summary><ul class="small muted">' +
|
|
732
1057
|
reasons.map((reason) => '<li>' + esc(reason) + '</li>').join('') + '</ul></details>' : '';
|
|
733
1058
|
const snippet = compact(item.snippet) && compact(item.snippet) !== compact(title) ? '<p>' + esc(item.snippet) + '</p>' : '';
|
|
734
|
-
return '<li class="memory-row"><div class="memory-head"><h3>' +
|
|
735
|
-
tags([['project', item.projectName || item.project], ['agent', item.agent], ['memory', nice(item.category)]]) + snippet +
|
|
1059
|
+
return '<li class="memory-row"><div class="memory-head"><h3>' + memoryLink(item.id, item.project, title) + '</h3>' + dateMarkup(item.updatedAt, true) + '</div>' +
|
|
1060
|
+
tags([['project', item.projectName || item.project], ['agent', item.agent], ['memory', nice(item.category)], ['state-' + (item.effectiveState || ''), reviewLabel(item.effectiveState)]]) + snippet +
|
|
736
1061
|
sourceMarkup([...list(item.sources), item.source]) + detail + '</li>';
|
|
737
1062
|
}).join('') + '</ul>' + (offset || total > items.length ? '<div class="list-footer"><span class="muted small">' +
|
|
738
1063
|
number(offset + 1) + '–' + number(offset + items.length) + ' of ' + number(total) + '</span><div class="form-actions">' +
|
|
@@ -771,9 +1096,193 @@ async function loadMemories(query, offset = 0) {
|
|
|
771
1096
|
}
|
|
772
1097
|
}
|
|
773
1098
|
|
|
1099
|
+
function linkCaption(link, memoryId) {
|
|
1100
|
+
if (link.type === 'related') return 'Related to';
|
|
1101
|
+
if (link.fromId === memoryId) return linkTypes[link.type] || nice(link.type);
|
|
1102
|
+
return { supports: 'Supported by', contradicts: 'Contradicted by', supersedes: 'Superseded by' }[link.type] || nice(link.type);
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
function renderReviewHistory(events) {
|
|
1106
|
+
const recent = [...list(events)].sort((a, b) => Number(b.revision) - Number(a.revision)).slice(0, 12);
|
|
1107
|
+
if (!recent.length) return '';
|
|
1108
|
+
const rows = recent.map((event) => {
|
|
1109
|
+
const command = event.command || {};
|
|
1110
|
+
const label = command.kind === 'review' ? 'Recorded review · ' + reviewLabel(command.state) :
|
|
1111
|
+
command.kind === 'link' ? 'Added link · ' + (linkTypes[command.type] || nice(command.type)) :
|
|
1112
|
+
command.kind === 'unlink' ? 'Removed link' : 'Memory change';
|
|
1113
|
+
return '<li class="detail-row"><div class="memory-head"><strong>' + esc(label) + '</strong>' + dateMarkup(event.at) + '</div>' +
|
|
1114
|
+
(command.note ? '<p class="detail-note small muted">' + esc(command.note) + '</p>' : '') + '</li>';
|
|
1115
|
+
}).join('');
|
|
1116
|
+
return '<section class="panel"><div class="panel-head"><h2>Recent changes</h2></div><ul class="list">' + rows + '</ul></section>';
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
function renderDetail(snapshot) {
|
|
1120
|
+
const { memory, review, links } = snapshot;
|
|
1121
|
+
const project = memory.project || memory.projectId || state.detail.project;
|
|
1122
|
+
const effective = snapshot.effectiveState || memory.effectiveState || review?.state;
|
|
1123
|
+
const title = memory.title || nice(memory.category) || 'Memory detail';
|
|
1124
|
+
const content = memory.content ?? memory.snippet;
|
|
1125
|
+
const ownState = review?.state || 'active';
|
|
1126
|
+
const reviewOptions = Object.entries(reviewStates).filter(([key]) => key !== 'superseded')
|
|
1127
|
+
.map(([key, label]) => '<option value="' + key + '"' + (key === ownState ? ' selected' : '') + '>' + label + '</option>').join('');
|
|
1128
|
+
const relationOptions = Object.entries(linkTypes).map(([key, label]) => '<option value="' + key + '">' + label + '</option>').join('');
|
|
1129
|
+
const replacement = list(links).find((link) => link.type === 'supersedes' && link.toId === memory.id);
|
|
1130
|
+
const titleEl = $('title-memory');
|
|
1131
|
+
titleEl.textContent = title;
|
|
1132
|
+
$('detail-meta').innerHTML = tags([
|
|
1133
|
+
['project', memory.projectName || project], ['agent', memory.agent], ['memory', nice(memory.category)],
|
|
1134
|
+
['state-' + (effective || ''), reviewLabel(effective)]
|
|
1135
|
+
]) + (memory.createdAt || memory.updatedAt ? '<div class="small muted">' + dateMarkup(memory.createdAt || memory.updatedAt, true) + '</div>' : '');
|
|
1136
|
+
$('detail-graph').href = routeLink('graph', { project, memory: memory.id });
|
|
1137
|
+
$('detail-context').href = routeLink('context', { project });
|
|
1138
|
+
const projectFeedback = document.documentElement.dataset.projectLocal === 'true' || state.projectKit?.projectId === project;
|
|
1139
|
+
$('detail-outcome').hidden = !projectFeedback;
|
|
1140
|
+
$('detail-outcome').href = routeLink('project-kit', { memory: memory.id });
|
|
1141
|
+
$('detail-actions').hidden = false;
|
|
1142
|
+
const linkRows = list(links).map((link) => {
|
|
1143
|
+
const outgoing = link.fromId === memory.id;
|
|
1144
|
+
const endpoint = outgoing ? link.toId : link.fromId;
|
|
1145
|
+
const endpointTitle = (outgoing ? link.toTitle : link.fromTitle) || endpoint;
|
|
1146
|
+
const endpointLink = link.orphaned ? esc(endpointTitle) : memoryLink(endpoint, project, endpointTitle);
|
|
1147
|
+
return '<li class="detail-row"><div class="tags"><span class="tag memory">' + esc(linkCaption(link, memory.id)) + '</span>' +
|
|
1148
|
+
endpointLink + '</div>' +
|
|
1149
|
+
(link.note ? '<p class="detail-note small muted">' + esc(link.note) + '</p>' : '') +
|
|
1150
|
+
'<details><summary class="small muted">Remove link</summary><div class="form-actions"><button class="button" type="button" data-unlink="' +
|
|
1151
|
+
esc(link.id) + '">Remove this link</button></div></details></li>';
|
|
1152
|
+
}).join('');
|
|
1153
|
+
const warnings = list(snapshot.warnings).map((warning) => '<p class="status small muted notice">' + esc(warning) + '</p>').join('');
|
|
1154
|
+
$('detail-body').innerHTML = warnings + '<div class="split detail-layout"><div class="stack">' +
|
|
1155
|
+
'<section class="panel"><div class="panel-head"><h2>Saved memory</h2></div><div class="panel-body">' +
|
|
1156
|
+
'<p class="memory-content">' + esc(content || 'No saved text.') + '</p></div></section>' +
|
|
1157
|
+
'<section class="panel"><div class="panel-head"><h2>Recorded sources</h2></div><div class="panel-body detail-sources">' +
|
|
1158
|
+
sourceMarkup([...list(memory.sources), ...list(snapshot.sources), memory.source]) + '</div></section>' +
|
|
1159
|
+
'<section class="panel"><div class="panel-head"><h2>Connections</h2><span class="small muted">' + countLabel(list(links).length, 'link') +
|
|
1160
|
+
'</span></div>' + (linkRows ? '<ul class="list">' + linkRows + '</ul>' :
|
|
1161
|
+
'<div class="panel-body"><p class="small muted">No links recorded for this memory.</p></div>') +
|
|
1162
|
+
'<div class="panel-body"><details id="link-editor"><summary>Add a memory link</summary><div class="detail-form" style="margin-top:10px">' +
|
|
1163
|
+
'<form id="link-search" class="link-search"><label class="label" for="link-query">Find a memory in this project<input id="link-query" type="search" maxlength="500" placeholder="Search by keyword"></label>' +
|
|
1164
|
+
'<button type="submit" class="button">Find</button></form>' +
|
|
1165
|
+
'<form id="link-form" class="detail-form"><label class="label" for="link-target">Target memory<select id="link-target" required><option value="">Choose a memory</option></select></label>' +
|
|
1166
|
+
'<p id="link-search-status" class="small muted" role="status"></p>' +
|
|
1167
|
+
'<label class="label" for="link-type">Relationship from this memory<select id="link-type">' + relationOptions + '</select></label>' +
|
|
1168
|
+
'<label class="label" for="link-note">Note<textarea id="link-note" maxlength="1000" rows="2" placeholder="Describe the relationship"></textarea></label>' +
|
|
1169
|
+
'<div class="form-actions"><button class="button primary" id="link-submit" type="submit">Save link</button></div></form></div></details></div>' +
|
|
1170
|
+
'<p id="link-edit-status" class="save-status small muted" role="status"></p></section>' +
|
|
1171
|
+
'</div><div class="stack"><section class="panel"><div class="panel-head"><h2>Recorded review</h2>' + reviewTag(effective) + '</div>' +
|
|
1172
|
+
'<div class="panel-body detail-form">' +
|
|
1173
|
+
(effective === 'superseded' ? '<p class="small muted">This memory is superseded' +
|
|
1174
|
+
(replacement?.fromId && !replacement.orphaned ? ' by ' + memoryLink(replacement.fromId, project, replacement.fromTitle || replacement.fromId) : '') + '.</p>' : '') +
|
|
1175
|
+
(review?.updatedAt ? '<div class="small muted">Reviewed ' + dateMarkup(review.updatedAt, true) + '</div>' :
|
|
1176
|
+
'<p class="small muted">No review recorded.</p>') +
|
|
1177
|
+
'<form id="review-form" class="detail-form"><label class="label" for="review-state">Manual status<select id="review-state">' +
|
|
1178
|
+
reviewOptions + '</select></label><label class="label" for="review-note">Review note<textarea id="review-note" maxlength="1000" rows="3" placeholder="Record what you checked">' +
|
|
1179
|
+
esc(review?.note) + '</textarea></label><div class="form-actions"><button class="button primary" id="review-submit" type="submit">Save review</button></div></form>' +
|
|
1180
|
+
'</div><p id="detail-status" class="save-status small muted" role="status"></p></section>' +
|
|
1181
|
+
renderReviewHistory(snapshot.history) + '</div></div>';
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
async function loadDetail(id, project, failurePrefix = '') {
|
|
1185
|
+
const store = state.detail;
|
|
1186
|
+
const request = ++store.request;
|
|
1187
|
+
store.id = id;
|
|
1188
|
+
store.project = project;
|
|
1189
|
+
store.snapshot = null;
|
|
1190
|
+
$('detail-actions').hidden = true;
|
|
1191
|
+
$('title-memory').textContent = 'Memory detail';
|
|
1192
|
+
$('detail-meta').replaceChildren();
|
|
1193
|
+
const target = $('detail-body');
|
|
1194
|
+
setBusy(target, true);
|
|
1195
|
+
target.innerHTML = skeleton('Loading memory…');
|
|
1196
|
+
try {
|
|
1197
|
+
if (!id || !project) {
|
|
1198
|
+
target.innerHTML = '<div class="panel">' + blank('memory', 'Choose a memory', 'Open a record from your memories, context, or graph.',
|
|
1199
|
+
'<a href="#memories" class="button quiet">Browse memories</a>') + '</div>';
|
|
1200
|
+
return false;
|
|
1201
|
+
}
|
|
1202
|
+
const snapshot = await api(queryPath('memory', { id, project }));
|
|
1203
|
+
if (request !== store.request) return false;
|
|
1204
|
+
if (snapshot.memory?.id !== id || (snapshot.memory.project || snapshot.memory.projectId) !== project ||
|
|
1205
|
+
!Number.isSafeInteger(snapshot.revision)) throw new Error('The server returned an unreadable memory.');
|
|
1206
|
+
store.snapshot = snapshot;
|
|
1207
|
+
renderDetail(snapshot);
|
|
1208
|
+
return true;
|
|
1209
|
+
} catch (error) {
|
|
1210
|
+
if (request !== store.request) return false;
|
|
1211
|
+
target.innerHTML = errorMarkup(failurePrefix + error.message, error.status === 404 ? null : 'detail');
|
|
1212
|
+
return false;
|
|
1213
|
+
} finally {
|
|
1214
|
+
if (request === store.request) setBusy(target, false);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
async function searchLinkTargets() {
|
|
1219
|
+
const store = state.detail;
|
|
1220
|
+
if (!store.snapshot || store.saving) return;
|
|
1221
|
+
const request = ++store.targetRequest;
|
|
1222
|
+
const detailRequest = store.request;
|
|
1223
|
+
const target = $('link-search-status');
|
|
1224
|
+
target.textContent = 'Looking for memories…';
|
|
1225
|
+
try {
|
|
1226
|
+
const q = $('link-query').value.trim();
|
|
1227
|
+
const result = await api(queryPath('memories', { q, project: store.project, limit: 15 }));
|
|
1228
|
+
if (request !== store.targetRequest || detailRequest !== store.request) return;
|
|
1229
|
+
const choices = list(result.items).filter((item) => item.id !== store.id && (item.project || item.projectId) === store.project)
|
|
1230
|
+
.map((item) => ({ id: item.id, name: item.title || item.id }));
|
|
1231
|
+
optionSet('link-target', 'Choose a memory', choices, '');
|
|
1232
|
+
target.textContent = choices.length ? countLabel(choices.length, 'memory', 'memories') + ' found.' : 'No other memories matched.';
|
|
1233
|
+
} catch (error) {
|
|
1234
|
+
if (request === store.targetRequest && detailRequest === store.request) target.textContent = error.message;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
function detailStatus(id, message, error = false) {
|
|
1239
|
+
const status = $(id);
|
|
1240
|
+
if (!status) return;
|
|
1241
|
+
status.textContent = message;
|
|
1242
|
+
status.dataset.error = String(error);
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
async function saveDetail(kind, payload, focus) {
|
|
1246
|
+
const store = state.detail;
|
|
1247
|
+
if (!store.snapshot || store.saving) return;
|
|
1248
|
+
const { id, project, request } = store;
|
|
1249
|
+
const status = kind === 'memory-review' ? 'detail-status' : 'link-edit-status';
|
|
1250
|
+
const controls = () => $('detail-body').querySelectorAll('button, select, textarea');
|
|
1251
|
+
store.saving = true;
|
|
1252
|
+
controls().forEach((element) => { element.disabled = true; });
|
|
1253
|
+
detailStatus(status, 'Saving…');
|
|
1254
|
+
try {
|
|
1255
|
+
await api(kind, { body: { projectId: project, expectedRevision: store.snapshot.revision, ...payload } });
|
|
1256
|
+
state.graph.snapshot = null;
|
|
1257
|
+
state.overviewLoaded = false;
|
|
1258
|
+
if (state.context.pack || state.context.pending) {
|
|
1259
|
+
state.context.dirty = true;
|
|
1260
|
+
if (!state.context.pending) renderContext();
|
|
1261
|
+
}
|
|
1262
|
+
if (request !== store.request || state.view !== 'memory') return;
|
|
1263
|
+
if (await loadDetail(id, project, 'Saved. Could not reload the memory. ')) detailStatus(status, 'Saved.');
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
if (request !== store.request || state.view !== 'memory') return;
|
|
1266
|
+
if (error.status === 409) {
|
|
1267
|
+
if (await loadDetail(id, project, 'This memory changed elsewhere. ')) detailStatus(status, 'This memory changed elsewhere. Review the latest state and try again.', true);
|
|
1268
|
+
} else {
|
|
1269
|
+
detailStatus(status, error.message, true);
|
|
1270
|
+
}
|
|
1271
|
+
} finally {
|
|
1272
|
+
store.saving = false;
|
|
1273
|
+
if (state.view === 'memory' && id === store.id && project === store.project) {
|
|
1274
|
+
controls().forEach((element) => { element.disabled = false; });
|
|
1275
|
+
if (focus === 'link-editor' && $(focus)) $(focus).querySelector('summary')?.focus();
|
|
1276
|
+
else ($(focus) || $('title-memory')).focus();
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
|
|
774
1281
|
const graphKinds = { project: 'Project', agent: 'Agent', memory: 'Memory' };
|
|
775
|
-
const graphEdgeLabels = { 'belongs-to': 'Belongs to project', 'recorded-by': 'Recorded by agent' };
|
|
1282
|
+
const graphEdgeLabels = { 'belongs-to': 'Belongs to project', 'recorded-by': 'Recorded by agent', ...linkTypes };
|
|
776
1283
|
const graphEdgeClass = { 'belongs-to': ' project-edge', 'recorded-by': ' agent-edge' };
|
|
1284
|
+
const recordedEdge = (edge) => edge?.id && Object.hasOwn(linkTypes, edge.kind);
|
|
1285
|
+
const graphMemoryId = (node) => node.memoryId || (node.kind === 'memory' && node.id.startsWith('memory:') ? node.id.slice(7) : '');
|
|
777
1286
|
|
|
778
1287
|
function graphNeighbors(edges, id) {
|
|
779
1288
|
return edges.filter((edge) => edge.source === id || edge.target === id).map((edge) => ({
|
|
@@ -893,6 +1402,15 @@ function graphEdgePath(edge, layout) {
|
|
|
893
1402
|
const by = b.y + b.height / 2;
|
|
894
1403
|
const start = 'M' + ax + ' ' + ay;
|
|
895
1404
|
if (edge.kind === 'belongs-to') return start + ' L' + bx + ' ' + by;
|
|
1405
|
+
if (recordedEdge(edge)) {
|
|
1406
|
+
const dx = bx - ax;
|
|
1407
|
+
const dy = by - ay;
|
|
1408
|
+
const length = Math.hypot(dx, dy) || 1;
|
|
1409
|
+
const pad = Math.min(b.width / 2 / Math.abs(dx), b.height / 2 / Math.abs(dy), .38);
|
|
1410
|
+
const lift = Math.min(63, Math.max(20, length * .25));
|
|
1411
|
+
return start + ' Q' + ((ax + bx) / 2 - dy / length * lift) + ' ' + ((ay + by) / 2 + dx / length * lift) +
|
|
1412
|
+
' ' + (bx - dx * pad) + ' ' + (by - dy * pad);
|
|
1413
|
+
}
|
|
896
1414
|
const bend = (ay + by) / 2;
|
|
897
1415
|
return start + ' C' + ax + ' ' + bend + ' ' + bx + ' ' + bend + ' ' + bx + ' ' + by;
|
|
898
1416
|
}
|
|
@@ -915,7 +1433,13 @@ function graphNodeMarkup(node, position, tabIndex) {
|
|
|
915
1433
|
}
|
|
916
1434
|
|
|
917
1435
|
function renderGraphList() {
|
|
918
|
-
|
|
1436
|
+
const graph = state.graph;
|
|
1437
|
+
const byId = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
1438
|
+
const links = graph.edges.filter(recordedEdge).map((edge) => '<li class="graph-row"><button type="button" class="button" data-edge-record="' +
|
|
1439
|
+
esc(edge.id) + '" aria-pressed="false"><span>' + esc(byId.get(edge.source)?.label || edge.source) + ' · ' +
|
|
1440
|
+
esc(graphEdgeLabels[edge.kind]) + ' · ' + esc(byId.get(edge.target)?.label || edge.target) + '</span></button></li>').join('');
|
|
1441
|
+
$('graph-list').innerHTML = (links ? '<details open class="panel-body"><summary>Recorded memory links</summary><ul class="list">' + links + '</ul></details>' : '') +
|
|
1442
|
+
'<ul class="list">' + graph.nodes.map((node) => '<li class="graph-row"><button type="button" class="button" data-node="' +
|
|
919
1443
|
esc(node.id) + '" aria-pressed="false">' + icon(node.kind, true) + '<span>' + esc(node.label || node.id) +
|
|
920
1444
|
'</span><span class="tag ' + node.kind + '">' + graphKinds[node.kind] + '</span></button></li>').join('') + '</ul>';
|
|
921
1445
|
}
|
|
@@ -926,12 +1450,27 @@ function renderGraphMap() {
|
|
|
926
1450
|
const svg = $('graph-svg');
|
|
927
1451
|
const clusters = layout.clusters.map(({ cx, cy, rx, ry }) => '<ellipse class="cluster-area" cx="' + cx + '" cy="' + cy +
|
|
928
1452
|
'" rx="' + rx + '" ry="' + ry + '"/>').join('');
|
|
929
|
-
const
|
|
930
|
-
|
|
1453
|
+
const byId = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
1454
|
+
const structural = [];
|
|
1455
|
+
const recordedLinks = [];
|
|
1456
|
+
graph.edges.forEach((edge, i) => {
|
|
1457
|
+
const path = graphEdgePath(edge, layout);
|
|
1458
|
+
const recorded = recordedEdge(edge);
|
|
1459
|
+
const visual = '<path class="graph-edge' + (recorded ? ' recorded-edge ' + edge.kind : graphEdgeClass[edge.kind] || '') +
|
|
1460
|
+
'" data-edge="' + i + '" d="' + path + '"' + (recorded && edge.kind !== 'related' ? ' marker-end="url(#graph-arrow)"' : '') + '/>';
|
|
1461
|
+
if (recorded) {
|
|
1462
|
+
const from = byId.get(edge.source)?.label || edge.source;
|
|
1463
|
+
const to = byId.get(edge.target)?.label || edge.target;
|
|
1464
|
+
const label = from + ' · ' + graphEdgeLabels[edge.kind] + ' · ' + to;
|
|
1465
|
+
recordedLinks.push('<g class="graph-link-group" data-edge-record="' + esc(edge.id) + '" role="button" tabindex="-1" aria-pressed="false" aria-label="' +
|
|
1466
|
+
esc(label) + '"><title>' + esc(label) + '</title>' + visual + '<path class="graph-edge-hit" d="' + path + '"/></g>');
|
|
1467
|
+
} else structural.push(visual);
|
|
1468
|
+
});
|
|
931
1469
|
const nodes = graph.nodes.map((node, i) => graphNodeMarkup(node, layout.positions.get(node.id), i === 0 ? 0 : -1)).join('');
|
|
932
|
-
svg.innerHTML = '<defs><pattern id="map-dots" width="18" height="18" patternUnits="userSpaceOnUse"><circle cx="2" cy="2" r="1" fill="var(--graph-dot)"/></pattern
|
|
933
|
-
'<
|
|
934
|
-
|
|
1470
|
+
svg.innerHTML = '<defs><pattern id="map-dots" width="18" height="18" patternUnits="userSpaceOnUse"><circle cx="2" cy="2" r="1" fill="var(--graph-dot)"/></pattern>' +
|
|
1471
|
+
'<marker id="graph-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path class="graph-arrow" d="M0 1 9 5 0 9z"/></marker></defs>' +
|
|
1472
|
+
'<rect width="100%" height="100%" fill="url(#map-dots)"/><g id="graph-transform"><g aria-hidden="true">' + clusters + structural.join('') + '</g>' +
|
|
1473
|
+
recordedLinks.join('') + nodes + '<g class="graph-tooltip" id="graph-tooltip" aria-hidden="true"></g></g>';
|
|
935
1474
|
fitGraph();
|
|
936
1475
|
}
|
|
937
1476
|
|
|
@@ -985,6 +1524,10 @@ function centerGraphNode(id) {
|
|
|
985
1524
|
const position = graph.layout?.positions.get(id);
|
|
986
1525
|
if (!position || graph.mode !== 'map') return;
|
|
987
1526
|
const { width, height } = graphViewport();
|
|
1527
|
+
const left = graph.panX + position.x * graph.zoom;
|
|
1528
|
+
const top = graph.panY + position.y * graph.zoom;
|
|
1529
|
+
if (graph.zoom >= .8 && left >= 14 && top >= 14 && left + position.width * graph.zoom <= width - 14 &&
|
|
1530
|
+
top + position.height * graph.zoom <= height - 52) return;
|
|
988
1531
|
const zoom = Math.max(.8, graph.zoom);
|
|
989
1532
|
graph.zoom = zoom;
|
|
990
1533
|
graph.panX = width / 2 - (position.x + position.width / 2) * zoom;
|
|
@@ -1015,15 +1558,28 @@ function renderGraphInspector() {
|
|
|
1015
1558
|
const all = new Map(list(graph.snapshot?.nodes).map((node) => [node.id, node]));
|
|
1016
1559
|
const selected = all.get(graph.selected);
|
|
1017
1560
|
const inspector = $('graph-inspector');
|
|
1561
|
+
const selectedEdge = graph.edges.find((edge) => recordedEdge(edge) && edge.id === graph.selectedEdge);
|
|
1562
|
+
if (selectedEdge) {
|
|
1563
|
+
const from = all.get(selectedEdge.source);
|
|
1564
|
+
const to = all.get(selectedEdge.target);
|
|
1565
|
+
const record = (node) => node ? memoryLink(graphMemoryId(node), node.project, node.label || node.id) : '';
|
|
1566
|
+
const endpoint = (label, node) => (selectedEdge.kind === 'related' ? '' : '<p class="small muted">' + label + '</p>') + '<p>' + record(node) + '</p>';
|
|
1567
|
+
inspector.innerHTML = '<div class="panel-head"><h3>Link details</h3></div><div class="panel-body">' +
|
|
1568
|
+
'<div>' + endpoint('From memory', from) + '</div>' + tags([['memory', graphEdgeLabels[selectedEdge.kind]], ['agent', 'Recorded memory link']]) +
|
|
1569
|
+
'<div>' + endpoint('To memory', to) + '</div>' + (selectedEdge.note ? '<p class="small muted detail-note">' + esc(selectedEdge.note) + '</p>' : '') + '</div>';
|
|
1570
|
+
return;
|
|
1571
|
+
}
|
|
1018
1572
|
if (!selected) {
|
|
1019
1573
|
inspector.innerHTML = '<div class="panel-head"><h3>Node details</h3></div>' +
|
|
1020
1574
|
blank('graph', 'Select a node', 'See its stored details and follow its recorded connections.');
|
|
1021
1575
|
return;
|
|
1022
1576
|
}
|
|
1023
|
-
const adjacent = graphNeighbors(
|
|
1577
|
+
const adjacent = graphNeighbors(graph.edges, selected.id).filter((item) => all.has(item.id));
|
|
1024
1578
|
const connected = adjacent.length ? '<details open><summary>' + countLabel(adjacent.length, 'connection') + '</summary><div class="related">' +
|
|
1025
1579
|
adjacent.map(({ id, edge }) => {
|
|
1026
1580
|
const node = all.get(id);
|
|
1581
|
+
if (recordedEdge(edge)) return '<button type="button" class="button quiet" data-edge-record="' + esc(edge.id) +
|
|
1582
|
+
'"><span>' + esc(linkCaption({ fromId: edge.source, type: edge.kind }, selected.id)) + ' · ' + esc(node.label || node.id) + '</span></button>';
|
|
1027
1583
|
return '<button type="button" class="button quiet" data-related="' + esc(id) + '" title="' +
|
|
1028
1584
|
esc(graphEdgeLabels[edge.kind] || nice(edge.kind) || 'Connection') + '">' + icon(node.kind, true) + '<span>' +
|
|
1029
1585
|
esc(node.label || node.id) + '</span></button>';
|
|
@@ -1031,37 +1587,59 @@ function renderGraphInspector() {
|
|
|
1031
1587
|
const project = state.projects.find((item) => item.id === selected.project)?.name || selected.project;
|
|
1032
1588
|
const snippet = compact(selected.snippet) && compact(selected.snippet) !== compact(selected.label)
|
|
1033
1589
|
? '<p class="small">' + esc(selected.snippet) + '</p>' : '';
|
|
1590
|
+
const canFocus = selected.kind === 'memory' && adjacent.some(({ edge }) => recordedEdge(edge)) && graph.lens !== selected.id;
|
|
1034
1591
|
inspector.innerHTML = '<div class="panel-head"><h3>Node details</h3></div><div class="panel-body">' +
|
|
1035
|
-
tags([[selected.kind, graphKinds[selected.kind]], ['memory', nice(selected.category)]
|
|
1592
|
+
tags([[selected.kind, graphKinds[selected.kind]], ['memory', nice(selected.category)], ['state-' + (selected.effectiveState || ''), reviewLabel(selected.effectiveState)]]) +
|
|
1593
|
+
'<h3>' + (selected.kind === 'memory' ? memoryLink(graphMemoryId(selected), selected.project, selected.label || selected.id) : esc(selected.label || selected.id)) + '</h3>' +
|
|
1036
1594
|
(project && selected.kind !== 'project' ? '<p class="muted small">' + esc(project) + '</p>' : '') + snippet +
|
|
1037
|
-
(selected.kind === 'memory' ? sourceMarkup([...list(selected.sources), selected.source]) : '') +
|
|
1595
|
+
(selected.kind === 'memory' ? sourceMarkup([...list(selected.sources), selected.source]) : '') +
|
|
1596
|
+
(canFocus ? '<button class="button" type="button" data-graph-lens="' + esc(selected.id) + '">' + icon('graph', true) + 'Focus recorded links</button>' : '') +
|
|
1597
|
+
connected + '</div>';
|
|
1038
1598
|
}
|
|
1039
1599
|
|
|
1040
|
-
function
|
|
1600
|
+
function updateGraphSelection() {
|
|
1041
1601
|
const graph = state.graph;
|
|
1042
|
-
graph.selected
|
|
1602
|
+
const id = graph.selected;
|
|
1603
|
+
const selectedEdge = graph.edges.find((edge) => recordedEdge(edge) && edge.id === graph.selectedEdge);
|
|
1043
1604
|
const connected = new Set(graphNeighbors(graph.edges, id).map((item) => item.id));
|
|
1044
1605
|
connected.add(id);
|
|
1045
|
-
|
|
1046
|
-
const
|
|
1606
|
+
if (selectedEdge) { connected.add(selectedEdge.source); connected.add(selectedEdge.target); }
|
|
1607
|
+
const hasSelection = !!id || !!selectedEdge;
|
|
1608
|
+
const tabStop = id || selectedEdge?.source || graph.nodes[0]?.id;
|
|
1047
1609
|
$('graph-body').querySelectorAll('[data-node]').forEach((element) => {
|
|
1048
1610
|
const nodeId = element.dataset.node;
|
|
1049
1611
|
element.setAttribute('aria-pressed', String(id === nodeId));
|
|
1050
1612
|
if (element instanceof SVGElement) {
|
|
1051
|
-
element.classList.toggle('unrelated',
|
|
1613
|
+
element.classList.toggle('unrelated', hasSelection && !connected.has(nodeId));
|
|
1614
|
+
element.classList.toggle('selected-memory', id === nodeId && nodeId.startsWith('memory:'));
|
|
1052
1615
|
element.setAttribute('tabindex', nodeId === tabStop ? '0' : '-1');
|
|
1053
1616
|
}
|
|
1054
1617
|
});
|
|
1055
1618
|
if (graph.mode === 'map') {
|
|
1056
1619
|
$('graph-svg').querySelectorAll('[data-edge]').forEach((element) => {
|
|
1057
1620
|
const edge = graph.edges[Number(element.dataset.edge)];
|
|
1058
|
-
const active = edge.source === id || edge.target === id;
|
|
1621
|
+
const active = edge === selectedEdge || edge.source === id || edge.target === id;
|
|
1059
1622
|
element.classList.toggle('active', active);
|
|
1060
|
-
element.classList.toggle('unrelated',
|
|
1623
|
+
element.classList.toggle('unrelated', hasSelection && !active);
|
|
1624
|
+
});
|
|
1625
|
+
const linkTabStop = graph.selectedEdge || graph.edges.find(recordedEdge)?.id;
|
|
1626
|
+
$('graph-svg').querySelectorAll('[data-edge-record]').forEach((element) => {
|
|
1627
|
+
element.setAttribute('aria-pressed', String(element.dataset.edgeRecord === graph.selectedEdge));
|
|
1628
|
+
element.setAttribute('tabindex', element.dataset.edgeRecord === linkTabStop ? '0' : '-1');
|
|
1061
1629
|
});
|
|
1062
1630
|
renderGraphTooltip(id);
|
|
1063
1631
|
}
|
|
1632
|
+
$('graph-list').querySelectorAll('[data-edge-record]').forEach((element) => {
|
|
1633
|
+
element.setAttribute('aria-pressed', String(element.dataset.edgeRecord === graph.selectedEdge));
|
|
1634
|
+
});
|
|
1064
1635
|
renderGraphInspector();
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
function selectGraphNode(id, { center = false, focus = false } = {}) {
|
|
1639
|
+
const graph = state.graph;
|
|
1640
|
+
graph.selected = id;
|
|
1641
|
+
graph.selectedEdge = '';
|
|
1642
|
+
updateGraphSelection();
|
|
1065
1643
|
if (center) centerGraphNode(id);
|
|
1066
1644
|
if (focus) {
|
|
1067
1645
|
const target = graph.mode === 'map' ? $('graph-svg') : $('graph-list');
|
|
@@ -1069,29 +1647,56 @@ function selectGraphNode(id, { center = false, focus = false } = {}) {
|
|
|
1069
1647
|
}
|
|
1070
1648
|
}
|
|
1071
1649
|
|
|
1650
|
+
function selectGraphEdge(id, fromInspector) {
|
|
1651
|
+
const graph = state.graph;
|
|
1652
|
+
if (!graph.edges.some((edge) => recordedEdge(edge) && edge.id === id)) return;
|
|
1653
|
+
graph.selected = '';
|
|
1654
|
+
graph.selectedEdge = id;
|
|
1655
|
+
updateGraphSelection();
|
|
1656
|
+
if (fromInspector) $('graph-inspector').querySelector('a')?.focus();
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1072
1659
|
function renderGraph() {
|
|
1073
1660
|
const graph = state.graph;
|
|
1074
1661
|
if (!graph.snapshot) return;
|
|
1075
1662
|
const search = $('graph-query').value.trim().toLowerCase();
|
|
1076
1663
|
const kind = $('graph-kind').value;
|
|
1077
|
-
const
|
|
1664
|
+
const relation = $('graph-relation').value;
|
|
1665
|
+
const allNodes = list(graph.snapshot.nodes).filter((node) => Object.hasOwn(graphKinds, node.kind));
|
|
1666
|
+
const allEdges = list(graph.snapshot.edges);
|
|
1667
|
+
const anchor = allNodes.find((node) => node.id === graph.lens);
|
|
1668
|
+
const edges = anchor ? allEdges.filter((edge) => recordedEdge(edge) && (edge.source === anchor.id || edge.target === anchor.id)) : allEdges;
|
|
1669
|
+
const lensIds = anchor ? new Set([anchor.id, ...edges.flatMap((edge) => [edge.source, edge.target])]) : null;
|
|
1670
|
+
const nodes = lensIds ? allNodes.filter((node) => lensIds.has(node.id)) : allNodes;
|
|
1671
|
+
$('graph-lens').hidden = !anchor;
|
|
1672
|
+
$('graph-body').dataset.lens = String(!!anchor);
|
|
1673
|
+
$('graph-lens-label').textContent = anchor ? 'Recorded links for ' + shorten(compact(anchor.label || anchor.id), 94) : '';
|
|
1078
1674
|
const matches = nodes.filter((node) => (!kind || node.kind === kind) &&
|
|
1079
1675
|
(!search || [node.label, node.snippet, node.project, node.category].filter(Boolean).join(' ').toLowerCase().includes(search)));
|
|
1080
1676
|
const ids = new Set(matches.map((node) => node.id));
|
|
1081
1677
|
if (search && !kind) {
|
|
1082
|
-
matches.forEach((node) => graphNeighbors(
|
|
1678
|
+
matches.forEach((node) => graphNeighbors(edges, node.id).forEach((item) => ids.add(item.id)));
|
|
1679
|
+
}
|
|
1680
|
+
const matchingEdges = edges.filter((edge) => ids.has(edge.source) && ids.has(edge.target) &&
|
|
1681
|
+
(!relation || (relation === 'recorded' ? recordedEdge(edge) : edge.kind === relation)));
|
|
1682
|
+
if (relation) {
|
|
1683
|
+
const endpoints = new Set(matchingEdges.flatMap((edge) => [edge.source, edge.target]));
|
|
1684
|
+
for (const id of ids) if (!endpoints.has(id)) ids.delete(id);
|
|
1083
1685
|
}
|
|
1084
1686
|
graph.nodes = nodes.filter((node) => ids.has(node.id));
|
|
1085
|
-
graph.edges =
|
|
1687
|
+
graph.edges = matchingEdges;
|
|
1086
1688
|
if (graph.selected && !ids.has(graph.selected)) graph.selected = '';
|
|
1689
|
+
if (graph.selectedEdge && !graph.edges.some((edge) => recordedEdge(edge) && edge.id === graph.selectedEdge)) graph.selectedEdge = '';
|
|
1087
1690
|
const matchingCount = search && matches.length !== graph.nodes.length ? countLabel(matches.length, 'match', 'matches') + ' · ' : '';
|
|
1088
1691
|
$('graph-count').textContent = matchingCount + countLabel(graph.nodes.length, 'node') + ' · ' + countLabel(graph.edges.length, 'connection');
|
|
1089
1692
|
$('graph-body').hidden = !graph.nodes.length;
|
|
1090
1693
|
$('graph-legend').hidden = !graph.nodes.length;
|
|
1091
|
-
const note = graph.snapshot.truncated ? '<div class="panel-body small muted">
|
|
1092
|
-
number(graph.snapshot.shownMemories) + ' memories
|
|
1093
|
-
|
|
1094
|
-
|
|
1694
|
+
const note = graph.snapshot.truncated ? '<div class="panel-body small muted">This snapshot includes ' +
|
|
1695
|
+
number(graph.snapshot.shownMemories) + ' memories. Filter by project to explore a narrower set.</div>' : '';
|
|
1696
|
+
const focused = graph.focusMemory && graph.nodes.find((node) => graphMemoryId(node) === graph.focusMemory);
|
|
1697
|
+
const missing = graph.focusMemory && !focused ? '<p class="panel-body small muted">This memory is outside the current graph view.</p>' : '';
|
|
1698
|
+
$('graph-status').innerHTML = graph.nodes.length ? note + missing : missing + blank('graph', search || kind || relation ? 'No matching nodes' : 'No relationships yet',
|
|
1699
|
+
search || kind || relation ? 'Change the name, node type, or relationship filter.' : 'The graph fills in as project and agent memories are stored.');
|
|
1095
1700
|
if (!graph.nodes.length) return;
|
|
1096
1701
|
$('graph-stage').hidden = graph.mode !== 'map';
|
|
1097
1702
|
$('graph-list').hidden = graph.mode !== 'list';
|
|
@@ -1099,12 +1704,16 @@ function renderGraph() {
|
|
|
1099
1704
|
$('graph-mode-list').setAttribute('aria-pressed', String(graph.mode === 'list'));
|
|
1100
1705
|
renderGraphList();
|
|
1101
1706
|
if (graph.mode === 'map') renderGraphMap();
|
|
1102
|
-
|
|
1707
|
+
if (focused) {
|
|
1708
|
+
graph.focusMemory = '';
|
|
1709
|
+
selectGraphNode(focused.id);
|
|
1710
|
+
} else updateGraphSelection();
|
|
1103
1711
|
}
|
|
1104
1712
|
|
|
1105
|
-
async function loadGraph(project, force = false) {
|
|
1713
|
+
async function loadGraph(project, force = false, memory = '') {
|
|
1106
1714
|
const graph = state.graph;
|
|
1107
|
-
|
|
1715
|
+
graph.focusMemory = memory;
|
|
1716
|
+
if (!force && !memory && graph.snapshot && graph.project === project) {
|
|
1108
1717
|
renderGraph();
|
|
1109
1718
|
return;
|
|
1110
1719
|
}
|
|
@@ -1113,15 +1722,18 @@ async function loadGraph(project, force = false) {
|
|
|
1113
1722
|
const controller = new AbortController();
|
|
1114
1723
|
graph.controller = controller;
|
|
1115
1724
|
graph.snapshot = null;
|
|
1725
|
+
if (graph.project !== project || memory) graph.lens = '';
|
|
1116
1726
|
graph.project = project;
|
|
1117
1727
|
graph.selected = '';
|
|
1728
|
+
graph.selectedEdge = '';
|
|
1118
1729
|
setBusy($('graph-panel'), true);
|
|
1119
1730
|
$('graph-status').innerHTML = skeleton('Loading relationships…');
|
|
1120
1731
|
$('graph-count').textContent = '';
|
|
1121
1732
|
$('graph-body').hidden = true;
|
|
1733
|
+
$('graph-lens').hidden = true;
|
|
1122
1734
|
$('graph-legend').hidden = true;
|
|
1123
1735
|
try {
|
|
1124
|
-
const snapshot = await api(queryPath('graph', { project }), { signal: AbortSignal.any([controller.signal, AbortSignal.timeout(12000)]) });
|
|
1736
|
+
const snapshot = await api(queryPath('graph', { project, memory }), { signal: AbortSignal.any([controller.signal, AbortSignal.timeout(12000)]) });
|
|
1125
1737
|
if (seq !== graph.request) return;
|
|
1126
1738
|
graph.snapshot = snapshot;
|
|
1127
1739
|
renderGraph();
|
|
@@ -1135,10 +1747,11 @@ async function loadGraph(project, force = false) {
|
|
|
1135
1747
|
function renderContextControls() {
|
|
1136
1748
|
const input = $('context-cross');
|
|
1137
1749
|
const toggle = state.features?.features;
|
|
1138
|
-
const canCross = toggle?.contextAssembly === true && toggle?.crossProjectContext === true;
|
|
1750
|
+
const canCross = document.documentElement.dataset.projectLocal !== 'true' && toggle?.contextAssembly === true && toggle?.crossProjectContext === true;
|
|
1139
1751
|
$('context-cross-row').hidden = !canCross;
|
|
1140
1752
|
if (!canCross) input.checked = false;
|
|
1141
1753
|
$('context-submit').disabled = !toggle?.contextAssembly || !state.projects.length || state.context.pending;
|
|
1754
|
+
$('context-related-row').hidden = $('context-profile').value !== 'adaptive';
|
|
1142
1755
|
const gate = $('context-gate');
|
|
1143
1756
|
if (!toggle && state.featurePending) {
|
|
1144
1757
|
gate.innerHTML = '<p class="muted small" role="status">Checking feature settings…</p>';
|
|
@@ -1161,11 +1774,99 @@ function contextReasonList(items, format) {
|
|
|
1161
1774
|
return '<ul class="list">' + items.map(format).join('') + '</ul>';
|
|
1162
1775
|
}
|
|
1163
1776
|
|
|
1777
|
+
const contextReasons = {
|
|
1778
|
+
selected: 'Included in the brief.', duplicate: 'Matches a memory already considered.', budget: 'Would exceed the selected budget.',
|
|
1779
|
+
limit: 'Beyond the brief’s memory limit.', 'candidate-limit': 'Beyond the evaluated candidate limit.', empty: 'Contains no memory text.',
|
|
1780
|
+
lookback: 'Falls outside the selected time range.', archived: 'Archived in a recorded review.', contested: 'Marked as needing review.',
|
|
1781
|
+
superseded: 'Superseded by a recorded replacement.'
|
|
1782
|
+
};
|
|
1783
|
+
const rankScore = (value) => Number.isFinite(value) ? value.toLocaleString(undefined, { maximumFractionDigits: 3 }) : '—';
|
|
1784
|
+
|
|
1785
|
+
function renderRetrieval(pack) {
|
|
1786
|
+
const retrieval = pack.retrieval;
|
|
1787
|
+
if (!retrieval || !pack.enabled) return '';
|
|
1788
|
+
const counts = list(retrieval.stages);
|
|
1789
|
+
const max = Math.max(1, ...counts.map((stage) => Number(stage.count) || 0));
|
|
1790
|
+
const stages = counts.map((stage) => '<li><strong>' + number(stage.count) + '</strong><span>' + esc(stage.label) +
|
|
1791
|
+
'</span><div class="retrieval-stage-bar" aria-hidden="true"><i style="width:' + Math.max(0, (Number(stage.count) || 0) / max * 100) + '%"></i></div></li>').join('');
|
|
1792
|
+
const candidates = list(retrieval.candidates);
|
|
1793
|
+
const labels = new Map([...list(pack.entries), ...candidates].map((item) => [item.id, item.title || shorten(compact(item.excerpt), 95) || item.id]));
|
|
1794
|
+
const rows = candidates.map((candidate) => {
|
|
1795
|
+
const signals = list(candidate.signals).map((signal) => {
|
|
1796
|
+
const sign = signal.label === 'Project task feedback' && signal.value > 0 ? '+' : '';
|
|
1797
|
+
return '<div><dt>' + esc(signal.label) + '</dt><dd>' + sign + esc(rankScore(signal.value)) + '</dd></div>';
|
|
1798
|
+
}).join('');
|
|
1799
|
+
const terms = list(candidate.matchedTerms);
|
|
1800
|
+
const fields = list(candidate.matchedFields);
|
|
1801
|
+
const paths = list(candidate.paths).map((path) => '<li><span class="tag agent">' + esc(linkTypes[path.relationType] || nice(path.relationType)) +
|
|
1802
|
+
'</span> ' + memoryLink(path.fromId, candidate.projectId, labels.get(path.fromId) || path.fromId) + ' ' + icon('chevron', true) + ' ' +
|
|
1803
|
+
memoryLink(path.toId, candidate.projectId, labels.get(path.toId) || path.toId) + '</li>').join('');
|
|
1804
|
+
const feedback = candidate.feedback;
|
|
1805
|
+
const reporters = list(feedback?.reporterKinds).map((kind) => kind === 'agent' ? 'Agent' : 'User');
|
|
1806
|
+
const feedbackSource = feedback ? '<p class="small muted">' + countLabel(list(feedback.recordIds).length, 'matching task report') +
|
|
1807
|
+
(reporters.length ? ' · ' + esc(reporters.join(' and ')) : '') +
|
|
1808
|
+
(state.projectKit?.projectId === candidate.projectId ? ' · <a href="' + esc(routeLink('project-kit', { memory: candidate.id })) + '">Review project outcomes</a>' : '') + '</p>' : '';
|
|
1809
|
+
const included = candidate.decision === 'selected';
|
|
1810
|
+
return '<li><details class="candidate"><summary><span class="candidate-title"><span class="rank">#' + number(candidate.rank) + '</span>' +
|
|
1811
|
+
'<span>' + esc(candidate.title || candidate.id) + '</span><span class="tag check-' + (included ? 'ok' : 'warn') + '">' +
|
|
1812
|
+
(included ? 'Included' : 'Excluded') + '</span></span></summary><div class="candidate-body"><p class="small muted">' +
|
|
1813
|
+
esc(contextReasons[candidate.reason] || nice(candidate.reason)) + ' Rank score ' + esc(rankScore(candidate.score)) + '.</p>' +
|
|
1814
|
+
(terms.length ? '<p class="small"><strong>Matched terms</strong> ' + esc(terms.join(', ')) + '</p>' : '') +
|
|
1815
|
+
(fields.length ? '<p class="small"><strong>Matched fields</strong> ' + esc(fields.map(nice).join(', ')) + '</p>' : '') +
|
|
1816
|
+
(signals ? '<dl class="candidate-signals" aria-label="Ranking contributions">' + signals + '</dl>' : '') + feedbackSource + sourceMarkup(candidate.sources) +
|
|
1817
|
+
(paths ? '<div><h3>Recorded link paths</h3><ul class="list">' + paths + '</ul></div>' : '') +
|
|
1818
|
+
'<p>' + memoryLink(candidate.id, candidate.projectId, 'Open memory') + '</p></div></details></li>';
|
|
1819
|
+
}).join('');
|
|
1820
|
+
const reportCount = retrieval.truncated ? ' Candidate diagnostics are bounded to ' + number(retrieval.candidateLimit) + ' records.' : '';
|
|
1821
|
+
return '<section class="context-section" aria-labelledby="retrieval-heading"><h3 id="retrieval-heading">Retrieval pipeline</h3>' +
|
|
1822
|
+
'<ol class="retrieval-flow" aria-label="Retrieval stages">' + stages + '</ol><p class="small muted">Scanned ' + number(retrieval.scanned) +
|
|
1823
|
+
' memories. Ranking contributions explain ordering.' + esc(reportCount) + '</p>' +
|
|
1824
|
+
(rows ? '<details class="context-section" open><summary>Candidate decisions · ' + number(candidates.length) + '</summary>' +
|
|
1825
|
+
'<ul class="list candidate-list" aria-label="Candidate decisions">' + rows + '</ul></details>' : '') + '</section>';
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
function renderComparison() {
|
|
1829
|
+
const target = $('context-comparison');
|
|
1830
|
+
if (!target) return;
|
|
1831
|
+
const { comparison, comparing, dirty } = state.context;
|
|
1832
|
+
if (dirty || !comparison && !comparing) {
|
|
1833
|
+
target.replaceChildren();
|
|
1834
|
+
return;
|
|
1835
|
+
}
|
|
1836
|
+
if (comparing) {
|
|
1837
|
+
target.innerHTML = skeleton('Comparing retrieval profiles…');
|
|
1838
|
+
return;
|
|
1839
|
+
}
|
|
1840
|
+
if (comparison.error) {
|
|
1841
|
+
target.innerHTML = errorMarkup(comparison.error);
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
const { legacy, adaptive, delta } = comparison;
|
|
1845
|
+
const shared = list(delta?.sharedIds);
|
|
1846
|
+
const column = (label, pack, ids) => {
|
|
1847
|
+
const unique = new Set(ids);
|
|
1848
|
+
const entries = list(pack.entries);
|
|
1849
|
+
const differences = entries.filter((entry) => unique.has(entry.id));
|
|
1850
|
+
return '<div><h3>' + label + '</h3><p class="small muted">' + countLabel(entries.length, 'included memory', 'included memories') + ' · ' +
|
|
1851
|
+
number(pack.budget?.estimatedTokens) + ' estimated tokens</p><p class="small"><strong>' + number(differences.length) + ' unique to ' + label + '</strong></p>' +
|
|
1852
|
+
(differences.length ? '<ul class="small">' + differences.map((entry) => '<li>' +
|
|
1853
|
+
memoryLink(entry.id, entry.projectId, shorten(compact(entry.excerpt), 95) || entry.id) + '</li>').join('') + '</ul>' : '') + '</div>';
|
|
1854
|
+
};
|
|
1855
|
+
target.innerHTML = '<h3>Profile comparison</h3><p class="small muted">Same task, scope, and requested budget · ' +
|
|
1856
|
+
countLabel(shared.length, 'shared memory', 'shared memories') + '</p><div class="compare-grid">' +
|
|
1857
|
+
column('Adaptive', adaptive, list(delta?.addedIds)) + column('Legacy', legacy, list(delta?.removedIds)) + '</div>' +
|
|
1858
|
+
list(comparison.warnings).map((warning) => '<p class="small muted">' + esc(warning) + '</p>').join('');
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1164
1861
|
function renderContext() {
|
|
1165
1862
|
const output = $('context-output');
|
|
1166
|
-
const { pack, dirty } = state.context;
|
|
1863
|
+
const { pack, dirty, params } = state.context;
|
|
1864
|
+
const status = $('context-result-state');
|
|
1167
1865
|
if (!pack) {
|
|
1168
|
-
|
|
1866
|
+
const enabled = state.features?.features?.contextAssembly !== false;
|
|
1867
|
+
status.textContent = enabled ? '' : 'Disabled';
|
|
1868
|
+
output.innerHTML = enabled ? blank('context', 'Ready for a task', 'Choose a project and describe the work to see the context and its sources here.') :
|
|
1869
|
+
blank('context', 'Context assembly is turned off', 'Enable it in Features to build a brief.');
|
|
1169
1870
|
return;
|
|
1170
1871
|
}
|
|
1171
1872
|
const entries = list(pack.entries);
|
|
@@ -1173,34 +1874,45 @@ function renderContext() {
|
|
|
1173
1874
|
const budget = pack.budget || {};
|
|
1174
1875
|
const warnings = list(pack.warnings);
|
|
1175
1876
|
const allowed = state.features?.features?.contextAssembly === true;
|
|
1176
|
-
const
|
|
1877
|
+
const canInspect = allowed && !dirty && pack.enabled;
|
|
1878
|
+
const canCopy = canInspect && !!pack.markdown && entries.length > 0;
|
|
1879
|
+
status.textContent = dirty ? 'Rebuild needed' : pack.enabled ? 'Assembled' : 'Disabled';
|
|
1177
1880
|
const warningMarkup = warnings.map((warning) => '<p class="small muted">' + esc(warning) + '</p>').join('');
|
|
1178
1881
|
const selection = contextReasonList(entries, (entry) => {
|
|
1179
1882
|
const title = shorten(compact(entry.excerpt), 120) || nice(entry.category) || entry.id;
|
|
1180
1883
|
const reason = list(entry.reasons).map((detail) => '<p class="small muted">' + esc(detail) + '</p>').join('');
|
|
1181
|
-
|
|
1182
|
-
'
|
|
1884
|
+
const links = list(entry.related).map((relation) => {
|
|
1885
|
+
const directional = { fromId: relation.direction === 'outgoing' ? entry.id : relation.otherMemoryId, type: relation.type };
|
|
1886
|
+
return '<p class="small">' + esc(linkCaption(directional, entry.id)) + ' ' +
|
|
1887
|
+
memoryLink(relation.otherMemoryId, entry.projectId, relation.title || relation.otherMemoryId) + '</p>';
|
|
1888
|
+
}).join('');
|
|
1889
|
+
return '<li><strong>' + memoryLink(entry.id, entry.projectId, title) + '</strong><div class="small muted">' +
|
|
1890
|
+
esc([entry.projectName, entry.agentTool, nice(entry.category)].filter(Boolean).join(' · ')) +
|
|
1891
|
+
'</div>' + reason + sourceMarkup(entry.sources) + links + '</li>';
|
|
1183
1892
|
});
|
|
1184
|
-
const
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
'
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1893
|
+
const rejections = contextReasonList(excluded, (entry) => '<li><strong class="mono">' +
|
|
1894
|
+
memoryLink(entry.id, entry.projectId || (pack.scope === 'project' ? pack.projectId : ''), entry.title || entry.id) +
|
|
1895
|
+
'</strong><p class="small muted">' + esc(contextReasons[entry.reason] || nice(entry.reason)) + '</p></li>');
|
|
1896
|
+
const projectName = state.projects.find((item) => item.id === pack.projectId)?.name || pack.projectId;
|
|
1897
|
+
const scope = pack.scope === 'cross-project' ? 'Cross-project · ' + projectName : projectName;
|
|
1898
|
+
const usage = budget.tokenBudget > 0 ? Math.min(100, Math.max(0, budget.estimatedTokens / budget.tokenBudget * 100)) : 0;
|
|
1899
|
+
output.innerHTML = '<div class="panel-body stack">' + (dirty ? '<p class="status small" role="status">This preview is out of date. Assemble again to refresh it.</p>' : '') +
|
|
1900
|
+
'<div class="context-output-head">' + tags([['project', scope], ['memory', params?.profile === 'adaptive' ? 'Adaptive' : 'Legacy']]) +
|
|
1901
|
+
'<div class="form-actions">' + (canInspect && params ? '<button type="button" class="button" id="context-compare-button"' +
|
|
1902
|
+
(state.context.comparing ? ' disabled' : '') + '>Compare profiles</button>' : '') +
|
|
1903
|
+
(canCopy ? '<button class="button" id="context-download" type="button">Download .md</button>' +
|
|
1904
|
+
'<button class="button primary" id="context-copy" type="button">' + icon('copy') + 'Copy brief</button>' : '') + '</div></div>' +
|
|
1905
|
+
'<p id="copy-status" class="small muted" role="status" hidden></p><dl class="context-metrics">' +
|
|
1906
|
+
'<div><dt>Estimated tokens</dt><dd>' + number(budget.estimatedTokens) + ' / ' + number(budget.tokenBudget) + '</dd><div class="context-budget-track" aria-hidden="true"><span style="width:' + usage + '%"></span></div></div>' +
|
|
1907
|
+
'<div><dt>Characters</dt><dd>' + number(budget.usedChars) + ' / ' + number(budget.maxChars) + '</dd></div>' +
|
|
1908
|
+
'<div><dt>Included / excluded</dt><dd>' + number(entries.length) + ' / ' + number(excluded.length) + '</dd></div></dl>' +
|
|
1909
|
+
warningMarkup + '<section class="context-compare" id="context-comparison" aria-live="polite"></section>' + renderRetrieval(pack) +
|
|
1200
1910
|
'<div class="context-reasons">' +
|
|
1201
|
-
(entries.length ? '<details
|
|
1202
|
-
(excluded.length ? '<details><summary>Excluded · ' + number(excluded.length) + '</summary>' + rejections + '</details>' : '') +
|
|
1203
|
-
'</div
|
|
1911
|
+
(entries.length ? '<details class="context-section"><summary>Included memories · ' + number(entries.length) + '</summary>' + selection + '</details>' : '') +
|
|
1912
|
+
(excluded.length ? '<details class="context-section"><summary>Excluded memories · ' + number(excluded.length) + '</summary>' + rejections + '</details>' : '') +
|
|
1913
|
+
'</div>' + (pack.markdown && pack.enabled && entries.length ? '<details class="context-section" open><summary>Assembled brief</summary><pre class="context-preview mono" id="context-text" tabindex="0" aria-label="Assembled context markdown">' + esc(pack.markdown) + '</pre></details>' :
|
|
1914
|
+
blank('context', pack.enabled ? 'No memories fit this task' : 'Context assembly is turned off', pack.enabled ? 'Review any exclusions or try a different task.' : 'Enable it in Features to build a brief.')) + '</div>';
|
|
1915
|
+
renderComparison();
|
|
1204
1916
|
}
|
|
1205
1917
|
|
|
1206
1918
|
async function assembleContext() {
|
|
@@ -1215,18 +1927,29 @@ async function assembleContext() {
|
|
|
1215
1927
|
$('context-task').reportValidity();
|
|
1216
1928
|
return;
|
|
1217
1929
|
}
|
|
1218
|
-
const
|
|
1930
|
+
const profile = $('context-profile').value;
|
|
1931
|
+
const body = { query, projectId, tokenBudget, includeCrossProject: toggle.crossProjectContext && $('context-cross').checked,
|
|
1932
|
+
profile, ...(profile === 'adaptive' ? { expandRelated: $('context-related').checked } : {}) };
|
|
1219
1933
|
store.pending = true;
|
|
1220
1934
|
store.dirty = false;
|
|
1935
|
+
store.pack = null;
|
|
1936
|
+
store.params = body;
|
|
1937
|
+
store.comparison = null;
|
|
1938
|
+
store.comparing = false;
|
|
1221
1939
|
renderContextControls();
|
|
1222
1940
|
setBusy($('context-result'), true);
|
|
1941
|
+
$('context-result-state').textContent = 'Assembling';
|
|
1223
1942
|
$('context-output').innerHTML = skeleton('Assembling context…');
|
|
1224
1943
|
try {
|
|
1225
|
-
|
|
1944
|
+
const pack = await api('context', { body });
|
|
1945
|
+
const current = state.features?.features;
|
|
1946
|
+
if (store.params === body && current?.contextAssembly && (pack.scope !== 'cross-project' || current.crossProjectContext)) store.pack = pack;
|
|
1226
1947
|
renderContext();
|
|
1227
1948
|
} catch (error) {
|
|
1228
|
-
store.
|
|
1229
|
-
|
|
1949
|
+
if (store.params === body) {
|
|
1950
|
+
$('context-result-state').textContent = 'Request failed';
|
|
1951
|
+
$('context-output').innerHTML = errorMarkup(error.message, 'context');
|
|
1952
|
+
}
|
|
1230
1953
|
} finally {
|
|
1231
1954
|
store.pending = false;
|
|
1232
1955
|
setBusy($('context-result'), false);
|
|
@@ -1234,10 +1957,44 @@ async function assembleContext() {
|
|
|
1234
1957
|
}
|
|
1235
1958
|
}
|
|
1236
1959
|
|
|
1960
|
+
async function compareContext() {
|
|
1961
|
+
const store = state.context;
|
|
1962
|
+
if (!store.params || store.dirty || store.pending || store.comparing || !store.pack?.enabled) return;
|
|
1963
|
+
const params = store.params;
|
|
1964
|
+
const { profile, ...body } = params;
|
|
1965
|
+
store.comparing = true;
|
|
1966
|
+
$('context-compare-button').disabled = true;
|
|
1967
|
+
renderComparison();
|
|
1968
|
+
try {
|
|
1969
|
+
const result = await api('context-compare', { body });
|
|
1970
|
+
if (!result.adaptive || !result.legacy || !result.delta) throw new Error('The server returned an unreadable comparison.');
|
|
1971
|
+
if (store.params === params && !store.dirty) store.comparison = result;
|
|
1972
|
+
} catch (error) {
|
|
1973
|
+
if (store.params === params && !store.dirty) store.comparison = { error: error.message };
|
|
1974
|
+
} finally {
|
|
1975
|
+
if (store.params === params) {
|
|
1976
|
+
store.comparing = false;
|
|
1977
|
+
if ($('context-compare-button')) $('context-compare-button').disabled = false;
|
|
1978
|
+
renderComparison();
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
function downloadContext() {
|
|
1984
|
+
const { pack, dirty } = state.context;
|
|
1985
|
+
if (!pack?.markdown || !pack.enabled || dirty || !state.features?.features.contextAssembly) return;
|
|
1986
|
+
const url = URL.createObjectURL(new Blob([pack.markdown], { type: 'text/markdown;charset=utf-8' }));
|
|
1987
|
+
const link = document.createElement('a');
|
|
1988
|
+
link.href = url;
|
|
1989
|
+
link.download = 'shadow-brain-context.md';
|
|
1990
|
+
link.click();
|
|
1991
|
+
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1237
1994
|
async function copyContext() {
|
|
1238
1995
|
const store = state.context;
|
|
1239
1996
|
const status = $('copy-status');
|
|
1240
|
-
if (!store.pack?.markdown || store.dirty || !status) return;
|
|
1997
|
+
if (!store.pack?.markdown || !store.pack.enabled || store.dirty || !status || !state.features?.features.contextAssembly) return;
|
|
1241
1998
|
try {
|
|
1242
1999
|
await navigator.clipboard.writeText(store.pack.markdown);
|
|
1243
2000
|
status.textContent = 'Brief copied.';
|
|
@@ -1247,18 +2004,110 @@ async function copyContext() {
|
|
|
1247
2004
|
status.hidden = false;
|
|
1248
2005
|
}
|
|
1249
2006
|
|
|
2007
|
+
const checkStates = { pass: ['ok', 'Passed'], warn: ['warn', 'Review'], unknown: ['', 'Unknown'], error: ['error', 'Error'] };
|
|
2008
|
+
const installStates = {
|
|
2009
|
+
complete: ['ok', 'Files present'], partial: ['warn', 'Some files present'], missing: ['', 'Files missing'],
|
|
2010
|
+
'not-checked': ['', 'Unchecked'], blocked: ['error', 'Check blocked']
|
|
2011
|
+
};
|
|
2012
|
+
|
|
2013
|
+
function checkTag(value, labels) {
|
|
2014
|
+
const [kind, label] = Object.hasOwn(labels, value) ? labels[value] : ['', 'Unknown'];
|
|
2015
|
+
return '<span class="tag check-' + kind + '">' + label + '</span>';
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
function commandMarkup(command) {
|
|
2019
|
+
if (typeof command !== 'string' || !command.trim()) return '';
|
|
2020
|
+
return '<div class="form-actions"><code class="mono">' + esc(command) + '</code><button type="button" class="button quiet" data-copy-command="' +
|
|
2021
|
+
esc(command) + '" aria-label="Copy command">' + icon('copy', true) + 'Copy</button><span class="small muted" role="status"></span></div>';
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
function docsLink(value) {
|
|
2025
|
+
if (typeof value !== 'string') return '';
|
|
2026
|
+
try {
|
|
2027
|
+
const url = new URL(value);
|
|
2028
|
+
if (url.protocol !== 'https:') return '';
|
|
2029
|
+
return '<a class="button quiet" href="' + esc(url.href) + '" target="_blank" rel="noopener noreferrer">Documentation ' +
|
|
2030
|
+
icon('diagonal', true) + '</a>';
|
|
2031
|
+
} catch { return ''; }
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
function renderIntegrations(data) {
|
|
2035
|
+
const checks = list(data.checks);
|
|
2036
|
+
const agents = list(data.agents);
|
|
2037
|
+
const checkRows = checks.map((check) => '<li class="check-row"><h3>' + esc(check.label || check.id) + '</h3><div class="check-result"><p class="small muted">' +
|
|
2038
|
+
esc(check.detail) + '</p>' + commandMarkup(check.command) + '</div>' + checkTag(check.status, checkStates) + '</li>').join('');
|
|
2039
|
+
const agentRows = agents.map((agent) => '<li class="check-row"><h3>' + esc(agent.label || agent.id) + '</h3><div class="check-result">' +
|
|
2040
|
+
tags(list(agent.capabilities).map((value) => ['memory', value])) + (agent.detail ? '<p class="small muted">' + esc(agent.detail) + '</p>' : '') +
|
|
2041
|
+
(agent.note ? '<p class="small muted">' + esc(agent.note) + '</p>' : '') + commandMarkup(agent.command) +
|
|
2042
|
+
(typeof agent.config?.content === 'string' ? '<details class="integration-config"><summary>View MCP setup example</summary>' +
|
|
2043
|
+
'<p class="small muted">' + esc([agent.config.format, agent.config.filename].filter(Boolean).join(' · ')) + '</p>' +
|
|
2044
|
+
'<pre class="context-preview mono" tabindex="0">' + esc(agent.config.content) + '</pre>' +
|
|
2045
|
+
list(agent.config.notes).map((note) => '<p class="small muted">' + esc(note) + '</p>').join('') +
|
|
2046
|
+
'<div class="form-actions"><button type="button" class="button quiet" data-copy-command="' + esc(agent.config.content) + '">' +
|
|
2047
|
+
icon('copy', true) + 'Copy example</button><span class="small muted" role="status"></span></div></details>' : '') +
|
|
2048
|
+
'<div class="form-actions">' + docsLink(agent.docs) + '</div></div>' + checkTag(agent.installation, installStates) + '</li>').join('');
|
|
2049
|
+
$('integrations-body').innerHTML = '<div class="stack"><section class="panel"><div class="panel-head"><div><h2>Local diagnostics</h2>' +
|
|
2050
|
+
(data.checkedAt ? '<p class="small muted">Last checked ' + dateMarkup(data.checkedAt) + '</p>' : '') + '</div>' +
|
|
2051
|
+
(typeof data.diagnostics === 'string' && data.diagnostics ? '<div class="form-actions"><button class="button" type="button" id="diagnostics-copy">' + icon('copy', true) +
|
|
2052
|
+
'Copy report</button><span class="small muted" role="status"></span></div>' : '') + '</div>' +
|
|
2053
|
+
(checkRows ? '<ul class="list">' + checkRows + '</ul>' : blank('activity', 'No diagnostic results', 'Run checks to refresh the local status.')) +
|
|
2054
|
+
'</section><section class="panel"><div class="panel-head"><div><h2>Supported agents</h2>' +
|
|
2055
|
+
'<p class="small muted">Installation status reflects project skill files. Use Live operations to inspect recorded sessions.</p></div></div>' +
|
|
2056
|
+
'<div class="integration-meta"><span class="tag project">Project skill files</span><span class="tag memory">MCP setup examples</span><a class="small" href="/live">Recorded sessions ' + icon('chevron', true) + '</a></div>' +
|
|
2057
|
+
(agentRows ? '<ul class="list">' + agentRows + '</ul>' : blank('integrations', 'No agent data', 'Run checks to load the support catalog.')) +
|
|
2058
|
+
'</section></div>';
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
async function loadIntegrations() {
|
|
2062
|
+
const store = state.integrations;
|
|
2063
|
+
if (store.pending) return;
|
|
2064
|
+
store.pending = true;
|
|
2065
|
+
const target = $('integrations-body');
|
|
2066
|
+
$('integrations-refresh').disabled = true;
|
|
2067
|
+
setBusy(target, true);
|
|
2068
|
+
target.innerHTML = '<div class="panel">' + skeleton('Running local checks…') + '</div>';
|
|
2069
|
+
try {
|
|
2070
|
+
store.snapshot = await api('integrations');
|
|
2071
|
+
renderIntegrations(store.snapshot);
|
|
2072
|
+
} catch (error) {
|
|
2073
|
+
target.innerHTML = errorMarkup(error.message, 'integrations');
|
|
2074
|
+
} finally {
|
|
2075
|
+
store.pending = false;
|
|
2076
|
+
$('integrations-refresh').disabled = false;
|
|
2077
|
+
setBusy(target, false);
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
async function copyIntegrationText(value, message, button) {
|
|
2082
|
+
const status = button.nextElementSibling;
|
|
2083
|
+
if (!value || !status) return;
|
|
2084
|
+
try {
|
|
2085
|
+
await navigator.clipboard.writeText(value);
|
|
2086
|
+
status.textContent = message;
|
|
2087
|
+
} catch {
|
|
2088
|
+
status.textContent = 'Clipboard access is unavailable.';
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
function projectFeatureLimit(id) {
|
|
2093
|
+
if (document.documentElement.dataset.projectLocal !== 'true') return undefined;
|
|
2094
|
+
return { crossProjectContext: 'Project memory stays scoped to this project.', automaticBriefing: 'Automatic briefing requires a legacy analysis session.' }[id];
|
|
2095
|
+
}
|
|
2096
|
+
|
|
1250
2097
|
function renderFeatures() {
|
|
1251
2098
|
const target = $('feature-list');
|
|
1252
2099
|
if (!state.features) return;
|
|
1253
2100
|
const current = state.features.features;
|
|
1254
2101
|
const overrides = new Map(list(state.features.definitions).map((item) => [item.id, item]));
|
|
2102
|
+
const icons = { contextAssembly: 'context', crossProjectContext: 'graph', automaticBriefing: 'agent', liveActivity: 'activity', activityRecording: 'memory' };
|
|
1255
2103
|
target.innerHTML = '<ul class="list">' + definitions.map((definition) => {
|
|
1256
2104
|
const display = overrides.get(definition.id) || definition;
|
|
1257
|
-
const
|
|
1258
|
-
|
|
1259
|
-
|
|
2105
|
+
const reason = projectFeatureLimit(definition.id);
|
|
2106
|
+
const on = !reason && current[definition.id] === true;
|
|
2107
|
+
return '<li class="setting"><div class="settings-description"><span class="tile-icon">' + icon(icons[definition.id], true) + '</span><div><h3 id="label-' + definition.id + '">' + esc(display.label) + '</h3><p class="muted small" id="desc-' +
|
|
2108
|
+
definition.id + '">' + esc(reason || display.description) + '</p></div></div><button type="button" class="button switch" role="switch" aria-checked="' +
|
|
1260
2109
|
on + '" aria-labelledby="label-' + definition.id + '" aria-describedby="desc-' + definition.id + '" data-feature="' + definition.id + '"' +
|
|
1261
|
-
(state.featureSaving ? ' disabled' : '') + '><span aria-hidden="true">' + (on ? 'On' : 'Off') + '</span><span class="switch-track" aria-hidden="true"></span></button></li>';
|
|
2110
|
+
(state.featureSaving || reason ? ' disabled' : '') + '><span aria-hidden="true">' + (reason ? 'Unavailable' : on ? 'On' : 'Off') + '</span><span class="switch-track" aria-hidden="true"></span></button></li>';
|
|
1262
2111
|
}).join('') + '</ul>';
|
|
1263
2112
|
}
|
|
1264
2113
|
|
|
@@ -1271,11 +2120,17 @@ function applyFeatures(snapshot) {
|
|
|
1271
2120
|
if (state.features && snapshot.revision < state.features.revision) return;
|
|
1272
2121
|
state.features = snapshot;
|
|
1273
2122
|
state.activityEnabled = current.liveActivity;
|
|
1274
|
-
|
|
2123
|
+
const context = state.context;
|
|
2124
|
+
if (!current.contextAssembly || !current.crossProjectContext && context.params?.includeCrossProject) {
|
|
2125
|
+
context.pack = null;
|
|
2126
|
+
context.params = null;
|
|
2127
|
+
context.comparison = null;
|
|
2128
|
+
context.comparing = false;
|
|
2129
|
+
}
|
|
1275
2130
|
renderFeatures();
|
|
1276
2131
|
renderActivity();
|
|
1277
2132
|
renderContextControls();
|
|
1278
|
-
if (
|
|
2133
|
+
if (!context.pending) renderContext();
|
|
1279
2134
|
if (current.liveActivity && before?.liveActivity !== true) void refreshActivity();
|
|
1280
2135
|
}
|
|
1281
2136
|
|
|
@@ -1310,7 +2165,7 @@ async function loadFeatures(force = false) {
|
|
|
1310
2165
|
}
|
|
1311
2166
|
|
|
1312
2167
|
async function toggleFeature(key) {
|
|
1313
|
-
if (state.featureSaving || !state.features || !definitions.some((definition) => definition.id === key)) return;
|
|
2168
|
+
if (state.featureSaving || !state.features || projectFeatureLimit(key) || !definitions.some((definition) => definition.id === key)) return;
|
|
1314
2169
|
state.featureSaving = true;
|
|
1315
2170
|
$('feature-list').querySelectorAll('[data-feature]').forEach((button) => { button.disabled = true; });
|
|
1316
2171
|
featureStatus('Saving…');
|
|
@@ -1362,23 +2217,30 @@ function routeParams() {
|
|
|
1362
2217
|
return new URLSearchParams(location.hash.split('?')[1] || '');
|
|
1363
2218
|
}
|
|
1364
2219
|
|
|
2220
|
+
function changeMemorySearch() {
|
|
2221
|
+
const query = memoryQuery();
|
|
2222
|
+
const route = routeLink('memories', query);
|
|
2223
|
+
if (location.hash !== route) location.hash = route;
|
|
2224
|
+
else void loadMemories(query);
|
|
2225
|
+
}
|
|
2226
|
+
|
|
1365
2227
|
function changeView(moveFocus = false) {
|
|
1366
2228
|
const hash = location.hash.slice(1).split('?')[0];
|
|
1367
2229
|
const view = Object.hasOwn(viewNames, hash) ? hash : 'overview';
|
|
1368
2230
|
const previous = state.view;
|
|
2231
|
+
const previousRoute = state.route;
|
|
1369
2232
|
state.view = view;
|
|
2233
|
+
state.route = location.hash || '#overview';
|
|
1370
2234
|
$('crumb').textContent = viewNames[view];
|
|
1371
|
-
document.title = viewNames[view] + ' · Shadow Brain';
|
|
2235
|
+
document.title = viewNames[view] + ' · Agent Shadow Brain';
|
|
1372
2236
|
for (const name of Object.keys(viewNames)) {
|
|
1373
2237
|
const section = $('page-' + name);
|
|
1374
2238
|
section.hidden = name !== view;
|
|
1375
|
-
section.classList.remove('entered');
|
|
1376
2239
|
const link = document.querySelector('[data-view="' + name + '"]');
|
|
1377
|
-
if (name === view) link
|
|
1378
|
-
else link
|
|
2240
|
+
if (name === view || name === 'memories' && view === 'memory') link?.setAttribute('aria-current', 'page');
|
|
2241
|
+
else link?.removeAttribute('aria-current');
|
|
1379
2242
|
}
|
|
1380
|
-
if (previous !== view
|
|
1381
|
-
if (moveFocus) {
|
|
2243
|
+
if (moveFocus && (previous !== view || view === 'memory')) {
|
|
1382
2244
|
window.scrollTo(0, 0);
|
|
1383
2245
|
$('title-' + view).focus({ preventScroll: true });
|
|
1384
2246
|
}
|
|
@@ -1386,19 +2248,44 @@ function changeView(moveFocus = false) {
|
|
|
1386
2248
|
const project = params.get('project') || '';
|
|
1387
2249
|
switch (view) {
|
|
1388
2250
|
case 'overview':
|
|
1389
|
-
void loadOverview(previous !== '' && previous !== 'overview');
|
|
2251
|
+
void loadOverview(previous !== '' && previous !== 'overview', project);
|
|
1390
2252
|
void loadFeatures();
|
|
1391
2253
|
break;
|
|
1392
|
-
case 'memories':
|
|
1393
|
-
|
|
1394
|
-
|
|
2254
|
+
case 'memories': {
|
|
2255
|
+
const query = { q: params.get('q') || '', project, category: params.get('category') || '', agent: params.get('agent') || '', state: params.get('state') || '' };
|
|
2256
|
+
$('memory-query').value = query.q;
|
|
2257
|
+
if (state.projects.length) optionSet('memory-project', 'All projects', state.projects, query.project);
|
|
2258
|
+
$('memory-state').value = query.state;
|
|
2259
|
+
void loadMemories(query);
|
|
2260
|
+
break;
|
|
2261
|
+
}
|
|
2262
|
+
case 'memory': {
|
|
2263
|
+
if (previous && previous !== 'memory') state.detail.back = previousRoute;
|
|
2264
|
+
const back = $('detail-back');
|
|
2265
|
+
const name = state.detail.back.slice(1).split('?')[0];
|
|
2266
|
+
back.href = state.detail.back;
|
|
2267
|
+
back.innerHTML = icon('left', true) + esc(viewNames[name] || 'Memories');
|
|
2268
|
+
void loadDetail(params.get('id') || '', project);
|
|
1395
2269
|
break;
|
|
1396
|
-
|
|
2270
|
+
}
|
|
2271
|
+
case 'graph': {
|
|
1397
2272
|
void loadOverview();
|
|
1398
2273
|
if (project) optionSet('graph-project', 'All projects', state.projects, project);
|
|
1399
|
-
|
|
2274
|
+
else $('graph-project').value = '';
|
|
2275
|
+
const memory = params.get('memory') || '';
|
|
2276
|
+
if (memory) ['graph-query', 'graph-kind', 'graph-relation'].forEach((id) => { $(id).value = ''; });
|
|
2277
|
+
void loadGraph(project, previous !== '' && previous !== 'graph', memory);
|
|
1400
2278
|
break;
|
|
2279
|
+
}
|
|
1401
2280
|
case 'context':
|
|
2281
|
+
if (project) {
|
|
2282
|
+
state.context.routeProject = project;
|
|
2283
|
+
syncProjectOptions();
|
|
2284
|
+
if (state.context.params && state.context.params.projectId !== project) {
|
|
2285
|
+
state.context.dirty = true;
|
|
2286
|
+
if (!state.context.pending) renderContext();
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
1402
2289
|
void loadOverview();
|
|
1403
2290
|
void loadFeatures(previous !== '');
|
|
1404
2291
|
renderContextControls();
|
|
@@ -1407,6 +2294,13 @@ function changeView(moveFocus = false) {
|
|
|
1407
2294
|
featureStatus('');
|
|
1408
2295
|
void loadFeatures(true);
|
|
1409
2296
|
break;
|
|
2297
|
+
case 'project-kit':
|
|
2298
|
+
void loadProjectKit();
|
|
2299
|
+
break;
|
|
2300
|
+
case 'integrations':
|
|
2301
|
+
if (state.integrations.snapshot) renderIntegrations(state.integrations.snapshot);
|
|
2302
|
+
else void loadIntegrations();
|
|
2303
|
+
break;
|
|
1410
2304
|
}
|
|
1411
2305
|
}
|
|
1412
2306
|
|
|
@@ -1414,12 +2308,29 @@ function graphSelectFromList(id, fromInspector) {
|
|
|
1414
2308
|
if (!state.graph.nodes.some((node) => node.id === id)) {
|
|
1415
2309
|
$('graph-query').value = '';
|
|
1416
2310
|
$('graph-kind').value = '';
|
|
2311
|
+
$('graph-relation').value = '';
|
|
1417
2312
|
renderGraph();
|
|
1418
2313
|
}
|
|
1419
2314
|
selectGraphNode(id, { center: fromInspector, focus: fromInspector });
|
|
1420
2315
|
}
|
|
1421
2316
|
|
|
1422
2317
|
function handleGraphKeyboard(event) {
|
|
2318
|
+
const edge = event.target.closest('[data-edge-record]');
|
|
2319
|
+
if (edge) {
|
|
2320
|
+
const links = Array.from($('graph-svg').querySelectorAll('[data-edge-record]'));
|
|
2321
|
+
const index = links.indexOf(edge);
|
|
2322
|
+
const nextIndex = { ArrowLeft: Math.max(index - 1, 0), ArrowRight: Math.min(index + 1, links.length - 1), Home: 0, End: links.length - 1 }[event.key];
|
|
2323
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
2324
|
+
event.preventDefault();
|
|
2325
|
+
selectGraphEdge(edge.dataset.edgeRecord);
|
|
2326
|
+
} else if (Number.isInteger(nextIndex)) {
|
|
2327
|
+
event.preventDefault();
|
|
2328
|
+
const next = links[nextIndex];
|
|
2329
|
+
selectGraphEdge(next.dataset.edgeRecord);
|
|
2330
|
+
next.focus();
|
|
2331
|
+
}
|
|
2332
|
+
return;
|
|
2333
|
+
}
|
|
1423
2334
|
const target = event.target.closest('[data-node]');
|
|
1424
2335
|
if (!target) return;
|
|
1425
2336
|
if (event.key === 'Enter' || event.key === ' ') {
|
|
@@ -1451,11 +2362,34 @@ function handleGraphKeyboard(event) {
|
|
|
1451
2362
|
$('theme-toggle').addEventListener('click', () => setPreference('theme', document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark'));
|
|
1452
2363
|
$('theme-setting').addEventListener('change', (event) => setPreference('theme', event.target.value));
|
|
1453
2364
|
$('motion-setting').addEventListener('change', (event) => setPreference('motion', event.target.value));
|
|
1454
|
-
$('memory-form').addEventListener('submit', (event) => { event.preventDefault();
|
|
1455
|
-
['memory-project', 'memory-category', 'memory-agent'].forEach((id) => $(id).addEventListener('change',
|
|
2365
|
+
$('memory-form').addEventListener('submit', (event) => { event.preventDefault(); changeMemorySearch(); });
|
|
2366
|
+
['memory-project', 'memory-category', 'memory-agent', 'memory-state'].forEach((id) => $(id).addEventListener('change', changeMemorySearch));
|
|
2367
|
+
$('overview-quality-project').addEventListener('change', (event) => {
|
|
2368
|
+
const project = event.target.value;
|
|
2369
|
+
const route = routeLink('overview', { project });
|
|
2370
|
+
window.history.replaceState(null, '', route);
|
|
2371
|
+
state.route = route;
|
|
2372
|
+
void loadOverview(true, project).then(() => {
|
|
2373
|
+
if (state.view === 'overview' && state.route === route) $('overview-quality-project')?.focus();
|
|
2374
|
+
});
|
|
2375
|
+
});
|
|
2376
|
+
$('overview-body').addEventListener('change', (event) => {
|
|
2377
|
+
if (event.target.id !== 'attention-filter') return;
|
|
2378
|
+
state.health.filter = event.target.value;
|
|
2379
|
+
renderAttention();
|
|
2380
|
+
});
|
|
2381
|
+
$('overview-body').addEventListener('click', (event) => {
|
|
2382
|
+
if (event.target.closest('#attention-jump')) $('attention-heading')?.focus();
|
|
2383
|
+
});
|
|
2384
|
+
$('integrations-refresh').addEventListener('click', () => { void loadIntegrations(); });
|
|
1456
2385
|
$('graph-query').addEventListener('input', renderGraph);
|
|
1457
2386
|
$('graph-kind').addEventListener('change', renderGraph);
|
|
1458
|
-
$('graph-
|
|
2387
|
+
$('graph-relation').addEventListener('change', renderGraph);
|
|
2388
|
+
$('graph-project').addEventListener('change', (event) => {
|
|
2389
|
+
const route = routeLink('graph', { project: event.target.value });
|
|
2390
|
+
if (location.hash !== route) location.hash = route;
|
|
2391
|
+
else void loadGraph(event.target.value);
|
|
2392
|
+
});
|
|
1459
2393
|
['map', 'list'].forEach((mode) => $('graph-mode-' + mode).addEventListener('click', () => {
|
|
1460
2394
|
state.graph.mode = mode;
|
|
1461
2395
|
$('graph-mode-map').setAttribute('aria-pressed', String(mode === 'map'));
|
|
@@ -1463,6 +2397,13 @@ $('graph-project').addEventListener('change', (event) => { void loadGraph(event.
|
|
|
1463
2397
|
renderGraph();
|
|
1464
2398
|
}));
|
|
1465
2399
|
$('graph-fit').addEventListener('click', () => fitGraph());
|
|
2400
|
+
$('graph-lens-clear').addEventListener('click', () => {
|
|
2401
|
+
const anchor = state.graph.lens;
|
|
2402
|
+
state.graph.lens = '';
|
|
2403
|
+
renderGraph();
|
|
2404
|
+
if (state.graph.nodes.some((node) => node.id === anchor)) selectGraphNode(anchor, { focus: true });
|
|
2405
|
+
else $('graph-query').focus();
|
|
2406
|
+
});
|
|
1466
2407
|
$('graph-in').addEventListener('click', () => zoomGraph(1.35));
|
|
1467
2408
|
$('graph-out').addEventListener('click', () => zoomGraph(1 / 1.35));
|
|
1468
2409
|
$('graph-svg').addEventListener('keydown', handleGraphKeyboard);
|
|
@@ -1472,7 +2413,7 @@ $('graph-svg').addEventListener('focusin', (event) => {
|
|
|
1472
2413
|
});
|
|
1473
2414
|
$('graph-svg').addEventListener('focusout', () => renderGraphTooltip(state.graph.selected));
|
|
1474
2415
|
$('graph-svg').addEventListener('pointerdown', (event) => {
|
|
1475
|
-
if (event.button !== 0 || !event.isPrimary || event.target.closest('[data-node]')) return;
|
|
2416
|
+
if (event.button !== 0 || !event.isPrimary || event.target.closest('[data-node],[data-edge-record]')) return;
|
|
1476
2417
|
const graph = state.graph;
|
|
1477
2418
|
graph.drag = { id: event.pointerId, x: event.clientX - graph.panX, y: event.clientY - graph.panY };
|
|
1478
2419
|
$('graph-svg').setPointerCapture(event.pointerId);
|
|
@@ -1492,13 +2433,14 @@ $('graph-svg').addEventListener('lostpointercapture', () => {
|
|
|
1492
2433
|
window.addEventListener('resize', () => {
|
|
1493
2434
|
if (state.view === 'graph' && state.graph.mode === 'map' && state.graph.nodes.length) {
|
|
1494
2435
|
renderGraphMap();
|
|
1495
|
-
|
|
2436
|
+
updateGraphSelection();
|
|
1496
2437
|
}
|
|
1497
2438
|
});
|
|
1498
2439
|
|
|
1499
2440
|
$('context-form').addEventListener('submit', (event) => { event.preventDefault(); void assembleContext(); });
|
|
1500
2441
|
$('context-form').addEventListener('input', () => {
|
|
1501
2442
|
$('context-task').setCustomValidity('');
|
|
2443
|
+
$('context-related-row').hidden = $('context-profile').value !== 'adaptive';
|
|
1502
2444
|
const store = state.context;
|
|
1503
2445
|
if (store.pack || store.pending) {
|
|
1504
2446
|
if (!store.dirty) {
|
|
@@ -1508,22 +2450,44 @@ $('context-form').addEventListener('input', () => {
|
|
|
1508
2450
|
}
|
|
1509
2451
|
});
|
|
1510
2452
|
|
|
2453
|
+
$('detail-body').addEventListener('submit', (event) => {
|
|
2454
|
+
const { id } = state.detail;
|
|
2455
|
+
switch (event.target.id) {
|
|
2456
|
+
case 'review-form':
|
|
2457
|
+
event.preventDefault();
|
|
2458
|
+
void saveDetail('memory-review', { memoryId: id, state: $('review-state').value, note: $('review-note').value.trim() }, 'review-submit');
|
|
2459
|
+
break;
|
|
2460
|
+
case 'link-search':
|
|
2461
|
+
event.preventDefault();
|
|
2462
|
+
void searchLinkTargets();
|
|
2463
|
+
break;
|
|
2464
|
+
case 'link-form':
|
|
2465
|
+
event.preventDefault();
|
|
2466
|
+
void saveDetail('memory-link', { fromId: id, toId: $('link-target').value, type: $('link-type').value, note: $('link-note').value.trim() }, 'link-editor');
|
|
2467
|
+
break;
|
|
2468
|
+
}
|
|
2469
|
+
});
|
|
2470
|
+
|
|
1511
2471
|
document.addEventListener('click', (event) => {
|
|
1512
|
-
const element = event.target.closest('[data-retry],[data-memory-clear],[data-memory-page],[data-node],[data-related],[data-feature],#context-copy,.skip');
|
|
2472
|
+
const element = event.target.closest('[data-retry],[data-memory-clear],[data-memory-page],[data-node],[data-related],[data-edge-record],[data-graph-lens],[data-feature],[data-unlink],[data-copy-command],#diagnostics-copy,#context-copy,#context-download,#context-compare-button,.skip');
|
|
1513
2473
|
if (!element) return;
|
|
1514
2474
|
const data = element.dataset;
|
|
1515
2475
|
if (data.retry) {
|
|
1516
2476
|
switch (data.retry) {
|
|
1517
|
-
case 'overview': void loadOverview(true); break;
|
|
2477
|
+
case 'overview': void loadOverview(true, state.view === 'overview' ? routeParams().get('project') || '' : ''); break;
|
|
2478
|
+
case 'health': void loadHealth(state.overviewScope); break;
|
|
2479
|
+
case 'kit-profile': void loadProjectKit(true); break;
|
|
1518
2480
|
case 'memories': void loadMemories(state.memory.query || memoryQuery(), state.memory.offset); break;
|
|
1519
|
-
case 'graph': void loadGraph(state.graph.project, true); break;
|
|
2481
|
+
case 'graph': void loadGraph(state.graph.project, true, state.graph.focusMemory); break;
|
|
2482
|
+
case 'detail': void loadDetail(state.detail.id, state.detail.project); break;
|
|
2483
|
+
case 'integrations': void loadIntegrations(); break;
|
|
1520
2484
|
case 'context': $('context-form').requestSubmit(); break;
|
|
1521
2485
|
case 'features': void loadFeatures(true); break;
|
|
1522
2486
|
}
|
|
1523
2487
|
} else if (Object.hasOwn(data, 'memoryClear')) {
|
|
1524
|
-
['memory-query', 'memory-project', 'memory-category', 'memory-agent'].forEach((id) => { $(id).value = ''; });
|
|
2488
|
+
['memory-query', 'memory-project', 'memory-category', 'memory-agent', 'memory-state'].forEach((id) => { $(id).value = ''; });
|
|
1525
2489
|
$('memory-query').focus();
|
|
1526
|
-
|
|
2490
|
+
changeMemorySearch();
|
|
1527
2491
|
} else if (data.memoryPage) {
|
|
1528
2492
|
const { query, offset, limit } = state.memory;
|
|
1529
2493
|
const step = data.memoryPage === 'next' ? limit : -limit;
|
|
@@ -1533,16 +2497,35 @@ document.addEventListener('click', (event) => {
|
|
|
1533
2497
|
selectGraphNode(data.node);
|
|
1534
2498
|
} else if (data.related) {
|
|
1535
2499
|
graphSelectFromList(data.related, true);
|
|
2500
|
+
} else if (data.edgeRecord) {
|
|
2501
|
+
selectGraphEdge(data.edgeRecord, $('graph-inspector').contains(element));
|
|
2502
|
+
} else if (data.graphLens) {
|
|
2503
|
+
state.graph.lens = data.graphLens;
|
|
2504
|
+
['graph-query', 'graph-kind', 'graph-relation'].forEach((id) => { $(id).value = ''; });
|
|
2505
|
+
renderGraph();
|
|
2506
|
+
$('graph-lens-clear').focus();
|
|
2507
|
+
} else if (data.unlink) {
|
|
2508
|
+
void saveDetail('memory-unlink', { memoryId: state.detail.id, linkId: data.unlink }, 'link-editor');
|
|
2509
|
+
} else if (data.copyCommand) {
|
|
2510
|
+
void copyIntegrationText(data.copyCommand, 'Command copied.', element);
|
|
1536
2511
|
} else if (data.feature) {
|
|
1537
2512
|
void toggleFeature(data.feature);
|
|
1538
2513
|
} else if (element.id === 'context-copy') {
|
|
1539
2514
|
void copyContext();
|
|
2515
|
+
} else if (element.id === 'context-download') {
|
|
2516
|
+
downloadContext();
|
|
2517
|
+
} else if (element.id === 'context-compare-button') {
|
|
2518
|
+
void compareContext();
|
|
2519
|
+
} else if (element.id === 'diagnostics-copy') {
|
|
2520
|
+
void copyIntegrationText(state.integrations.snapshot?.diagnostics, 'Diagnostic report copied.', element);
|
|
1540
2521
|
} else if (element.classList.contains('skip')) {
|
|
1541
2522
|
event.preventDefault();
|
|
1542
2523
|
$('main-content').focus();
|
|
1543
2524
|
}
|
|
1544
2525
|
});
|
|
1545
2526
|
|
|
2527
|
+
${PROJECT_KIT_SCRIPT}
|
|
2528
|
+
|
|
1546
2529
|
window.addEventListener('hashchange', () => changeView(true));
|
|
1547
2530
|
document.addEventListener('visibilitychange', () => { if (!document.hidden) void refreshActivity(); });
|
|
1548
2531
|
setInterval(() => { void refreshActivity(); }, 10000);
|