shieldcortex 4.16.0 → 4.17.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/dashboard/.next/standalone/dashboard/.next/BUILD_ID +1 -1
- package/dashboard/.next/standalone/dashboard/.next/build-manifest.json +2 -2
- package/dashboard/.next/standalone/dashboard/.next/prerender-manifest.json +3 -3
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.segments/!KGRhc2hib2FyZCk/admin/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.segments/!KGRhc2hib2FyZCk/admin.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/admin.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.segments/!KGRhc2hib2FyZCk/cloud/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.segments/!KGRhc2hib2FyZCk/cloud.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/cloud.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.segments/!KGRhc2hib2FyZCk/memory/capture/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.segments/!KGRhc2hib2FyZCk/memory/capture.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.segments/!KGRhc2hib2FyZCk/memory.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/capture.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.segments/!KGRhc2hib2FyZCk/memory/graph/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.segments/!KGRhc2hib2FyZCk/memory/graph.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.segments/!KGRhc2hib2FyZCk/memory.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/graph.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.segments/!KGRhc2hib2FyZCk/memory/recall/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.segments/!KGRhc2hib2FyZCk/memory/recall.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.segments/!KGRhc2hib2FyZCk/memory.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/recall.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.segments/!KGRhc2hib2FyZCk/memory/review/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.segments/!KGRhc2hib2FyZCk/memory/review.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.segments/!KGRhc2hib2FyZCk/memory.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/review.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.segments/!KGRhc2hib2FyZCk/memory/timeline/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.segments/!KGRhc2hib2FyZCk/memory/timeline.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.segments/!KGRhc2hib2FyZCk/memory.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory/timeline.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.segments/!KGRhc2hib2FyZCk/memory/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.segments/!KGRhc2hib2FyZCk/memory.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/memory.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.segments/!KGRhc2hib2FyZCk/overview/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.segments/!KGRhc2hib2FyZCk/overview.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/overview.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.segments/!KGRhc2hib2FyZCk/protection/audit/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.segments/!KGRhc2hib2FyZCk/protection/audit.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.segments/!KGRhc2hib2FyZCk/protection.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/audit.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.segments/!KGRhc2hib2FyZCk/protection/intercepts/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.segments/!KGRhc2hib2FyZCk/protection/intercepts.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.segments/!KGRhc2hib2FyZCk/protection.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/intercepts.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.segments/!KGRhc2hib2FyZCk/protection/iron-dome/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.segments/!KGRhc2hib2FyZCk/protection/iron-dome.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.segments/!KGRhc2hib2FyZCk/protection.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/iron-dome.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.segments/!KGRhc2hib2FyZCk/protection/policies/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.segments/!KGRhc2hib2FyZCk/protection/policies.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.segments/!KGRhc2hib2FyZCk/protection.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/policies.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.segments/!KGRhc2hib2FyZCk/protection/quarantine/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.segments/!KGRhc2hib2FyZCk/protection/quarantine.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.segments/!KGRhc2hib2FyZCk/protection.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection/quarantine.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.segments/!KGRhc2hib2FyZCk/protection/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.segments/!KGRhc2hib2FyZCk/protection.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/protection.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.segments/!KGRhc2hib2FyZCk/settings/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.segments/!KGRhc2hib2FyZCk/settings.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.segments/!KGRhc2hib2FyZCk/supply-chain/xray/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.segments/!KGRhc2hib2FyZCk/supply-chain/xray.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.segments/!KGRhc2hib2FyZCk/supply-chain.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain/xray.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.html +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.segments/!KGRhc2hib2FyZCk/supply-chain/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.segments/!KGRhc2hib2FyZCk/supply-chain.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/supply-chain.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.segments/!KGRhc2hib2FyZCk/xray/__PAGE__.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.segments/!KGRhc2hib2FyZCk/xray.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.segments/!KGRhc2hib2FyZCk.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.segments/_full.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.segments/_head.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.segments/_index.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/app/xray.segments/_tree.segment.rsc +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/pages/404.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/pages/500.html +2 -2
- package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.js +1 -1
- package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.json +1 -1
- package/dist/api/routes/sessions.d.ts +19 -0
- package/dist/api/routes/sessions.js +153 -0
- package/dist/api/visualization-server.js +2 -0
- package/dist/cli/import-jsonl.d.ts +12 -0
- package/dist/cli/import-jsonl.js +88 -0
- package/dist/database/init.js +44 -0
- package/dist/database/schema.sql +31 -0
- package/dist/index.js +8 -0
- package/dist/sessions/capture.d.ts +45 -0
- package/dist/sessions/capture.js +59 -0
- package/dist/sessions/import-jsonl.d.ts +85 -0
- package/dist/sessions/import-jsonl.js +193 -0
- package/dist/sessions/timeline.d.ts +28 -0
- package/dist/sessions/timeline.js +52 -0
- package/package.json +1 -1
- package/plugins/openclaw/dist/openclaw.plugin.json +1 -1
- package/scripts/lib/session-capture.mjs +102 -0
- package/scripts/pre-compact-hook.mjs +19 -0
- package/scripts/prompt-recall-hook.mjs +30 -4
- package/scripts/session-end-hook.mjs +19 -0
- /package/dashboard/.next/standalone/dashboard/.next/static/{6WjF0Utj3STrFgg7vZVPK → 9C0MR5ahRDqnfBA64wKCY}/_buildManifest.js +0 -0
- /package/dashboard/.next/standalone/dashboard/.next/static/{6WjF0Utj3STrFgg7vZVPK → 9C0MR5ahRDqnfBA64wKCY}/_clientMiddlewareManifest.json +0 -0
- /package/dashboard/.next/standalone/dashboard/.next/static/{6WjF0Utj3STrFgg7vZVPK → 9C0MR5ahRDqnfBA64wKCY}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session capture — write side.
|
|
3
|
+
*
|
|
4
|
+
* Hooks (`scripts/prompt-recall-hook.mjs`, `scripts/stop-hook.mjs`,
|
|
5
|
+
* `scripts/session-end-hook.mjs`, etc.) call `recordEvent()` or the
|
|
6
|
+
* batched `recordEvents()` to persist turn-by-turn events into the
|
|
7
|
+
* `session_events` table. The timeline reader in `./timeline.ts`
|
|
8
|
+
* then reassembles them into the replay stream.
|
|
9
|
+
*
|
|
10
|
+
* Distinct from the summary `sessions` table (one row per session)
|
|
11
|
+
* and from `defence_audit` (one row per scan). Captures every event
|
|
12
|
+
* with enough fidelity to scrub/replay.
|
|
13
|
+
*/
|
|
14
|
+
import { getDatabase } from '../database/init.js';
|
|
15
|
+
/**
|
|
16
|
+
* Serialise payload to text. Objects/arrays go through JSON.stringify;
|
|
17
|
+
* strings pass through. This keeps the column NOT NULL constraint
|
|
18
|
+
* satisfied even for empty-object payloads and matches the timeline
|
|
19
|
+
* reader's `JSON.parse` with raw-string fallback.
|
|
20
|
+
*/
|
|
21
|
+
function serialisePayload(payload) {
|
|
22
|
+
if (typeof payload === 'string')
|
|
23
|
+
return payload;
|
|
24
|
+
return JSON.stringify(payload ?? null);
|
|
25
|
+
}
|
|
26
|
+
const INSERT_SQL = `
|
|
27
|
+
INSERT INTO session_events
|
|
28
|
+
(session_id, project, ts, kind, actor, payload, duration_ms, audit_id)
|
|
29
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
30
|
+
`;
|
|
31
|
+
/** Insert one event row. Returns the new row id. */
|
|
32
|
+
export function recordEvent(input) {
|
|
33
|
+
const db = getDatabase();
|
|
34
|
+
const stmt = db.prepare(INSERT_SQL);
|
|
35
|
+
const result = stmt.run(input.session_id, input.project ?? null, input.ts, input.kind, input.actor ?? null, serialisePayload(input.payload), input.duration_ms ?? null, input.audit_id ?? null);
|
|
36
|
+
return Number(result.lastInsertRowid);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Batched insert wrapped in a single transaction. Used when a hook
|
|
40
|
+
* fires and produces several events at once (e.g. stop-hook emitting
|
|
41
|
+
* a `response` plus N tool_call/tool_result pairs from the just-
|
|
42
|
+
* finished turn). All-or-nothing: a CHECK violation rolls back the
|
|
43
|
+
* lot, so the table never holds half a turn.
|
|
44
|
+
*/
|
|
45
|
+
export function recordEvents(inputs) {
|
|
46
|
+
if (inputs.length === 0)
|
|
47
|
+
return [];
|
|
48
|
+
const db = getDatabase();
|
|
49
|
+
const stmt = db.prepare(INSERT_SQL);
|
|
50
|
+
const ids = [];
|
|
51
|
+
const tx = db.transaction((rows) => {
|
|
52
|
+
for (const row of rows) {
|
|
53
|
+
const result = stmt.run(row.session_id, row.project ?? null, row.ts, row.kind, row.actor ?? null, serialisePayload(row.payload), row.duration_ms ?? null, row.audit_id ?? null);
|
|
54
|
+
ids.push(Number(result.lastInsertRowid));
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
tx(inputs);
|
|
58
|
+
return ids;
|
|
59
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONL transcript importer for Claude Code session files.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code writes session transcripts to `~/.claude/projects/<slug>/
|
|
5
|
+
* <session-uuid>.jsonl`. Each line is a JSON object. Conversation lines
|
|
6
|
+
* have shape `{type, sessionId, timestamp, message: {role, content}}`
|
|
7
|
+
* where `content` is an Anthropic SDK content-block array
|
|
8
|
+
* (`text|thinking|tool_use|tool_result`).
|
|
9
|
+
*
|
|
10
|
+
* Mapping to `session_events.kind`:
|
|
11
|
+
*
|
|
12
|
+
* user.text → prompt
|
|
13
|
+
* assistant.text → response
|
|
14
|
+
* assistant.tool_use → tool_call
|
|
15
|
+
* user.tool_result → tool_result
|
|
16
|
+
* thinking → (skipped — not user-replayable in v1)
|
|
17
|
+
*
|
|
18
|
+
* Non-conversation lines (`ai-title`, `attachment`, `system`,
|
|
19
|
+
* `queue-operation`, etc.) are skipped. Idempotent on re-import: each
|
|
20
|
+
* row carries a sha256 `content_hash` of `kind|payload`, and the
|
|
21
|
+
* `idx_session_events_dedupe` UNIQUE index drops collisions silently
|
|
22
|
+
* via `INSERT OR IGNORE`.
|
|
23
|
+
*
|
|
24
|
+
* Lossy by design — agentmemory documents the same scope:
|
|
25
|
+
* - Anthropic-internal cache markers, partial-message frames, and
|
|
26
|
+
* non-string tool_result encodings beyond plain text are not
|
|
27
|
+
* preserved.
|
|
28
|
+
* - Things we WILL preserve: prompts, assistant text, tool name +
|
|
29
|
+
* args + result, and timestamps.
|
|
30
|
+
*/
|
|
31
|
+
import type { SessionEventInput } from './capture.js';
|
|
32
|
+
export interface TranscriptLine {
|
|
33
|
+
type?: string;
|
|
34
|
+
sessionId?: string;
|
|
35
|
+
timestamp?: string;
|
|
36
|
+
message?: {
|
|
37
|
+
role?: 'user' | 'assistant';
|
|
38
|
+
content?: ContentBlock[];
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
type ContentBlock = {
|
|
42
|
+
type: 'text';
|
|
43
|
+
text?: string;
|
|
44
|
+
} | {
|
|
45
|
+
type: 'thinking';
|
|
46
|
+
thinking?: string;
|
|
47
|
+
} | {
|
|
48
|
+
type: 'tool_use';
|
|
49
|
+
id?: string;
|
|
50
|
+
name?: string;
|
|
51
|
+
input?: unknown;
|
|
52
|
+
} | {
|
|
53
|
+
type: 'tool_result';
|
|
54
|
+
tool_use_id?: string;
|
|
55
|
+
content?: unknown;
|
|
56
|
+
} | {
|
|
57
|
+
type: string;
|
|
58
|
+
[key: string]: unknown;
|
|
59
|
+
};
|
|
60
|
+
export interface ImportResult {
|
|
61
|
+
sessionId: string | null;
|
|
62
|
+
eventCount: number;
|
|
63
|
+
/** Lines that produced zero events — non-conversation types + parse errors. */
|
|
64
|
+
skipped: number;
|
|
65
|
+
/** Lines whose JSON parsed but were rejected (missing required fields, unknown block types). */
|
|
66
|
+
malformed: number;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Pure mapper: one transcript line → 0..N `SessionEventInput`s.
|
|
70
|
+
*
|
|
71
|
+
* Exported so unit tests can exercise the mapping in isolation without
|
|
72
|
+
* standing up a database. Returns `[]` for any line that doesn't yield
|
|
73
|
+
* replayable events (thinking blocks, ai-title, missing fields, etc.) —
|
|
74
|
+
* the caller treats an empty array as "skipped".
|
|
75
|
+
*/
|
|
76
|
+
export declare function parseTranscriptLine(line: unknown): SessionEventInput[];
|
|
77
|
+
/**
|
|
78
|
+
* Import one JSONL file. Wraps the whole import in a single transaction
|
|
79
|
+
* so a mid-file error rolls back cleanly — better-sqlite3's transaction
|
|
80
|
+
* helper restores DB state if the callback throws. Malformed lines do
|
|
81
|
+
* NOT throw — they're counted and skipped so a single bad row never
|
|
82
|
+
* blocks the rest of the file.
|
|
83
|
+
*/
|
|
84
|
+
export declare function importJsonlTranscript(path: string): ImportResult;
|
|
85
|
+
export {};
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONL transcript importer for Claude Code session files.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code writes session transcripts to `~/.claude/projects/<slug>/
|
|
5
|
+
* <session-uuid>.jsonl`. Each line is a JSON object. Conversation lines
|
|
6
|
+
* have shape `{type, sessionId, timestamp, message: {role, content}}`
|
|
7
|
+
* where `content` is an Anthropic SDK content-block array
|
|
8
|
+
* (`text|thinking|tool_use|tool_result`).
|
|
9
|
+
*
|
|
10
|
+
* Mapping to `session_events.kind`:
|
|
11
|
+
*
|
|
12
|
+
* user.text → prompt
|
|
13
|
+
* assistant.text → response
|
|
14
|
+
* assistant.tool_use → tool_call
|
|
15
|
+
* user.tool_result → tool_result
|
|
16
|
+
* thinking → (skipped — not user-replayable in v1)
|
|
17
|
+
*
|
|
18
|
+
* Non-conversation lines (`ai-title`, `attachment`, `system`,
|
|
19
|
+
* `queue-operation`, etc.) are skipped. Idempotent on re-import: each
|
|
20
|
+
* row carries a sha256 `content_hash` of `kind|payload`, and the
|
|
21
|
+
* `idx_session_events_dedupe` UNIQUE index drops collisions silently
|
|
22
|
+
* via `INSERT OR IGNORE`.
|
|
23
|
+
*
|
|
24
|
+
* Lossy by design — agentmemory documents the same scope:
|
|
25
|
+
* - Anthropic-internal cache markers, partial-message frames, and
|
|
26
|
+
* non-string tool_result encodings beyond plain text are not
|
|
27
|
+
* preserved.
|
|
28
|
+
* - Things we WILL preserve: prompts, assistant text, tool name +
|
|
29
|
+
* args + result, and timestamps.
|
|
30
|
+
*/
|
|
31
|
+
import { readFileSync, existsSync } from 'fs';
|
|
32
|
+
import { createHash } from 'crypto';
|
|
33
|
+
import { getDatabase } from '../database/init.js';
|
|
34
|
+
/**
|
|
35
|
+
* Pure mapper: one transcript line → 0..N `SessionEventInput`s.
|
|
36
|
+
*
|
|
37
|
+
* Exported so unit tests can exercise the mapping in isolation without
|
|
38
|
+
* standing up a database. Returns `[]` for any line that doesn't yield
|
|
39
|
+
* replayable events (thinking blocks, ai-title, missing fields, etc.) —
|
|
40
|
+
* the caller treats an empty array as "skipped".
|
|
41
|
+
*/
|
|
42
|
+
export function parseTranscriptLine(line) {
|
|
43
|
+
if (!line || typeof line !== 'object')
|
|
44
|
+
return [];
|
|
45
|
+
const l = line;
|
|
46
|
+
// Only conversation lines yield events.
|
|
47
|
+
if (l.type !== 'user' && l.type !== 'assistant')
|
|
48
|
+
return [];
|
|
49
|
+
if (typeof l.sessionId !== 'string' || l.sessionId.length === 0)
|
|
50
|
+
return [];
|
|
51
|
+
if (typeof l.timestamp !== 'string' || l.timestamp.length === 0)
|
|
52
|
+
return [];
|
|
53
|
+
const content = l.message?.content;
|
|
54
|
+
if (!Array.isArray(content) || content.length === 0)
|
|
55
|
+
return [];
|
|
56
|
+
const events = [];
|
|
57
|
+
for (const block of content) {
|
|
58
|
+
const event = blockToEvent(block, l);
|
|
59
|
+
if (event)
|
|
60
|
+
events.push(event);
|
|
61
|
+
}
|
|
62
|
+
return events;
|
|
63
|
+
}
|
|
64
|
+
function blockToEvent(block, line) {
|
|
65
|
+
const session_id = line.sessionId;
|
|
66
|
+
const ts = line.timestamp;
|
|
67
|
+
if (block.type === 'thinking')
|
|
68
|
+
return null;
|
|
69
|
+
if (block.type === 'text' && line.type === 'user') {
|
|
70
|
+
const text = typeof block.text === 'string' ? block.text : '';
|
|
71
|
+
if (!text)
|
|
72
|
+
return null;
|
|
73
|
+
return { session_id, ts, kind: 'prompt', payload: { text } };
|
|
74
|
+
}
|
|
75
|
+
if (block.type === 'text' && line.type === 'assistant') {
|
|
76
|
+
const text = typeof block.text === 'string' ? block.text : '';
|
|
77
|
+
if (!text)
|
|
78
|
+
return null;
|
|
79
|
+
return {
|
|
80
|
+
session_id,
|
|
81
|
+
ts,
|
|
82
|
+
kind: 'response',
|
|
83
|
+
payload: { text },
|
|
84
|
+
actor: 'assistant',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (block.type === 'tool_use' && line.type === 'assistant') {
|
|
88
|
+
const toolBlock = block;
|
|
89
|
+
return {
|
|
90
|
+
session_id,
|
|
91
|
+
ts,
|
|
92
|
+
kind: 'tool_call',
|
|
93
|
+
payload: {
|
|
94
|
+
tool_use_id: toolBlock.id ?? null,
|
|
95
|
+
name: toolBlock.name ?? null,
|
|
96
|
+
input: toolBlock.input ?? null,
|
|
97
|
+
},
|
|
98
|
+
actor: 'assistant',
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (block.type === 'tool_result' && line.type === 'user') {
|
|
102
|
+
const tr = block;
|
|
103
|
+
return {
|
|
104
|
+
session_id,
|
|
105
|
+
ts,
|
|
106
|
+
kind: 'tool_result',
|
|
107
|
+
payload: {
|
|
108
|
+
tool_use_id: tr.tool_use_id ?? null,
|
|
109
|
+
content: tr.content ?? null,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
/** SHA-256 of `kind|payload-as-canonical-json`. 64-char hex string. */
|
|
116
|
+
function hashEvent(kind, payload) {
|
|
117
|
+
const canonical = typeof payload === 'string' ? payload : JSON.stringify(payload ?? null);
|
|
118
|
+
return createHash('sha256').update(`${kind}|${canonical}`).digest('hex');
|
|
119
|
+
}
|
|
120
|
+
const INSERT_OR_IGNORE_SQL = `
|
|
121
|
+
INSERT OR IGNORE INTO session_events
|
|
122
|
+
(session_id, project, ts, kind, actor, payload, duration_ms, audit_id, content_hash)
|
|
123
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
124
|
+
`;
|
|
125
|
+
/**
|
|
126
|
+
* Import one JSONL file. Wraps the whole import in a single transaction
|
|
127
|
+
* so a mid-file error rolls back cleanly — better-sqlite3's transaction
|
|
128
|
+
* helper restores DB state if the callback throws. Malformed lines do
|
|
129
|
+
* NOT throw — they're counted and skipped so a single bad row never
|
|
130
|
+
* blocks the rest of the file.
|
|
131
|
+
*/
|
|
132
|
+
export function importJsonlTranscript(path) {
|
|
133
|
+
if (!existsSync(path)) {
|
|
134
|
+
throw new Error(`JSONL transcript not found: ${path}`);
|
|
135
|
+
}
|
|
136
|
+
const raw = readFileSync(path, 'utf-8');
|
|
137
|
+
const lines = raw.split(/\r?\n/);
|
|
138
|
+
const rows = [];
|
|
139
|
+
let sessionId = null;
|
|
140
|
+
let skipped = 0;
|
|
141
|
+
let malformed = 0;
|
|
142
|
+
for (const rawLine of lines) {
|
|
143
|
+
const trimmed = rawLine.trim();
|
|
144
|
+
if (trimmed.length === 0)
|
|
145
|
+
continue; // blank lines aren't skipped — they're just whitespace
|
|
146
|
+
let parsed;
|
|
147
|
+
try {
|
|
148
|
+
parsed = JSON.parse(trimmed);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
malformed++;
|
|
152
|
+
skipped++;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const events = parseTranscriptLine(parsed);
|
|
156
|
+
if (events.length === 0) {
|
|
157
|
+
skipped++;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (sessionId === null && events[0]?.session_id) {
|
|
161
|
+
sessionId = events[0].session_id;
|
|
162
|
+
}
|
|
163
|
+
for (const event of events) {
|
|
164
|
+
rows.push({
|
|
165
|
+
...event,
|
|
166
|
+
content_hash: hashEvent(event.kind, event.payload),
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (rows.length === 0) {
|
|
171
|
+
return { sessionId, eventCount: 0, skipped, malformed };
|
|
172
|
+
}
|
|
173
|
+
const db = getDatabase();
|
|
174
|
+
const stmt = db.prepare(INSERT_OR_IGNORE_SQL);
|
|
175
|
+
const before = db.prepare('SELECT COUNT(*) AS c FROM session_events').get().c;
|
|
176
|
+
const tx = db.transaction((batch) => {
|
|
177
|
+
for (const row of batch) {
|
|
178
|
+
stmt.run(row.session_id, row.project ?? null, row.ts, row.kind, row.actor ?? null, typeof row.payload === 'string' ? row.payload : JSON.stringify(row.payload ?? null), row.duration_ms ?? null, row.audit_id ?? null, row.content_hash);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
tx(rows);
|
|
182
|
+
const after = db.prepare('SELECT COUNT(*) AS c FROM session_events').get().c;
|
|
183
|
+
const inserted = after - before;
|
|
184
|
+
// `eventCount` reflects the total events parsed from this file regardless
|
|
185
|
+
// of whether the dedupe index dropped them. Caller can compare to
|
|
186
|
+
// `inserted` (via row count diff) to see how many were duplicates.
|
|
187
|
+
return {
|
|
188
|
+
sessionId,
|
|
189
|
+
eventCount: rows.length,
|
|
190
|
+
skipped: skipped + (rows.length - inserted),
|
|
191
|
+
malformed,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session capture — read side.
|
|
3
|
+
*
|
|
4
|
+
* `getTimeline(sessionId)` returns every event for a session, sorted
|
|
5
|
+
* by `ts` ascending, with payload JSON.parse'd back into a JS value
|
|
6
|
+
* (or surfaced as a raw string when the payload was deliberately
|
|
7
|
+
* stored as plain text). Powers the dashboard replay scrubber.
|
|
8
|
+
*
|
|
9
|
+
* Kept deliberately minimal — pagination, filtering by kind, and
|
|
10
|
+
* cross-session timelines belong on the API route (`./api/routes/
|
|
11
|
+
* sessions.ts`) above, not in this reader.
|
|
12
|
+
*/
|
|
13
|
+
import type { SessionEventKind } from './capture.js';
|
|
14
|
+
export interface SessionEventRow {
|
|
15
|
+
id: number;
|
|
16
|
+
session_id: string;
|
|
17
|
+
project: string | null;
|
|
18
|
+
ts: string;
|
|
19
|
+
kind: SessionEventKind;
|
|
20
|
+
actor: string | null;
|
|
21
|
+
/** Parsed JSON (object/array/primitive) or raw string fall-through. */
|
|
22
|
+
payload: unknown;
|
|
23
|
+
duration_ms: number | null;
|
|
24
|
+
audit_id: number | null;
|
|
25
|
+
created_at: string;
|
|
26
|
+
}
|
|
27
|
+
/** All events for a session, in `ts` ascending order. */
|
|
28
|
+
export declare function getTimeline(sessionId: string): SessionEventRow[];
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session capture — read side.
|
|
3
|
+
*
|
|
4
|
+
* `getTimeline(sessionId)` returns every event for a session, sorted
|
|
5
|
+
* by `ts` ascending, with payload JSON.parse'd back into a JS value
|
|
6
|
+
* (or surfaced as a raw string when the payload was deliberately
|
|
7
|
+
* stored as plain text). Powers the dashboard replay scrubber.
|
|
8
|
+
*
|
|
9
|
+
* Kept deliberately minimal — pagination, filtering by kind, and
|
|
10
|
+
* cross-session timelines belong on the API route (`./api/routes/
|
|
11
|
+
* sessions.ts`) above, not in this reader.
|
|
12
|
+
*/
|
|
13
|
+
import { getDatabase } from '../database/init.js';
|
|
14
|
+
/**
|
|
15
|
+
* Reverse of `capture.serialisePayload`: try JSON.parse, fall back to
|
|
16
|
+
* the raw string if the payload was stored as plain text. We don't
|
|
17
|
+
* throw on un-parseable — the importer may legitimately ingest non-JSON
|
|
18
|
+
* tool output, and a replay viewer that crashes on bad data is useless.
|
|
19
|
+
*/
|
|
20
|
+
function deserialisePayload(text) {
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(text);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return text;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function hydrate(row) {
|
|
29
|
+
return {
|
|
30
|
+
id: row.id,
|
|
31
|
+
session_id: row.session_id,
|
|
32
|
+
project: row.project,
|
|
33
|
+
ts: row.ts,
|
|
34
|
+
kind: row.kind,
|
|
35
|
+
actor: row.actor,
|
|
36
|
+
payload: deserialisePayload(row.payload),
|
|
37
|
+
duration_ms: row.duration_ms,
|
|
38
|
+
audit_id: row.audit_id,
|
|
39
|
+
created_at: row.created_at,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** All events for a session, in `ts` ascending order. */
|
|
43
|
+
export function getTimeline(sessionId) {
|
|
44
|
+
const db = getDatabase();
|
|
45
|
+
const rows = db
|
|
46
|
+
.prepare(`SELECT id, session_id, project, ts, kind, actor, payload, duration_ms, audit_id, created_at
|
|
47
|
+
FROM session_events
|
|
48
|
+
WHERE session_id = ?
|
|
49
|
+
ORDER BY ts ASC, id ASC`)
|
|
50
|
+
.all(sessionId);
|
|
51
|
+
return rows.map(hydrate);
|
|
52
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shieldcortex",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.17.0",
|
|
4
4
|
"description": "Trustworthy memory and security for AI agents. Recall debugging, review queue, OpenClaw session capture, and memory poisoning defence for Claude Code, Codex, OpenClaw, LangChain, and MCP agents.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "shieldcortex-realtime",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.17.0",
|
|
4
4
|
"name": "ShieldCortex Real-time Scanner",
|
|
5
5
|
"description": "Real-time defence scanning on LLM input, memory extraction on LLM output, and active tool call interception with approval gating.",
|
|
6
6
|
"kind": null,
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook-side session_events writer.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the API of `src/sessions/capture.ts` but for `.mjs` hook
|
|
5
|
+
* scripts that import `better-sqlite3` directly and can't reach into
|
|
6
|
+
* the TS module without a build step. The two implementations write
|
|
7
|
+
* the same rows and share the same dedupe constraints.
|
|
8
|
+
*
|
|
9
|
+
* Hooks (prompt-recall, stop, session-end, pre-compact) call
|
|
10
|
+
* `recordSessionEvent(db, event)` with an already-open writable DB
|
|
11
|
+
* connection. Failures are caught + logged at the call site so a
|
|
12
|
+
* defective event capture never blocks the hook's primary job.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const VALID_KINDS = new Set([
|
|
16
|
+
'prompt',
|
|
17
|
+
'response',
|
|
18
|
+
'tool_call',
|
|
19
|
+
'tool_result',
|
|
20
|
+
'tool_error',
|
|
21
|
+
'hook_fire',
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
const INSERT_SQL = `
|
|
25
|
+
INSERT INTO session_events
|
|
26
|
+
(session_id, project, ts, kind, actor, payload, duration_ms, audit_id)
|
|
27
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
/** Stringify payload — objects → JSON, strings pass through verbatim. */
|
|
31
|
+
function serialisePayload(payload) {
|
|
32
|
+
if (typeof payload === 'string') return payload;
|
|
33
|
+
return JSON.stringify(payload ?? null);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Insert one event row. Returns the new row id, or `null` on validation
|
|
38
|
+
* failure (the caller decides whether that's an error or expected —
|
|
39
|
+
* e.g. session_id missing from hook data is "expected, skip" not "throw").
|
|
40
|
+
*
|
|
41
|
+
* Does NOT throw on SQL errors — the caller's hook flow must keep going.
|
|
42
|
+
* Captures the error message and returns null so the hook can log it.
|
|
43
|
+
*/
|
|
44
|
+
export function recordSessionEvent(db, event) {
|
|
45
|
+
if (!db || typeof db.prepare !== 'function') return null;
|
|
46
|
+
if (!event || typeof event !== 'object') return null;
|
|
47
|
+
if (typeof event.session_id !== 'string' || event.session_id.length === 0) return null;
|
|
48
|
+
if (typeof event.ts !== 'string' || event.ts.length === 0) return null;
|
|
49
|
+
if (!VALID_KINDS.has(event.kind)) return null;
|
|
50
|
+
if (event.payload === undefined) return null;
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const stmt = db.prepare(INSERT_SQL);
|
|
54
|
+
const result = stmt.run(
|
|
55
|
+
event.session_id,
|
|
56
|
+
event.project ?? null,
|
|
57
|
+
event.ts,
|
|
58
|
+
event.kind,
|
|
59
|
+
event.actor ?? null,
|
|
60
|
+
serialisePayload(event.payload),
|
|
61
|
+
event.duration_ms ?? null,
|
|
62
|
+
event.audit_id ?? null,
|
|
63
|
+
);
|
|
64
|
+
return Number(result.lastInsertRowid);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
return { error: err.message };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Batched insert under a single transaction. Returns an array of
|
|
72
|
+
* inserted row ids; rolls back the lot if any row violates a
|
|
73
|
+
* constraint. Empty input returns [] without opening a transaction.
|
|
74
|
+
*/
|
|
75
|
+
export function recordSessionEvents(db, events) {
|
|
76
|
+
if (!Array.isArray(events) || events.length === 0) return [];
|
|
77
|
+
const stmt = db.prepare(INSERT_SQL);
|
|
78
|
+
const ids = [];
|
|
79
|
+
const tx = db.transaction((rows) => {
|
|
80
|
+
for (const event of rows) {
|
|
81
|
+
if (typeof event?.session_id !== 'string') {
|
|
82
|
+
throw new Error('session_id required');
|
|
83
|
+
}
|
|
84
|
+
if (!VALID_KINDS.has(event.kind)) {
|
|
85
|
+
throw new Error(`invalid kind: ${event.kind}`);
|
|
86
|
+
}
|
|
87
|
+
const result = stmt.run(
|
|
88
|
+
event.session_id,
|
|
89
|
+
event.project ?? null,
|
|
90
|
+
event.ts,
|
|
91
|
+
event.kind,
|
|
92
|
+
event.actor ?? null,
|
|
93
|
+
serialisePayload(event.payload),
|
|
94
|
+
event.duration_ms ?? null,
|
|
95
|
+
event.audit_id ?? null,
|
|
96
|
+
);
|
|
97
|
+
ids.push(Number(result.lastInsertRowid));
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
tx(events);
|
|
101
|
+
return ids;
|
|
102
|
+
}
|
|
@@ -21,6 +21,7 @@ import { readTranscriptText } from './lib/transcript-reader.mjs';
|
|
|
21
21
|
import { getAutoMemoryConfig } from './lib/auto-memory-config.mjs';
|
|
22
22
|
import { recordHookInvocation } from './lib/telemetry.mjs';
|
|
23
23
|
import { deriveProjectKey } from './lib/project-key.mjs';
|
|
24
|
+
import { recordSessionEvent } from './lib/session-capture.mjs';
|
|
24
25
|
import {
|
|
25
26
|
extractMemorableSegments,
|
|
26
27
|
processSegments,
|
|
@@ -154,6 +155,24 @@ process.stdin.on('end', async () => {
|
|
|
154
155
|
// timeout: 5000ms prevents hook from hanging if DB is locked
|
|
155
156
|
db = new Database(DB_PATH, { timeout: 5000 });
|
|
156
157
|
|
|
158
|
+
// ── Session capture (v4.17): mark the compaction boundary in the
|
|
159
|
+
// replay timeline. Reuses the already-open write connection so
|
|
160
|
+
// no extra better-sqlite3 handle is created.
|
|
161
|
+
{
|
|
162
|
+
const sessionId = hookData.session_id || hookData.sessionId || null;
|
|
163
|
+
if (sessionId) {
|
|
164
|
+
try {
|
|
165
|
+
recordSessionEvent(db, {
|
|
166
|
+
session_id: sessionId,
|
|
167
|
+
ts: new Date().toISOString(),
|
|
168
|
+
kind: 'hook_fire',
|
|
169
|
+
payload: { hook: 'pre-compact', trigger, transcript_path: hookData.transcript_path ?? null },
|
|
170
|
+
project: project || null,
|
|
171
|
+
});
|
|
172
|
+
} catch { /* best-effort */ }
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
157
176
|
// Get current memory stats for dynamic threshold calculation
|
|
158
177
|
const stats = getMemoryStats(db);
|
|
159
178
|
const totalMemories = stats.shortTerm + stats.longTerm;
|
|
@@ -15,6 +15,7 @@ import { join } from 'path';
|
|
|
15
15
|
import { homedir } from 'os';
|
|
16
16
|
import { deriveProjectKey } from './lib/project-key.mjs';
|
|
17
17
|
import { sanitisePromptForRecall } from './lib/prompt-sanitiser.mjs';
|
|
18
|
+
import { recordSessionEvent } from './lib/session-capture.mjs';
|
|
18
19
|
|
|
19
20
|
// ==================== CONFIG ====================
|
|
20
21
|
|
|
@@ -153,15 +154,40 @@ process.stdin.on('readable', () => {
|
|
|
153
154
|
process.stdin.on('end', () => {
|
|
154
155
|
try {
|
|
155
156
|
const config = loadConfig();
|
|
157
|
+
const hookData = JSON.parse(input || '{}');
|
|
158
|
+
const rawPrompt = hookData.prompt || '';
|
|
159
|
+
const cwd = hookData.cwd || process.cwd();
|
|
160
|
+
const sessionId = hookData.session_id || hookData.sessionId || null;
|
|
161
|
+
|
|
162
|
+
// ── Session capture (v4.17): record the user prompt regardless of
|
|
163
|
+
// proactiveRecall config so the dashboard replay timeline gets a
|
|
164
|
+
// complete event stream. Opt-out via captureEvents=false. Failures
|
|
165
|
+
// are swallowed — capture must never block the user prompt.
|
|
166
|
+
if (config.captureEvents !== false && sessionId && rawPrompt) {
|
|
167
|
+
try {
|
|
168
|
+
const project = deriveProjectKey(cwd);
|
|
169
|
+
const dbPath = getDbPath();
|
|
170
|
+
if (existsSync(dbPath)) {
|
|
171
|
+
const captureDb = new Database(dbPath, { timeout: 1000 });
|
|
172
|
+
recordSessionEvent(captureDb, {
|
|
173
|
+
session_id: sessionId,
|
|
174
|
+
ts: new Date().toISOString(),
|
|
175
|
+
kind: 'prompt',
|
|
176
|
+
payload: { text: rawPrompt },
|
|
177
|
+
project: project || null,
|
|
178
|
+
actor: 'user',
|
|
179
|
+
});
|
|
180
|
+
captureDb.close();
|
|
181
|
+
}
|
|
182
|
+
} catch {
|
|
183
|
+
// Capture is best-effort.
|
|
184
|
+
}
|
|
185
|
+
}
|
|
156
186
|
|
|
157
187
|
if (config.proactiveRecall !== true) {
|
|
158
188
|
process.exit(0);
|
|
159
189
|
}
|
|
160
190
|
|
|
161
|
-
const hookData = JSON.parse(input || '{}');
|
|
162
|
-
const rawPrompt = hookData.prompt || '';
|
|
163
|
-
const cwd = hookData.cwd || process.cwd();
|
|
164
|
-
|
|
165
191
|
// Strip OpenClaw / framework metadata wrappers (e.g. Telegram channel
|
|
166
192
|
// headers) before any prompt-based decisions. Without this, the FTS5
|
|
167
193
|
// query is built from "Conversation info untrusted metadata json" tokens
|
|
@@ -30,6 +30,7 @@ import { readTranscriptText } from './lib/transcript-reader.mjs';
|
|
|
30
30
|
import { getAutoMemoryConfig } from './lib/auto-memory-config.mjs';
|
|
31
31
|
import { recordHookInvocation } from './lib/telemetry.mjs';
|
|
32
32
|
import { deriveProjectKey } from './lib/project-key.mjs';
|
|
33
|
+
import { recordSessionEvent } from './lib/session-capture.mjs';
|
|
33
34
|
import {
|
|
34
35
|
extractMemorableSegments,
|
|
35
36
|
processSegments,
|
|
@@ -128,6 +129,24 @@ process.stdin.on('end', async () => {
|
|
|
128
129
|
const reason = hookData.reason || 'unknown';
|
|
129
130
|
const project = deriveProjectKey(hookData.cwd);
|
|
130
131
|
const autoMemConfig = getAutoMemoryConfig();
|
|
132
|
+
const sessionId = hookData.session_id || hookData.sessionId || null;
|
|
133
|
+
|
|
134
|
+
// ── Session capture (v4.17): record a `hook_fire` marker so the
|
|
135
|
+
// replay timeline shows where sessions end. Best-effort — never
|
|
136
|
+
// blocks memory extraction below.
|
|
137
|
+
if (sessionId && existsSync(DB_PATH)) {
|
|
138
|
+
try {
|
|
139
|
+
const captureDb = new Database(DB_PATH, { timeout: 1000 });
|
|
140
|
+
recordSessionEvent(captureDb, {
|
|
141
|
+
session_id: sessionId,
|
|
142
|
+
ts: new Date().toISOString(),
|
|
143
|
+
kind: 'hook_fire',
|
|
144
|
+
payload: { hook: 'session-end', reason, transcript_path: hookData.transcript_path ?? null },
|
|
145
|
+
project: project || null,
|
|
146
|
+
});
|
|
147
|
+
captureDb.close();
|
|
148
|
+
} catch { /* best-effort */ }
|
|
149
|
+
}
|
|
131
150
|
|
|
132
151
|
// Config gate: opt-in (default off). Preserves the historical default
|
|
133
152
|
// that protected OpenClaw users. We exit before touching the DB so a
|
|
File without changes
|
|
File without changes
|