@jlongo78/agent-spaces 0.7.3 → 0.7.5
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +7 -0
- package/.next/standalone/.next/build-manifest.json +2 -2
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +19 -19
- package/.next/standalone/.next/routes-manifest.json +42 -0
- package/.next/standalone/.next/server/app/(desktop)/admin/analytics/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/admin/analytics/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/admin/analytics/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/admin/users/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/admin/users/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/admin/users/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/analytics/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/analytics/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/analytics/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/cortex/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/cortex/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/cortex/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/network/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/network/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/network/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/projects/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/sessions/[id]/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/sessions/[id]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/sessions/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/sessions/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/sessions/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/settings/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/pane/[id]/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/pane/[id]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/pane/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/workspaces/page.js +1 -1
- package/.next/standalone/.next/server/app/(desktop)/workspaces/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/(desktop)/workspaces/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +2 -2
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/analytics.html +1 -1
- package/.next/standalone/.next/server/app/admin/analytics.rsc +17 -16
- package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin/analytics/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin/analytics.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/admin/analytics.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/admin/analytics.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/analytics.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/analytics.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/users.html +1 -1
- package/.next/standalone/.next/server/app/admin/users.rsc +17 -16
- package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin/users/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin/users.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/admin/users.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/admin/users.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/admin/users.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/admin/users.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/analytics.html +1 -1
- package/.next/standalone/.next/server/app/analytics.rsc +17 -16
- package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap/analytics/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap/analytics.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/analytics.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/analytics.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/analytics.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/analytics.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/analytics/overview/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bulk/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/chat/route.js +1 -1
- package/.next/standalone/.next/server/app/api/chat/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/config/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/context/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route.js +7 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route.js +7 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route.js +7 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route.js +7 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route.js +7 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/cortex/export/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/pending/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/resolve/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/search/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/federation/teach/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/edges/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/entities/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/entities/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/graph/populate/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/import/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/ingest/bootstrap/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/ingest/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/knowledge/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/knowledge/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/lobes/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/lobes/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/lobes/share/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route.js +7 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route.js +7 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/cortex/mcp/call/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/mcp/tools/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/search/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/settings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/timeline/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/usage/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/workspace/[id]/context/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/events/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/folders/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/handshake/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/projects/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/search/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/sessions/[id]/messages/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/sessions/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/workspaces/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/workspaces/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/workspaces/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/panes/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/panes/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/search/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/chat/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/messages/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sync/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/tags/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/tier/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/context/[key]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/context/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/[msgId]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/messages/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/cortex.html +1 -1
- package/.next/standalone/.next/server/app/cortex.rsc +17 -16
- package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap/cortex/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap/cortex.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/cortex.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/cortex.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/cortex.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/cortex.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/cortex.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/login.html +1 -1
- package/.next/standalone/.next/server/app/login.rsc +2 -2
- package/.next/standalone/.next/server/app/login.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/login.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/login.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/login.segments/login/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/login.segments/login.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/m/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/m/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/projects.html +1 -1
- package/.next/standalone/.next/server/app/m/projects.rsc +4 -4
- package/.next/standalone/.next/server/app/m/projects.segments/_full.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/m/projects.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/projects.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/projects.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/projects.segments/m/projects/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/projects.segments/m/projects.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/projects.segments/m.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/sessions/[id]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/m/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/sessions/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/m/sessions/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/sessions.html +1 -1
- package/.next/standalone/.next/server/app/m/sessions.rsc +4 -4
- package/.next/standalone/.next/server/app/m/sessions.segments/_full.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/m/sessions.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/sessions.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/sessions.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/sessions.segments/m/sessions/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/sessions.segments/m/sessions.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/sessions.segments/m.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/m/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/settings.html +1 -1
- package/.next/standalone/.next/server/app/m/settings.rsc +4 -4
- package/.next/standalone/.next/server/app/m/settings.segments/_full.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/m/settings.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/settings.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/settings.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/settings.segments/m/settings/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/settings.segments/m/settings.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/settings.segments/m.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/terminal/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/m/terminal/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/m/terminal.html +1 -1
- package/.next/standalone/.next/server/app/m/terminal.rsc +4 -4
- package/.next/standalone/.next/server/app/m/terminal.segments/_full.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/m/terminal.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/terminal.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/terminal.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/terminal.segments/m/terminal/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m/terminal.segments/m/terminal.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m/terminal.segments/m.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m.html +1 -1
- package/.next/standalone/.next/server/app/m.rsc +4 -4
- package/.next/standalone/.next/server/app/m.segments/_full.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/m.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/m.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m.segments/m/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/m.segments/m.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/network.html +1 -1
- package/.next/standalone/.next/server/app/network.rsc +17 -16
- package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap/network/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap/network.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/network.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/network.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/network.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/network.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/projects.html +1 -1
- package/.next/standalone/.next/server/app/projects.rsc +17 -16
- package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap/projects/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap/projects.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/projects.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/projects.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/projects.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/projects.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/sessions.html +1 -1
- package/.next/standalone/.next/server/app/sessions.rsc +17 -16
- package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap/sessions/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap/sessions.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/sessions.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/sessions.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/sessions.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/sessions.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.html +1 -1
- package/.next/standalone/.next/server/app/settings.rsc +17 -16
- package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap/settings/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap/settings.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/settings.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/terminal.html +1 -1
- package/.next/standalone/.next/server/app/terminal.rsc +17 -16
- package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap/terminal/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap/terminal.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/terminal.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/terminal.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/terminal.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/terminal.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/terminal.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/workspaces.html +1 -1
- package/.next/standalone/.next/server/app/workspaces.rsc +17 -16
- package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap/workspaces/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap/workspaces.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap.segment.rsc +5 -4
- package/.next/standalone/.next/server/app/workspaces.segments/_full.segment.rsc +17 -16
- package/.next/standalone/.next/server/app/workspaces.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/workspaces.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/workspaces.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app-paths-manifest.json +7 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0041efe4._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__00bf0ace._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e71d908._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e9142f3._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__10e47926._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1665dc78._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__175cbabf._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1adae357._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1d359752._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1e8fabeb._.js +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1f8deca0._.js +8 -8
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__253fdda1._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__28e6434f._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__2a386564._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__2c20fb38._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__309132cd._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__cf3c60c2._.js → [root-of-the-server]__33fec964._.js} +4 -4
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__3786d8ae._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__3ae92407._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__3beda9fe._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__4619e9bd._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__4a051043._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__508002e4._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__5086c373._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__5913e097._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__5b5f68d2._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__5c1f2459._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__5ec8c977._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__63cebc6c._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__64d30d4d._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__6c54fc2e._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__6dc1fb7e._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__6e568102._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__6faa04c0._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__74a34dc3._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__7e7250a4._.js +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__8309e0a4._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__86cc0e2b._.js +6 -6
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__89c2565a._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__8d178ad9._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__93ee06f3._.js +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__9e4c154a._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__a9d2e1d3._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ae53d343._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b3a04cef._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b4270b77._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b6b6ce60._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b9545dd9._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c88b63f7._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__eee4c5e8._.js → [root-of-the-server]__cba5f007._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__cbf4ceb0._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__cefdba2f._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__cf9e82bb._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d2897392._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d3b2d856._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d73273ca._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d8417eb6._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__dc2a55de._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__e0d4690b._.js +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__e678dd53._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__e9223f55._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ea630076._.js +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__eb8acb65._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__f26ca49d._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__f33e1101._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__f515f865._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__fceb5d60._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__fed41403._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ff2e98c2._.js +2 -2
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_cortex_curation_assess_route_actions_3e8ef2a6.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_cortex_curation_publish_route_actions_49238be3.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_cortex_curation_refine_route_actions_05675688.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_cortex_curation_review_route_actions_5cbb5f6b.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_cortex_curation_seed_route_actions_21084bdb.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_cortex_marketplace_browse_route_actions_7ed0768c.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_cortex_marketplace_preview_route_actions_38d4cc17.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__19afc53d._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__66aca5d4._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__efef0a06._.js → [root-of-the-server]__ca5beb09._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_7dd2ac82._.js → _078dd64d._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_163d0838._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_a4eeff0d._.js → _2230ad2d._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_9303a965._.js → _701606d5._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_72b1de37._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_d39bcfda._.js → _93ef0f79._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_950142a4._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_a22b5eb0._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_aeeff784._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_c1cfdd09._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_c2d3f6de._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_8167090e._.js → _db2fec84._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/src_02bae6e5._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_app_(desktop)_terminal_page_tsx_de5e8d85._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/src_components_terminal_terminal-pane_tsx_803c5e2c._.js +2 -2
- package/.next/standalone/.next/server/edge/chunks/_d73df637._.js +1 -1
- package/.next/standalone/.next/server/middleware-manifest.json +5 -5
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +2 -2
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/25b7a243a404a1a7.js +1 -0
- package/.next/standalone/.next/static/chunks/2b997e211a5d547b.js +1 -0
- package/.next/standalone/.next/static/chunks/5e08abb00653754a.js +1 -0
- package/.next/standalone/.next/static/chunks/61f2ed39b75b1efc.js +1 -0
- package/.next/standalone/.next/static/chunks/6c78a1dfa7ec2959.css +3 -0
- package/.next/standalone/.next/static/chunks/7246f1ee445f7024.js +1 -0
- package/.next/standalone/.next/static/chunks/7424664c6ffa94bd.js +1 -0
- package/.next/standalone/.next/static/chunks/7e0091ab6c5ee8bd.js +1 -0
- package/.next/standalone/.next/static/chunks/8b3f4572fec83caa.js +5 -0
- package/.next/standalone/.next/static/chunks/{a7b2795949a6c63e.js → 9899cf4c2bdbe61d.js} +2 -2
- package/.next/standalone/.next/static/chunks/ac339e970df82fa5.js +5 -0
- package/.next/standalone/.next/static/chunks/{0e61e67b7b8fc3f3.js → c418112e102673ce.js} +1 -1
- package/.next/standalone/.next/static/chunks/e2f0a2330dedff4b.js +85 -0
- package/.next/standalone/.next/static/chunks/f0c8b3f8cc1939ec.js +1 -0
- package/.next/standalone/.next/static/chunks/f9f2628207848ac2.js +1 -0
- package/.next/standalone/bin/cortex-hook.sh +62 -62
- package/.next/standalone/bin/cortex-mcp.js +60 -60
- package/.next/standalone/docs/superpowers/plans/2026-03-13-cortex-wiring.md +1387 -1387
- package/.next/standalone/docs/superpowers/plans/2026-03-14-cortex-v2-entity-graph.md +1923 -1923
- package/.next/standalone/docs/superpowers/plans/2026-03-14-cortex-v2-knowledge-evolution.md +1113 -1113
- package/.next/standalone/docs/superpowers/plans/2026-03-15-cortex-v2-boundary-engine.md +853 -853
- package/.next/standalone/docs/superpowers/plans/2026-03-15-cortex-v2-context-engine.md +1274 -1274
- package/.next/standalone/docs/superpowers/plans/2026-03-15-cortex-v2-signal-ingestion.md +933 -933
- package/.next/standalone/docs/superpowers/plans/2026-03-16-cortex-lobes.md +1080 -1080
- package/.next/standalone/docs/superpowers/plans/2026-03-16-cortex-v2-gravity-system.md +768 -768
- package/.next/standalone/docs/superpowers/plans/2026-03-16-cortex-v2-ui.md +1108 -1108
- package/.next/standalone/docs/superpowers/plans/2026-03-18-cortex-ui-integration.md +1846 -0
- package/.next/standalone/docs/superpowers/specs/2026-03-13-cortex-wiring-design.md +268 -268
- package/.next/standalone/docs/superpowers/specs/2026-03-14-cortex-v2-design.md +623 -623
- package/.next/standalone/docs/superpowers/specs/2026-03-16-cortex-lobes-design.md +263 -263
- package/.next/standalone/docs/superpowers/specs/2026-03-16-cortex-v2-ui-design.md +240 -240
- package/.next/standalone/docs/superpowers/specs/2026-03-18-cortex-ui-integration-design.md +341 -0
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/README.md +46 -0
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/glib-2.0/include/glibconfig.h +221 -0
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/index.js +1 -0
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/libvips-cpp.so.8.17.3 +0 -0
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/package.json +42 -0
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/README.md +46 -0
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/glib-2.0/include/glibconfig.h +221 -0
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/index.js +1 -0
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/libvips-cpp.so.8.17.3 +0 -0
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/package.json +42 -0
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/versions.json +30 -0
- package/.next/standalone/node_modules/@img/sharp-linux-x64/lib/sharp-linux-x64.node +0 -0
- package/.next/standalone/node_modules/@img/{sharp-win32-x64 → sharp-linux-x64}/package.json +46 -39
- package/.next/standalone/node_modules/@img/sharp-linuxmusl-x64/lib/sharp-linuxmusl-x64.node +0 -0
- package/.next/standalone/node_modules/@img/sharp-linuxmusl-x64/package.json +46 -0
- package/.next/standalone/package.json +102 -102
- package/.next/standalone/server.js +1 -1
- package/.next/standalone/src/app/(desktop)/cortex/page.tsx +78 -75
- package/.next/standalone/src/app/api/cortex/context/route.ts +78 -78
- package/.next/standalone/src/app/api/cortex/curation/assess/route.ts +27 -0
- package/.next/standalone/src/app/api/cortex/curation/publish/route.ts +23 -0
- package/.next/standalone/src/app/api/cortex/curation/refine/route.ts +23 -0
- package/.next/standalone/src/app/api/cortex/curation/review/route.ts +29 -0
- package/.next/standalone/src/app/api/cortex/curation/seed/route.ts +23 -0
- package/.next/standalone/src/app/api/cortex/export/route.ts +40 -40
- package/.next/standalone/src/app/api/cortex/federation/pending/route.ts +20 -20
- package/.next/standalone/src/app/api/cortex/federation/resolve/route.ts +43 -43
- package/.next/standalone/src/app/api/cortex/federation/search/route.ts +35 -35
- package/.next/standalone/src/app/api/cortex/federation/teach/route.ts +76 -76
- package/.next/standalone/src/app/api/cortex/graph/edges/route.ts +112 -112
- package/.next/standalone/src/app/api/cortex/graph/entities/[id]/route.ts +73 -73
- package/.next/standalone/src/app/api/cortex/graph/entities/route.ts +75 -75
- package/.next/standalone/src/app/api/cortex/graph/populate/route.ts +203 -203
- package/.next/standalone/src/app/api/cortex/import/route.ts +75 -43
- package/.next/standalone/src/app/api/cortex/import/status/route.ts +15 -15
- package/.next/standalone/src/app/api/cortex/ingest/bootstrap/route.ts +29 -29
- package/.next/standalone/src/app/api/cortex/ingest/status/route.ts +15 -15
- package/.next/standalone/src/app/api/cortex/knowledge/[id]/route.ts +91 -91
- package/.next/standalone/src/app/api/cortex/knowledge/route.ts +93 -93
- package/.next/standalone/src/app/api/cortex/lobes/[id]/route.ts +67 -67
- package/.next/standalone/src/app/api/cortex/lobes/route.ts +22 -22
- package/.next/standalone/src/app/api/cortex/lobes/share/route.ts +80 -80
- package/.next/standalone/src/app/api/cortex/marketplace/browse/route.ts +43 -0
- package/.next/standalone/src/app/api/cortex/marketplace/preview/route.ts +46 -0
- package/.next/standalone/src/app/api/cortex/mcp/call/route.ts +11 -11
- package/.next/standalone/src/app/api/cortex/mcp/tools/route.ts +6 -6
- package/.next/standalone/src/app/api/cortex/search/route.ts +43 -43
- package/.next/standalone/src/app/api/cortex/settings/route.ts +33 -33
- package/.next/standalone/src/app/api/cortex/status/route.ts +169 -129
- package/.next/standalone/src/app/api/cortex/timeline/route.ts +42 -42
- package/.next/standalone/src/app/api/cortex/usage/route.ts +31 -31
- package/.next/standalone/src/app/api/cortex/workspace/[id]/context/route.ts +41 -41
- package/.next/standalone/src/components/cortex/constants.ts +29 -0
- package/.next/standalone/src/components/cortex/cortex-dashboard.tsx +304 -228
- package/.next/standalone/src/components/cortex/cortex-indicator.tsx +44 -44
- package/.next/standalone/src/components/cortex/cortex-panel.tsx +140 -140
- package/.next/standalone/src/components/cortex/cortex-settings.tsx +221 -221
- package/.next/standalone/src/components/cortex/curation-tab.tsx +810 -0
- package/.next/standalone/src/components/cortex/entity-detail.tsx +101 -101
- package/.next/standalone/src/components/cortex/entity-graph.tsx +382 -382
- package/.next/standalone/src/components/cortex/import-dialog.tsx +212 -0
- package/.next/standalone/src/components/cortex/injection-badge.tsx +72 -72
- package/.next/standalone/src/components/cortex/knowledge-card.tsx +109 -127
- package/.next/standalone/src/components/cortex/knowledge-tab.tsx +158 -56
- package/.next/standalone/src/components/cortex/lobe-settings.tsx +215 -199
- package/.next/standalone/src/components/cortex/marketplace-card.tsx +126 -0
- package/.next/standalone/src/components/cortex/marketplace-tab.tsx +113 -0
- package/.next/standalone/src/lib/cortex/config.ts +40 -40
- package/.next/standalone/src/lib/cortex/debug.ts +10 -10
- package/.next/standalone/src/lib/cortex/distillation/usage-store.ts +18 -18
- package/.next/standalone/src/lib/cortex/graph/resolver.ts +10 -10
- package/.next/standalone/src/lib/cortex/graph/types.ts +22 -22
- package/.next/standalone/src/lib/cortex/index.ts +56 -56
- package/.next/standalone/src/lib/cortex/ingestion/bootstrap.ts +14 -14
- package/.next/standalone/src/lib/cortex/knowledge/compat.ts +14 -14
- package/.next/standalone/src/lib/cortex/knowledge/contradiction.ts +10 -10
- package/.next/standalone/src/lib/cortex/knowledge/types.ts +67 -67
- package/.next/standalone/src/lib/cortex/lobes/config.ts +16 -16
- package/.next/standalone/src/lib/cortex/lobes/resolver.ts +8 -8
- package/.next/standalone/src/lib/cortex/lobes/shares.ts +14 -14
- package/.next/standalone/src/lib/cortex/mcp/server.ts +8 -8
- package/.next/standalone/src/lib/cortex/portability/exporter.ts +6 -6
- package/.next/standalone/src/lib/cortex/portability/importer.ts +10 -10
- package/.next/standalone/src/lib/cortex/retrieval/context-engine.ts +10 -10
- package/.next/standalone/src/lib/cortex/types.ts +39 -38
- package/.next/standalone/tsconfig.json +34 -34
- package/LICENSE +661 -661
- package/README.md +131 -131
- package/bin/cortex-hook.sh +62 -62
- package/bin/cortex-mcp.js +60 -60
- package/bin/fix-standalone-externals.js +79 -79
- package/bin/lib/auto-setup.js +110 -110
- package/bin/mdns-service.js +171 -171
- package/bin/postinstall.js +35 -35
- package/bin/setup-admin.js +195 -195
- package/bin/spaces-dev.js +208 -208
- package/bin/spaces-install.js +599 -599
- package/bin/spaces-reset-totp.js +50 -50
- package/bin/spaces-service.js +1020 -1020
- package/bin/spaces-setup.js +253 -253
- package/bin/spaces.js +776 -728
- package/bin/ssh-auth-keys.sh +68 -68
- package/bin/terminal-server.js +1649 -1646
- package/package.json +102 -102
- package/.next/standalone/.claude/settings.local.json +0 -55
- package/.next/standalone/.claude/spaces-env.json +0 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ecce08b._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_7e63066a._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_a012c43b._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_b4db3983._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_cd50a174._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_e7a9e2b0._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_ed6c285b._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/src_133a7c8a._.js +0 -3
- package/.next/standalone/.next/static/chunks/17d164c01fa1aaa9.js +0 -5
- package/.next/standalone/.next/static/chunks/19da759dde107f02.js +0 -1
- package/.next/standalone/.next/static/chunks/58d78765e5140dcb.js +0 -1
- package/.next/standalone/.next/static/chunks/596bd56e0ad09173.js +0 -5
- package/.next/standalone/.next/static/chunks/78dd908e70bf8c4b.js +0 -85
- package/.next/standalone/.next/static/chunks/79aacab676df80c4.js +0 -1
- package/.next/standalone/.next/static/chunks/846d6ef408f69390.js +0 -1
- package/.next/standalone/.next/static/chunks/8de5e432a2fc563a.js +0 -1
- package/.next/standalone/.next/static/chunks/9196173f0d081f2c.js +0 -1
- package/.next/standalone/.next/static/chunks/9bd8a119aaeafc9e.js +0 -1
- package/.next/standalone/.next/static/chunks/e35e863c09511a51.js +0 -1
- package/.next/standalone/.next/static/chunks/f644c11ace7a0d9d.css +0 -3
- package/.next/standalone/.spaces/cortex-context.md +0 -70
- package/.next/standalone/node_modules/@img/sharp-win32-x64/lib/sharp-win32-x64.node +0 -0
- package/.next/standalone/src/components/cortex/context-tab.tsx +0 -171
- /package/.next/standalone/.next/static/{B207XFa7QvXAYS7Sqq2_4 → 77VYbwIoyxFNr5xevTrCu}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{B207XFa7QvXAYS7Sqq2_4 → 77VYbwIoyxFNr5xevTrCu}/_clientMiddlewareManifest.json +0 -0
- /package/.next/standalone/.next/static/{B207XFa7QvXAYS7Sqq2_4 → 77VYbwIoyxFNr5xevTrCu}/_ssgManifest.js +0 -0
- /package/.next/standalone/node_modules/@img/{sharp-win32-x64 → sharp-libvips-linux-x64}/versions.json +0 -0
|
@@ -1,1113 +1,1113 @@
|
|
|
1
|
-
# Cortex v2 — Pillar 2: Knowledge Unit Schema Evolution
|
|
2
|
-
|
|
3
|
-
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
-
|
|
5
|
-
**Goal:** Evolve the KnowledgeUnit schema from flat `layer` to graph-aware `scope`, add entity links, evidence tracking, sensitivity classification, and provenance — while maintaining full backward compatibility with the v1 API and existing ~542 knowledge units.
|
|
6
|
-
|
|
7
|
-
**Architecture:** The `layer` field is KEPT for backward compatibility (spec says "removed" but we intentionally keep it as a derived field to avoid breaking existing consumers — this is a documented deviation). New v2 fields (`scope`, `entity_links`, `evidence_score`, etc.) are added as nullable Arrow columns. Existing LanceDB tables are migrated in-place using LanceDB's `addColumns()` API in a migration step during `store.init()`. A compatibility layer maps v1 `layer` params to v2 `scope` in all API routes, MCP tools, and hooks.
|
|
8
|
-
|
|
9
|
-
**Tech Stack:** TypeScript, LanceDB (Arrow schema), vitest
|
|
10
|
-
|
|
11
|
-
**Spec:** `docs/superpowers/specs/2026-03-14-cortex-v2-design.md` — Pillar 2
|
|
12
|
-
|
|
13
|
-
**Depends on:** Pillar 1 (Entity Graph) — completed
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## File Structure
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
Modified files:
|
|
21
|
-
├── src/lib/cortex/knowledge/types.ts — Add v2 interfaces, keep v1 layer for compat (intentional spec deviation)
|
|
22
|
-
├── src/lib/cortex/knowledge/evidence.ts — NEW: evidence score computation
|
|
23
|
-
├── src/lib/cortex/knowledge/compat.ts — NEW: v1↔v2 layer/scope mapping
|
|
24
|
-
├── src/lib/cortex/store.ts — Evolve Arrow schema, add v2 fields, migrate existing tables
|
|
25
|
-
├── src/lib/cortex/store-migration.ts — NEW: LanceDB table schema migration (addColumns)
|
|
26
|
-
├── src/lib/cortex/retrieval/search.ts — Use scope instead of layer for weights
|
|
27
|
-
├── src/lib/cortex/retrieval/scoring.ts — Add evidence_score to formula
|
|
28
|
-
├── src/app/api/cortex/knowledge/route.ts — Accept both layer and scope params
|
|
29
|
-
├── src/lib/cortex/mcp/server.ts — Accept both layer and scope in tools
|
|
30
|
-
├── bin/cortex-learn-hook.js — Map 'personal' to scope format
|
|
31
|
-
|
|
32
|
-
Test files:
|
|
33
|
-
├── tests/lib/cortex/knowledge/evidence.test.ts — Evidence score computation
|
|
34
|
-
├── tests/lib/cortex/knowledge/compat.test.ts — v1↔v2 mapping
|
|
35
|
-
├── tests/lib/cortex/knowledge/types.test.ts — Updated for v2 types
|
|
36
|
-
├── tests/lib/cortex/store.test.ts — Updated for v2 schema
|
|
37
|
-
├── tests/lib/cortex/retrieval/search.test.ts — Updated for scope-based weights
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
## Chunk 1: New Types and Compatibility Layer
|
|
43
|
-
|
|
44
|
-
### Task 1: Add v2 type definitions
|
|
45
|
-
|
|
46
|
-
**Files:**
|
|
47
|
-
- Modify: `src/lib/cortex/knowledge/types.ts`
|
|
48
|
-
|
|
49
|
-
- [ ] **Step 1: Add new interfaces after existing ones (keep all v1 types for backward compat)**
|
|
50
|
-
|
|
51
|
-
Add these types to `src/lib/cortex/knowledge/types.ts` below the existing interfaces:
|
|
52
|
-
|
|
53
|
-
```typescript
|
|
54
|
-
// --- v2 Schema Extensions ---
|
|
55
|
-
|
|
56
|
-
export const SCOPE_LEVELS = ['personal', 'team', 'department', 'organization'] as const;
|
|
57
|
-
export type ScopeLevel = typeof SCOPE_LEVELS[number];
|
|
58
|
-
|
|
59
|
-
export interface Scope {
|
|
60
|
-
level: ScopeLevel;
|
|
61
|
-
entity_id: string; // format: {type}-{slug}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export interface EntityLink {
|
|
65
|
-
entity_id: string;
|
|
66
|
-
entity_type: EntityType; // imported from graph module
|
|
67
|
-
relation: 'created_by' | 'about' | 'scoped_to' | 'derived_from';
|
|
68
|
-
weight: number; // 0-1
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Add this import at the top of types.ts:
|
|
73
|
-
```typescript
|
|
74
|
-
import type { EntityType } from '@/lib/cortex/graph/types';
|
|
75
|
-
|
|
76
|
-
export const SENSITIVITY_CLASSES = ['public', 'internal', 'restricted', 'confidential'] as const;
|
|
77
|
-
export type SensitivityClass = typeof SENSITIVITY_CLASSES[number];
|
|
78
|
-
|
|
79
|
-
export interface ScopeOverride {
|
|
80
|
-
max_level: ScopeLevel;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export const ORIGIN_SOURCE_TYPES = [
|
|
84
|
-
'conversation', 'git_commit', 'pr_review', 'document',
|
|
85
|
-
'behavioral', 'distillation', 'manual',
|
|
86
|
-
] as const;
|
|
87
|
-
export type OriginSourceType = typeof ORIGIN_SOURCE_TYPES[number];
|
|
88
|
-
|
|
89
|
-
export interface Origin {
|
|
90
|
-
source_type: OriginSourceType;
|
|
91
|
-
source_ref: string;
|
|
92
|
-
creator_entity_id: string;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export interface PropHop {
|
|
96
|
-
from_scope: Scope;
|
|
97
|
-
to_scope: Scope;
|
|
98
|
-
reason: 'evidence_threshold' | 'policy_push' | 'manual_promote';
|
|
99
|
-
timestamp: string;
|
|
100
|
-
confidence_at_hop: number;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function isValidScopeLevel(s: string): s is ScopeLevel {
|
|
104
|
-
return SCOPE_LEVELS.includes(s as ScopeLevel);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export function isValidSensitivity(s: string): s is SensitivityClass {
|
|
108
|
-
return SENSITIVITY_CLASSES.includes(s as SensitivityClass);
|
|
109
|
-
}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
- [ ] **Step 1b: Update HOP_DECAY_FACTOR from 0.8 to 0.85**
|
|
113
|
-
|
|
114
|
-
Per spec Key Constants table, update the existing `HOP_DECAY_FACTOR` constant in types.ts from `0.8` to `0.85` (increased to preserve more signal across propagation hops).
|
|
115
|
-
|
|
116
|
-
- [ ] **Step 2: Extend KnowledgeUnit interface with optional v2 fields**
|
|
117
|
-
|
|
118
|
-
Add new optional fields to the existing `KnowledgeUnit` interface (keeping `layer` for backward compat):
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
export interface KnowledgeUnit {
|
|
122
|
-
// ... all existing v1 fields unchanged ...
|
|
123
|
-
layer: Layer; // KEPT for backward compat (derived from scope on read)
|
|
124
|
-
|
|
125
|
-
// v2 fields (optional — null/default when reading v1 data)
|
|
126
|
-
scope?: Scope;
|
|
127
|
-
entity_links?: EntityLink[];
|
|
128
|
-
evidence_score?: number;
|
|
129
|
-
corroborations?: number;
|
|
130
|
-
contradiction_refs?: string[];
|
|
131
|
-
sensitivity?: SensitivityClass;
|
|
132
|
-
creator_scope?: ScopeOverride | null;
|
|
133
|
-
origin?: Origin;
|
|
134
|
-
propagation_path?: PropHop[];
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
- [ ] **Step 3: Commit**
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
git add src/lib/cortex/knowledge/types.ts
|
|
142
|
-
git commit -m "feat(cortex): add v2 knowledge unit type definitions"
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
---
|
|
146
|
-
|
|
147
|
-
### Task 2: Create v1↔v2 compatibility layer
|
|
148
|
-
|
|
149
|
-
**Files:**
|
|
150
|
-
- Create: `src/lib/cortex/knowledge/compat.ts`
|
|
151
|
-
- Create: `tests/lib/cortex/knowledge/compat.test.ts`
|
|
152
|
-
|
|
153
|
-
- [ ] **Step 1: Write failing tests**
|
|
154
|
-
|
|
155
|
-
```typescript
|
|
156
|
-
// tests/lib/cortex/knowledge/compat.test.ts
|
|
157
|
-
import { describe, it, expect } from 'vitest';
|
|
158
|
-
import {
|
|
159
|
-
layerToScope,
|
|
160
|
-
scopeToLayer,
|
|
161
|
-
scopeToLayerKey,
|
|
162
|
-
layerKeyToScope,
|
|
163
|
-
} from '@/lib/cortex/knowledge/compat';
|
|
164
|
-
|
|
165
|
-
describe('v1↔v2 Compatibility', () => {
|
|
166
|
-
describe('layerToScope', () => {
|
|
167
|
-
it('maps personal to personal scope', () => {
|
|
168
|
-
const scope = layerToScope('personal', null, 'default-user');
|
|
169
|
-
expect(scope).toEqual({ level: 'personal', entity_id: 'person-default-user' });
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it('maps workspace to team scope', () => {
|
|
173
|
-
const scope = layerToScope('workspace', 42);
|
|
174
|
-
expect(scope).toEqual({ level: 'team', entity_id: 'team-default' });
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('maps team to organization scope', () => {
|
|
178
|
-
const scope = layerToScope('team', null);
|
|
179
|
-
expect(scope).toEqual({ level: 'organization', entity_id: 'organization-default' });
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
describe('scopeToLayer', () => {
|
|
184
|
-
it('maps personal scope to personal layer', () => {
|
|
185
|
-
expect(scopeToLayer({ level: 'personal', entity_id: 'person-alice' })).toBe('personal');
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('maps team scope to workspace layer', () => {
|
|
189
|
-
expect(scopeToLayer({ level: 'team', entity_id: 'team-platform' })).toBe('workspace');
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it('maps department scope to team layer', () => {
|
|
193
|
-
expect(scopeToLayer({ level: 'department', entity_id: 'dept-eng' })).toBe('team');
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('maps organization scope to team layer', () => {
|
|
197
|
-
expect(scopeToLayer({ level: 'organization', entity_id: 'org-acme' })).toBe('team');
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
describe('scopeToLayerKey', () => {
|
|
202
|
-
it('maps personal scope to personal key', () => {
|
|
203
|
-
expect(scopeToLayerKey({ level: 'personal', entity_id: 'person-alice' })).toBe('personal');
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it('maps team scope with workspace_id to workspace/id key', () => {
|
|
207
|
-
expect(scopeToLayerKey({ level: 'team', entity_id: 'team-platform' }, 42)).toBe('workspace/42');
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it('maps team scope without workspace_id to team key', () => {
|
|
211
|
-
expect(scopeToLayerKey({ level: 'team', entity_id: 'team-platform' })).toBe('team');
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it('maps organization scope to team key', () => {
|
|
215
|
-
expect(scopeToLayerKey({ level: 'organization', entity_id: 'org-acme' })).toBe('team');
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
describe('layerKeyToScope', () => {
|
|
220
|
-
it('maps personal key to personal scope', () => {
|
|
221
|
-
const scope = layerKeyToScope('personal', 'default-user');
|
|
222
|
-
expect(scope).toEqual({ level: 'personal', entity_id: 'person-default-user' });
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it('maps workspace/id key to team scope', () => {
|
|
226
|
-
const scope = layerKeyToScope('workspace/42');
|
|
227
|
-
expect(scope).toEqual({ level: 'team', entity_id: 'team-default' });
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it('maps team key to organization scope', () => {
|
|
231
|
-
const scope = layerKeyToScope('team');
|
|
232
|
-
expect(scope).toEqual({ level: 'organization', entity_id: 'organization-default' });
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
- [ ] **Step 2: Run tests to verify they fail**
|
|
239
|
-
|
|
240
|
-
Run: `npx vitest run tests/lib/cortex/knowledge/compat.test.ts`
|
|
241
|
-
|
|
242
|
-
- [ ] **Step 3: Implement compatibility layer**
|
|
243
|
-
|
|
244
|
-
```typescript
|
|
245
|
-
// src/lib/cortex/knowledge/compat.ts
|
|
246
|
-
import type { Layer, Scope, ScopeLevel } from './types';
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Convert v1 layer to v2 scope.
|
|
250
|
-
* personal → personal scope, workspace → team scope, team → organization scope.
|
|
251
|
-
*/
|
|
252
|
-
export function layerToScope(
|
|
253
|
-
layer: Layer,
|
|
254
|
-
workspaceId?: number | null,
|
|
255
|
-
userId?: string,
|
|
256
|
-
): Scope {
|
|
257
|
-
switch (layer) {
|
|
258
|
-
case 'personal':
|
|
259
|
-
return { level: 'personal', entity_id: `person-${userId ?? 'default-user'}` };
|
|
260
|
-
case 'workspace':
|
|
261
|
-
return { level: 'team', entity_id: 'team-default' };
|
|
262
|
-
case 'team':
|
|
263
|
-
return { level: 'organization', entity_id: 'organization-default' };
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Convert v2 scope back to v1 layer (for backward compat).
|
|
269
|
-
* personal → personal, team → workspace, department/organization → team.
|
|
270
|
-
*/
|
|
271
|
-
export function scopeToLayer(scope: Scope): Layer {
|
|
272
|
-
switch (scope.level) {
|
|
273
|
-
case 'personal': return 'personal';
|
|
274
|
-
case 'team': return 'workspace';
|
|
275
|
-
case 'department':
|
|
276
|
-
case 'organization': return 'team';
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Convert v2 scope to a LanceDB layer key (storage path).
|
|
282
|
-
* Maintains compatibility with existing store.layerPath() logic.
|
|
283
|
-
*/
|
|
284
|
-
export function scopeToLayerKey(scope: Scope, workspaceId?: number | null): string {
|
|
285
|
-
switch (scope.level) {
|
|
286
|
-
case 'personal': return 'personal';
|
|
287
|
-
case 'team':
|
|
288
|
-
return workspaceId ? `workspace/${workspaceId}` : 'team';
|
|
289
|
-
case 'department':
|
|
290
|
-
case 'organization':
|
|
291
|
-
return 'team';
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Convert a LanceDB layer key back to a v2 scope.
|
|
297
|
-
*/
|
|
298
|
-
export function layerKeyToScope(layerKey: string, userId?: string): Scope {
|
|
299
|
-
if (layerKey === 'personal') {
|
|
300
|
-
return { level: 'personal', entity_id: `person-${userId ?? 'default-user'}` };
|
|
301
|
-
}
|
|
302
|
-
if (layerKey.startsWith('workspace/')) {
|
|
303
|
-
return { level: 'team', entity_id: 'team-default' };
|
|
304
|
-
}
|
|
305
|
-
// 'team' key → organization scope
|
|
306
|
-
return { level: 'organization', entity_id: 'organization-default' };
|
|
307
|
-
}
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
- [ ] **Step 4: Run tests to verify they pass**
|
|
311
|
-
|
|
312
|
-
Run: `npx vitest run tests/lib/cortex/knowledge/compat.test.ts`
|
|
313
|
-
Expected: PASS (10 tests)
|
|
314
|
-
|
|
315
|
-
- [ ] **Step 5: Commit**
|
|
316
|
-
|
|
317
|
-
```bash
|
|
318
|
-
git add src/lib/cortex/knowledge/compat.ts tests/lib/cortex/knowledge/compat.test.ts
|
|
319
|
-
git commit -m "feat(cortex): add v1↔v2 layer/scope compatibility mapping"
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
---
|
|
323
|
-
|
|
324
|
-
### Task 3: Evidence score computation
|
|
325
|
-
|
|
326
|
-
**Files:**
|
|
327
|
-
- Create: `src/lib/cortex/knowledge/evidence.ts`
|
|
328
|
-
- Create: `tests/lib/cortex/knowledge/evidence.test.ts`
|
|
329
|
-
|
|
330
|
-
- [ ] **Step 1: Write failing tests**
|
|
331
|
-
|
|
332
|
-
```typescript
|
|
333
|
-
// tests/lib/cortex/knowledge/evidence.test.ts
|
|
334
|
-
import { describe, it, expect } from 'vitest';
|
|
335
|
-
import { computeEvidenceScore, AUTHORITY_FACTORS } from '@/lib/cortex/knowledge/evidence';
|
|
336
|
-
|
|
337
|
-
describe('computeEvidenceScore', () => {
|
|
338
|
-
it('returns base confidence for fresh unit with no interactions', () => {
|
|
339
|
-
const score = computeEvidenceScore({
|
|
340
|
-
baseConfidence: 0.8,
|
|
341
|
-
corroborations: 0,
|
|
342
|
-
accessCount: 0,
|
|
343
|
-
authorityFactor: 1.0,
|
|
344
|
-
contradictionCount: 0,
|
|
345
|
-
});
|
|
346
|
-
expect(score).toBeCloseTo(0.8);
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
it('increases with corroborations', () => {
|
|
350
|
-
const base = computeEvidenceScore({
|
|
351
|
-
baseConfidence: 0.8, corroborations: 0, accessCount: 0, authorityFactor: 1.0, contradictionCount: 0,
|
|
352
|
-
});
|
|
353
|
-
const withCorr = computeEvidenceScore({
|
|
354
|
-
baseConfidence: 0.8, corroborations: 3, accessCount: 0, authorityFactor: 1.0, contradictionCount: 0,
|
|
355
|
-
});
|
|
356
|
-
expect(withCorr).toBeGreaterThan(base);
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
it('increases with access count (diminishing returns)', () => {
|
|
360
|
-
const low = computeEvidenceScore({
|
|
361
|
-
baseConfidence: 0.8, corroborations: 0, accessCount: 5, authorityFactor: 1.0, contradictionCount: 0,
|
|
362
|
-
});
|
|
363
|
-
const high = computeEvidenceScore({
|
|
364
|
-
baseConfidence: 0.8, corroborations: 0, accessCount: 50, authorityFactor: 1.0, contradictionCount: 0,
|
|
365
|
-
});
|
|
366
|
-
expect(high).toBeGreaterThan(low);
|
|
367
|
-
// Capped at 50 — going higher has no effect
|
|
368
|
-
const over = computeEvidenceScore({
|
|
369
|
-
baseConfidence: 0.8, corroborations: 0, accessCount: 100, authorityFactor: 1.0, contradictionCount: 0,
|
|
370
|
-
});
|
|
371
|
-
expect(over).toBeCloseTo(high);
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
it('decreases with contradictions', () => {
|
|
375
|
-
const clean = computeEvidenceScore({
|
|
376
|
-
baseConfidence: 0.8, corroborations: 2, accessCount: 10, authorityFactor: 1.0, contradictionCount: 0,
|
|
377
|
-
});
|
|
378
|
-
const contested = computeEvidenceScore({
|
|
379
|
-
baseConfidence: 0.8, corroborations: 2, accessCount: 10, authorityFactor: 1.0, contradictionCount: 2,
|
|
380
|
-
});
|
|
381
|
-
expect(contested).toBeLessThan(clean);
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
it('caps corroboration contribution at 10', () => {
|
|
385
|
-
const at10 = computeEvidenceScore({
|
|
386
|
-
baseConfidence: 0.8, corroborations: 10, accessCount: 0, authorityFactor: 1.0, contradictionCount: 0,
|
|
387
|
-
});
|
|
388
|
-
const at20 = computeEvidenceScore({
|
|
389
|
-
baseConfidence: 0.8, corroborations: 20, accessCount: 0, authorityFactor: 1.0, contradictionCount: 0,
|
|
390
|
-
});
|
|
391
|
-
expect(at20).toBeCloseTo(at10); // no additional benefit beyond 10
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
it('is boosted by authority factor', () => {
|
|
395
|
-
const conversation = computeEvidenceScore({
|
|
396
|
-
baseConfidence: 0.8, corroborations: 0, accessCount: 0, authorityFactor: AUTHORITY_FACTORS.conversation, contradictionCount: 0,
|
|
397
|
-
});
|
|
398
|
-
const document = computeEvidenceScore({
|
|
399
|
-
baseConfidence: 0.8, corroborations: 0, accessCount: 0, authorityFactor: AUTHORITY_FACTORS.document, contradictionCount: 0,
|
|
400
|
-
});
|
|
401
|
-
expect(document).toBeGreaterThan(conversation);
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
it('is capped at 1.0', () => {
|
|
405
|
-
const score = computeEvidenceScore({
|
|
406
|
-
baseConfidence: 0.95, corroborations: 10, accessCount: 50, authorityFactor: 1.3, contradictionCount: 0,
|
|
407
|
-
});
|
|
408
|
-
expect(score).toBeLessThanOrEqual(1.0);
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
it('never goes below 0', () => {
|
|
412
|
-
const score = computeEvidenceScore({
|
|
413
|
-
baseConfidence: 0.1, corroborations: 0, accessCount: 0, authorityFactor: 1.0, contradictionCount: 10,
|
|
414
|
-
});
|
|
415
|
-
expect(score).toBeGreaterThanOrEqual(0);
|
|
416
|
-
});
|
|
417
|
-
});
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
- [ ] **Step 2: Run tests to verify they fail**
|
|
421
|
-
|
|
422
|
-
Run: `npx vitest run tests/lib/cortex/knowledge/evidence.test.ts`
|
|
423
|
-
|
|
424
|
-
- [ ] **Step 3: Implement evidence score**
|
|
425
|
-
|
|
426
|
-
```typescript
|
|
427
|
-
// src/lib/cortex/knowledge/evidence.ts
|
|
428
|
-
|
|
429
|
-
export const AUTHORITY_FACTORS = {
|
|
430
|
-
conversation: 1.0,
|
|
431
|
-
git_commit: 1.1,
|
|
432
|
-
pr_review: 1.1,
|
|
433
|
-
document: 1.2,
|
|
434
|
-
behavioral: 1.0,
|
|
435
|
-
distillation: 1.1,
|
|
436
|
-
manual: 1.3,
|
|
437
|
-
} as const;
|
|
438
|
-
|
|
439
|
-
export interface EvidenceScoreInput {
|
|
440
|
-
baseConfidence: number;
|
|
441
|
-
corroborations: number;
|
|
442
|
-
accessCount: number;
|
|
443
|
-
authorityFactor: number;
|
|
444
|
-
contradictionCount: number;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* Compute evidence score (0-1) from knowledge unit metrics.
|
|
449
|
-
*
|
|
450
|
-
* Formula from spec:
|
|
451
|
-
* evidence_score = min(1.0,
|
|
452
|
-
* base_confidence
|
|
453
|
-
* × (1 + 0.1 × corroborations)
|
|
454
|
-
* × (1 + 0.01 × min(access_count, 50))
|
|
455
|
-
* × authority_factor
|
|
456
|
-
* ÷ (1 + 0.5 × contradiction_count)
|
|
457
|
-
* )
|
|
458
|
-
*/
|
|
459
|
-
export function computeEvidenceScore(input: EvidenceScoreInput): number {
|
|
460
|
-
const { baseConfidence, corroborations, accessCount, authorityFactor, contradictionCount } = input;
|
|
461
|
-
|
|
462
|
-
const corroborationBoost = 1 + 0.1 * Math.min(corroborations, 10);
|
|
463
|
-
const accessBoost = 1 + 0.01 * Math.min(accessCount, 50);
|
|
464
|
-
const contradictionPenalty = 1 + 0.5 * contradictionCount;
|
|
465
|
-
|
|
466
|
-
const raw = (baseConfidence * corroborationBoost * accessBoost * authorityFactor) / contradictionPenalty;
|
|
467
|
-
|
|
468
|
-
return Math.max(0, Math.min(1.0, raw));
|
|
469
|
-
}
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
- [ ] **Step 4: Run tests to verify they pass**
|
|
473
|
-
|
|
474
|
-
Run: `npx vitest run tests/lib/cortex/knowledge/evidence.test.ts`
|
|
475
|
-
Expected: PASS (7 tests)
|
|
476
|
-
|
|
477
|
-
- [ ] **Step 5: Commit**
|
|
478
|
-
|
|
479
|
-
```bash
|
|
480
|
-
git add src/lib/cortex/knowledge/evidence.ts tests/lib/cortex/knowledge/evidence.test.ts
|
|
481
|
-
git commit -m "feat(cortex): add evidence score computation"
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
---
|
|
485
|
-
|
|
486
|
-
## Chunk 2: LanceDB Schema Evolution and Store Updates
|
|
487
|
-
|
|
488
|
-
### Task 4: Evolve Arrow schema with v2 fields and table migration
|
|
489
|
-
|
|
490
|
-
**Files:**
|
|
491
|
-
- Create: `src/lib/cortex/store-migration.ts`
|
|
492
|
-
- Modify: `src/lib/cortex/store.ts`
|
|
493
|
-
- Modify: `tests/lib/cortex/store.test.ts`
|
|
494
|
-
|
|
495
|
-
> **CRITICAL:** LanceDB is strict about schema — you cannot add records with columns that don't exist in the table's schema. Existing v1 tables lack the 9 new v2 columns. We must migrate existing tables by adding the new columns before any v2 writes.
|
|
496
|
-
|
|
497
|
-
- [ ] **Step 1: Read the current store.ts**
|
|
498
|
-
|
|
499
|
-
Read `src/lib/cortex/store.ts` to understand:
|
|
500
|
-
- The `buildSchema(dimensions)` function (Arrow field definitions)
|
|
501
|
-
- The `unitToRecord` serialization
|
|
502
|
-
- The deserialization in `search()` and `browse()`
|
|
503
|
-
- The `updateAccessCount()` method (delete + re-add pattern)
|
|
504
|
-
- How `getConnection()` opens tables
|
|
505
|
-
|
|
506
|
-
- [ ] **Step 1b: Create store-migration.ts**
|
|
507
|
-
|
|
508
|
-
```typescript
|
|
509
|
-
// src/lib/cortex/store-migration.ts
|
|
510
|
-
import type { Table } from '@lancedb/lancedb';
|
|
511
|
-
|
|
512
|
-
/**
|
|
513
|
-
* V2 columns to add to existing LanceDB tables.
|
|
514
|
-
* Each entry: [column_name, sql_expression_for_default_value]
|
|
515
|
-
*/
|
|
516
|
-
const V2_COLUMNS: [string, string][] = [
|
|
517
|
-
['scope', "'null'"], // JSON string, null for v1 data
|
|
518
|
-
['entity_links', "'[]'"], // JSON array string
|
|
519
|
-
['evidence_score', '0.5'], // float default
|
|
520
|
-
['corroborations', '0'], // int default
|
|
521
|
-
['contradiction_refs', "'[]'"], // JSON array string
|
|
522
|
-
['sensitivity', "'internal'"], // string default
|
|
523
|
-
['creator_scope', "'null'"], // JSON string, null
|
|
524
|
-
['origin', "'null'"], // JSON string, null
|
|
525
|
-
['propagation_path', "'[]'"], // JSON array string
|
|
526
|
-
];
|
|
527
|
-
|
|
528
|
-
/**
|
|
529
|
-
* Migrate a LanceDB table to v2 schema by adding missing columns.
|
|
530
|
-
* Safe to call repeatedly — checks column existence before adding.
|
|
531
|
-
*/
|
|
532
|
-
export async function migrateTableToV2(table: Table): Promise<void> {
|
|
533
|
-
const schema = await table.schema;
|
|
534
|
-
const existingFields = new Set(schema.fields.map(f => f.name));
|
|
535
|
-
|
|
536
|
-
for (const [colName, defaultExpr] of V2_COLUMNS) {
|
|
537
|
-
if (!existingFields.has(colName)) {
|
|
538
|
-
try {
|
|
539
|
-
await table.addColumns([{ name: colName, valueSql: defaultExpr }]);
|
|
540
|
-
} catch {
|
|
541
|
-
// Column may have been added concurrently, ignore
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
This uses LanceDB's `table.addColumns()` API to add nullable columns with default values to existing tables in-place, without needing to read/drop/recreate.
|
|
549
|
-
|
|
550
|
-
- [ ] **Step 2: Write a failing test for v2 fields**
|
|
551
|
-
|
|
552
|
-
Add to `tests/lib/cortex/store.test.ts`:
|
|
553
|
-
|
|
554
|
-
```typescript
|
|
555
|
-
describe('CortexStore — v2 fields', () => {
|
|
556
|
-
let tmpDir: string;
|
|
557
|
-
let store: CortexStore;
|
|
558
|
-
|
|
559
|
-
beforeEach(async () => {
|
|
560
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cortex-store-'));
|
|
561
|
-
store = new CortexStore(tmpDir);
|
|
562
|
-
await store.init(384);
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
afterEach(async () => {
|
|
566
|
-
await store.close();
|
|
567
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
it('stores and retrieves v2 fields', async () => {
|
|
571
|
-
const vector = new Array(384).fill(0).map(() => Math.random());
|
|
572
|
-
const unit = {
|
|
573
|
-
id: 'v2-test-1',
|
|
574
|
-
vector,
|
|
575
|
-
text: 'Auth uses JWT with refresh tokens',
|
|
576
|
-
type: 'decision' as const,
|
|
577
|
-
layer: 'personal' as const,
|
|
578
|
-
workspace_id: null,
|
|
579
|
-
session_id: 'sess-1',
|
|
580
|
-
agent_type: 'claude' as const,
|
|
581
|
-
project_path: '/project',
|
|
582
|
-
file_refs: ['src/auth.ts'],
|
|
583
|
-
confidence: 0.85,
|
|
584
|
-
created: new Date().toISOString(),
|
|
585
|
-
source_timestamp: new Date().toISOString(),
|
|
586
|
-
stale_score: 0,
|
|
587
|
-
access_count: 0,
|
|
588
|
-
last_accessed: null,
|
|
589
|
-
metadata: {},
|
|
590
|
-
// v2 fields
|
|
591
|
-
scope: { level: 'personal' as const, entity_id: 'person-alice' },
|
|
592
|
-
entity_links: [
|
|
593
|
-
{ entity_id: 'topic-auth', entity_type: 'topic', relation: 'about' as const, weight: 0.9 },
|
|
594
|
-
],
|
|
595
|
-
evidence_score: 0.72,
|
|
596
|
-
corroborations: 2,
|
|
597
|
-
contradiction_refs: ['other-id-1'],
|
|
598
|
-
sensitivity: 'internal' as const,
|
|
599
|
-
creator_scope: null,
|
|
600
|
-
origin: { source_type: 'conversation' as const, source_ref: 'sess-1', creator_entity_id: 'person-alice' },
|
|
601
|
-
propagation_path: [],
|
|
602
|
-
};
|
|
603
|
-
|
|
604
|
-
await store.add('personal', unit);
|
|
605
|
-
const results = await store.search('personal', vector, 5);
|
|
606
|
-
expect(results).toHaveLength(1);
|
|
607
|
-
|
|
608
|
-
const result = results[0];
|
|
609
|
-
expect(result.scope).toEqual({ level: 'personal', entity_id: 'person-alice' });
|
|
610
|
-
expect(result.entity_links).toHaveLength(1);
|
|
611
|
-
expect(result.entity_links![0].entity_id).toBe('topic-auth');
|
|
612
|
-
expect(result.evidence_score).toBeCloseTo(0.72);
|
|
613
|
-
expect(result.corroborations).toBe(2);
|
|
614
|
-
expect(result.contradiction_refs).toEqual(['other-id-1']);
|
|
615
|
-
expect(result.sensitivity).toBe('internal');
|
|
616
|
-
expect(result.origin?.source_type).toBe('conversation');
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
it('reads v1 data with default v2 fields', async () => {
|
|
620
|
-
const vector = new Array(384).fill(0).map(() => Math.random());
|
|
621
|
-
// Store a unit WITHOUT v2 fields (simulating v1 data)
|
|
622
|
-
const unit = {
|
|
623
|
-
id: 'v1-test-1',
|
|
624
|
-
vector,
|
|
625
|
-
text: 'Old v1 knowledge',
|
|
626
|
-
type: 'context' as const,
|
|
627
|
-
layer: 'personal' as const,
|
|
628
|
-
workspace_id: null,
|
|
629
|
-
session_id: null,
|
|
630
|
-
agent_type: 'claude' as const,
|
|
631
|
-
project_path: null,
|
|
632
|
-
file_refs: [],
|
|
633
|
-
confidence: 0.6,
|
|
634
|
-
created: new Date().toISOString(),
|
|
635
|
-
source_timestamp: new Date().toISOString(),
|
|
636
|
-
stale_score: 0,
|
|
637
|
-
access_count: 0,
|
|
638
|
-
last_accessed: null,
|
|
639
|
-
metadata: {},
|
|
640
|
-
};
|
|
641
|
-
|
|
642
|
-
await store.add('personal', unit);
|
|
643
|
-
const results = await store.search('personal', vector, 5);
|
|
644
|
-
const result = results[0];
|
|
645
|
-
|
|
646
|
-
// v2 fields should have sensible defaults
|
|
647
|
-
expect(result.evidence_score).toBe(0.5);
|
|
648
|
-
expect(result.corroborations).toBe(0);
|
|
649
|
-
expect(result.contradiction_refs).toEqual([]);
|
|
650
|
-
expect(result.sensitivity).toBe('internal');
|
|
651
|
-
expect(result.entity_links).toEqual([]);
|
|
652
|
-
expect(result.propagation_path).toEqual([]);
|
|
653
|
-
});
|
|
654
|
-
});
|
|
655
|
-
```
|
|
656
|
-
|
|
657
|
-
- [ ] **Step 3: Run tests to verify they fail**
|
|
658
|
-
|
|
659
|
-
Run: `npx vitest run tests/lib/cortex/store.test.ts`
|
|
660
|
-
|
|
661
|
-
- [ ] **Step 4: Evolve the Arrow schema**
|
|
662
|
-
|
|
663
|
-
In `src/lib/cortex/store.ts`, add new Arrow fields to the schema builder function. These are all nullable Utf8 fields (JSON-serialized for complex types) or nullable Float64/Int32 for numbers:
|
|
664
|
-
|
|
665
|
-
```typescript
|
|
666
|
-
// Add after existing fields in the schema:
|
|
667
|
-
new arrow.Field('scope', new arrow.Utf8(), true), // JSON: { level, entity_id }
|
|
668
|
-
new arrow.Field('entity_links', new arrow.Utf8(), true), // JSON array
|
|
669
|
-
new arrow.Field('evidence_score', new arrow.Float64(), true), // nullable
|
|
670
|
-
new arrow.Field('corroborations', new arrow.Int32(), true), // nullable
|
|
671
|
-
new arrow.Field('contradiction_refs', new arrow.Utf8(), true), // JSON array
|
|
672
|
-
new arrow.Field('sensitivity', new arrow.Utf8(), true), // nullable
|
|
673
|
-
new arrow.Field('creator_scope', new arrow.Utf8(), true), // JSON or null
|
|
674
|
-
new arrow.Field('origin', new arrow.Utf8(), true), // JSON or null
|
|
675
|
-
new arrow.Field('propagation_path', new arrow.Utf8(), true), // JSON array
|
|
676
|
-
```
|
|
677
|
-
|
|
678
|
-
In the `add()` method, after opening/creating the table but before `table.add([record])`, call migration:
|
|
679
|
-
```typescript
|
|
680
|
-
import { migrateTableToV2 } from './store-migration';
|
|
681
|
-
|
|
682
|
-
// In add(), after getting/creating the table:
|
|
683
|
-
await migrateTableToV2(table);
|
|
684
|
-
```
|
|
685
|
-
|
|
686
|
-
Also call `migrateTableToV2` in `search()` and `browse()` after getting the table, to ensure reads from v1 tables also get the new columns. Cache a Set of already-migrated table paths to avoid repeated migration checks.
|
|
687
|
-
|
|
688
|
-
Update the serialization (unitToRecord) to include v2 fields:
|
|
689
|
-
```typescript
|
|
690
|
-
scope: unit.scope ? JSON.stringify(unit.scope) : null,
|
|
691
|
-
entity_links: JSON.stringify(unit.entity_links ?? []),
|
|
692
|
-
evidence_score: unit.evidence_score ?? 0.5,
|
|
693
|
-
corroborations: unit.corroborations ?? 0,
|
|
694
|
-
contradiction_refs: JSON.stringify(unit.contradiction_refs ?? []),
|
|
695
|
-
sensitivity: unit.sensitivity ?? 'internal',
|
|
696
|
-
creator_scope: unit.creator_scope ? JSON.stringify(unit.creator_scope) : null,
|
|
697
|
-
origin: unit.origin ? JSON.stringify(unit.origin) : null,
|
|
698
|
-
propagation_path: JSON.stringify(unit.propagation_path ?? []),
|
|
699
|
-
```
|
|
700
|
-
|
|
701
|
-
**CRITICAL: Also update `updateAccessCount()`** — this method uses a delete+re-add pattern that reconstructs the record from raw fields. It must include all v2 fields in the reconstruction to avoid dropping them:
|
|
702
|
-
|
|
703
|
-
```typescript
|
|
704
|
-
// In updateAccessCount(), when reconstructing the record, add:
|
|
705
|
-
scope: raw.scope ?? null,
|
|
706
|
-
entity_links: raw.entity_links ?? '[]',
|
|
707
|
-
evidence_score: raw.evidence_score ?? 0.5,
|
|
708
|
-
corroborations: raw.corroborations ?? 0,
|
|
709
|
-
contradiction_refs: raw.contradiction_refs ?? '[]',
|
|
710
|
-
sensitivity: raw.sensitivity ?? 'internal',
|
|
711
|
-
creator_scope: raw.creator_scope ?? null,
|
|
712
|
-
origin: raw.origin ?? null,
|
|
713
|
-
propagation_path: raw.propagation_path ?? '[]',
|
|
714
|
-
```
|
|
715
|
-
|
|
716
|
-
**Also update `browse()` deserialization** — `browse()` has its own row-mapping logic separate from `search()`. Apply the same v2 field parsing to `browse()` results.
|
|
717
|
-
|
|
718
|
-
Update deserialization in BOTH `search()` and `browse()` to parse v2 fields with defaults:
|
|
719
|
-
```typescript
|
|
720
|
-
scope: row.scope ? JSON.parse(row.scope) : undefined,
|
|
721
|
-
entity_links: JSON.parse(row.entity_links || '[]'),
|
|
722
|
-
evidence_score: row.evidence_score ?? 0.5,
|
|
723
|
-
corroborations: row.corroborations ?? 0,
|
|
724
|
-
contradiction_refs: JSON.parse(row.contradiction_refs || '[]'),
|
|
725
|
-
sensitivity: row.sensitivity || 'internal',
|
|
726
|
-
creator_scope: row.creator_scope ? JSON.parse(row.creator_scope) : null,
|
|
727
|
-
origin: row.origin ? JSON.parse(row.origin) : undefined,
|
|
728
|
-
propagation_path: JSON.parse(row.propagation_path || '[]'),
|
|
729
|
-
```
|
|
730
|
-
|
|
731
|
-
- [ ] **Step 5: Run tests to verify they pass**
|
|
732
|
-
|
|
733
|
-
Run: `npx vitest run tests/lib/cortex/store.test.ts`
|
|
734
|
-
Expected: All tests pass (existing + 2 new)
|
|
735
|
-
|
|
736
|
-
- [ ] **Step 6: Commit**
|
|
737
|
-
|
|
738
|
-
```bash
|
|
739
|
-
git add src/lib/cortex/store.ts tests/lib/cortex/store.test.ts
|
|
740
|
-
git commit -m "feat(cortex): evolve LanceDB schema with v2 knowledge unit fields"
|
|
741
|
-
```
|
|
742
|
-
|
|
743
|
-
---
|
|
744
|
-
|
|
745
|
-
### Task 5: Update search and scoring for v2
|
|
746
|
-
|
|
747
|
-
**Files:**
|
|
748
|
-
- Modify: `src/lib/cortex/retrieval/scoring.ts`
|
|
749
|
-
- Modify: `src/lib/cortex/retrieval/search.ts`
|
|
750
|
-
- Modify: `tests/lib/cortex/retrieval/search.test.ts`
|
|
751
|
-
|
|
752
|
-
- [ ] **Step 1: Read current search.ts and scoring.ts**
|
|
753
|
-
|
|
754
|
-
Read both files to understand the current scoring formula and search layer iteration.
|
|
755
|
-
|
|
756
|
-
- [ ] **Step 2: Write a failing test for evidence-aware scoring**
|
|
757
|
-
|
|
758
|
-
Add to `tests/lib/cortex/retrieval/scoring.test.ts` (or the existing scoring test file):
|
|
759
|
-
|
|
760
|
-
```typescript
|
|
761
|
-
import { computeRelevanceScore } from '@/lib/cortex/retrieval/scoring';
|
|
762
|
-
|
|
763
|
-
it('uses evidence_score when provided instead of confidence', () => {
|
|
764
|
-
const withConfidenceOnly = computeRelevanceScore({
|
|
765
|
-
similarity: 0.9, confidence: 0.8, stale_score: 0, created: new Date().toISOString(),
|
|
766
|
-
});
|
|
767
|
-
const withHighEvidence = computeRelevanceScore({
|
|
768
|
-
similarity: 0.9, confidence: 0.8, stale_score: 0, created: new Date().toISOString(),
|
|
769
|
-
evidence_score: 0.95,
|
|
770
|
-
});
|
|
771
|
-
const withLowEvidence = computeRelevanceScore({
|
|
772
|
-
similarity: 0.9, confidence: 0.8, stale_score: 0, created: new Date().toISOString(),
|
|
773
|
-
evidence_score: 0.3,
|
|
774
|
-
});
|
|
775
|
-
|
|
776
|
-
// evidence_score should override confidence in the formula
|
|
777
|
-
expect(withHighEvidence).toBeGreaterThan(withConfidenceOnly);
|
|
778
|
-
expect(withLowEvidence).toBeLessThan(withConfidenceOnly);
|
|
779
|
-
});
|
|
780
|
-
|
|
781
|
-
it('falls back to confidence when evidence_score is undefined', () => {
|
|
782
|
-
const result = computeRelevanceScore({
|
|
783
|
-
similarity: 0.9, confidence: 0.8, stale_score: 0, created: new Date().toISOString(),
|
|
784
|
-
});
|
|
785
|
-
// Should use confidence (0.8) as the evidence factor
|
|
786
|
-
expect(result).toBeGreaterThan(0);
|
|
787
|
-
});
|
|
788
|
-
```
|
|
789
|
-
|
|
790
|
-
- [ ] **Step 3: Update scoring.ts to incorporate evidence_score**
|
|
791
|
-
|
|
792
|
-
Add `evidence_score` as an optional parameter to `computeRelevanceScore`:
|
|
793
|
-
|
|
794
|
-
```typescript
|
|
795
|
-
export function computeRelevanceScore(params: {
|
|
796
|
-
similarity: number;
|
|
797
|
-
confidence: number;
|
|
798
|
-
stale_score: number;
|
|
799
|
-
created: string;
|
|
800
|
-
evidence_score?: number; // NEW: v2 evidence score
|
|
801
|
-
}): number {
|
|
802
|
-
const recencyBoost = computeRecencyBoost(params.created);
|
|
803
|
-
const evidence = params.evidence_score ?? params.confidence; // fallback to confidence for v1 data
|
|
804
|
-
return Math.min(1.0, params.similarity * evidence * (1 - params.stale_score) * recencyBoost);
|
|
805
|
-
}
|
|
806
|
-
```
|
|
807
|
-
|
|
808
|
-
- [ ] **Step 4: Update search.ts to pass evidence_score in ALL scoring calls**
|
|
809
|
-
|
|
810
|
-
In the search method, pass `evidence_score` in BOTH the initial scoring AND the staleness recomputation:
|
|
811
|
-
|
|
812
|
-
```typescript
|
|
813
|
-
// Initial scoring (in the per-layer loop):
|
|
814
|
-
const relevance = computeRelevanceScore({
|
|
815
|
-
similarity,
|
|
816
|
-
confidence: unit.confidence,
|
|
817
|
-
stale_score: unit.stale_score,
|
|
818
|
-
created: unit.created,
|
|
819
|
-
evidence_score: unit.evidence_score, // NEW
|
|
820
|
-
}) * weight;
|
|
821
|
-
```
|
|
822
|
-
|
|
823
|
-
Also find the staleness recomputation block (where `computeStaleness` results are used to re-score) and pass `evidence_score` there too:
|
|
824
|
-
```typescript
|
|
825
|
-
// After staleness recomputation:
|
|
826
|
-
r.relevance_score = computeRelevanceScore({
|
|
827
|
-
similarity: r.similarity,
|
|
828
|
-
confidence: r.confidence,
|
|
829
|
-
stale_score: r.stale_score,
|
|
830
|
-
created: r.created,
|
|
831
|
-
evidence_score: r.evidence_score, // NEW — must be here too
|
|
832
|
-
}) * (LAYER_WEIGHTS[r.layer] ?? 0.5);
|
|
833
|
-
```
|
|
834
|
-
|
|
835
|
-
- [ ] **Step 5: Run tests to verify they pass**
|
|
836
|
-
|
|
837
|
-
Run: `npx vitest run tests/lib/cortex/retrieval/`
|
|
838
|
-
Expected: All tests pass
|
|
839
|
-
|
|
840
|
-
- [ ] **Step 6: Commit**
|
|
841
|
-
|
|
842
|
-
```bash
|
|
843
|
-
git add src/lib/cortex/retrieval/scoring.ts src/lib/cortex/retrieval/search.ts tests/lib/cortex/retrieval/search.test.ts
|
|
844
|
-
git commit -m "feat(cortex): incorporate evidence_score into retrieval scoring"
|
|
845
|
-
```
|
|
846
|
-
|
|
847
|
-
---
|
|
848
|
-
|
|
849
|
-
## Chunk 3: API Backward Compatibility
|
|
850
|
-
|
|
851
|
-
### Task 6: Update knowledge API to accept both layer and scope
|
|
852
|
-
|
|
853
|
-
**Files:**
|
|
854
|
-
- Modify: `src/app/api/cortex/knowledge/route.ts`
|
|
855
|
-
|
|
856
|
-
- [ ] **Step 1: Read current knowledge/route.ts**
|
|
857
|
-
|
|
858
|
-
- [ ] **Step 2: Update POST handler to accept both formats**
|
|
859
|
-
|
|
860
|
-
The POST handler currently requires `layer`. Update it to:
|
|
861
|
-
1. Accept `scope` as an alternative to `layer`
|
|
862
|
-
2. If `scope` is provided, use it directly and derive `layer` for backward compat
|
|
863
|
-
3. If only `layer` is provided, derive `scope` from it
|
|
864
|
-
4. Accept new v2 fields: `sensitivity`, `origin`, `entity_links`
|
|
865
|
-
|
|
866
|
-
```typescript
|
|
867
|
-
// In POST handler, after parsing body:
|
|
868
|
-
const { text, type, workspace_id } = body;
|
|
869
|
-
let { layer, scope, sensitivity, origin, entity_links } = body;
|
|
870
|
-
|
|
871
|
-
// Validate: need either layer or scope
|
|
872
|
-
if (!text || !type) {
|
|
873
|
-
return NextResponse.json({ error: 'text and type are required' }, { status: 400 });
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
if (!layer && !scope) {
|
|
877
|
-
return NextResponse.json({ error: 'layer or scope is required' }, { status: 400 });
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
// Resolve layer ↔ scope
|
|
881
|
-
if (scope && !layer) {
|
|
882
|
-
layer = scopeToLayer(scope);
|
|
883
|
-
} else if (layer && !scope) {
|
|
884
|
-
scope = layerToScope(layer, workspace_id);
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
// Compute layerKey from scope (or fallback to old method)
|
|
888
|
-
const layerKey = scope
|
|
889
|
-
? scopeToLayerKey(scope, workspace_id)
|
|
890
|
-
: (layer === 'workspace' && workspace_id ? `workspace/${workspace_id}` : layer);
|
|
891
|
-
```
|
|
892
|
-
|
|
893
|
-
Import the compat functions:
|
|
894
|
-
```typescript
|
|
895
|
-
import { layerToScope, scopeToLayer, scopeToLayerKey } from '@/lib/cortex/knowledge/compat';
|
|
896
|
-
```
|
|
897
|
-
|
|
898
|
-
Add v2 fields to the unit construction. Use `getConfidenceBase(type)` for initial evidence_score (not a flat 0.5 — per spec, evidence starts from the type's base confidence). Derive `creator_entity_id` from the authenticated user:
|
|
899
|
-
|
|
900
|
-
```typescript
|
|
901
|
-
import { getConfidenceBase } from '@/lib/cortex/knowledge/types';
|
|
902
|
-
|
|
903
|
-
// In unit construction:
|
|
904
|
-
scope,
|
|
905
|
-
entity_links: entity_links ?? [],
|
|
906
|
-
evidence_score: getConfidenceBase(type), // NOT 0.5 — matches spec
|
|
907
|
-
corroborations: 0,
|
|
908
|
-
contradiction_refs: [],
|
|
909
|
-
sensitivity: sensitivity ?? 'internal',
|
|
910
|
-
creator_scope: null,
|
|
911
|
-
origin: origin ?? { source_type: 'manual', source_ref: '', creator_entity_id: `person-${user}` },
|
|
912
|
-
propagation_path: [],
|
|
913
|
-
```
|
|
914
|
-
|
|
915
|
-
Note: `user` is already available from `getAuthUser(request)` at the top of the handler.
|
|
916
|
-
|
|
917
|
-
- [ ] **Step 3: Commit**
|
|
918
|
-
|
|
919
|
-
```bash
|
|
920
|
-
git add src/app/api/cortex/knowledge/route.ts
|
|
921
|
-
git commit -m "feat(cortex): accept both layer and scope in knowledge API"
|
|
922
|
-
```
|
|
923
|
-
|
|
924
|
-
---
|
|
925
|
-
|
|
926
|
-
### Task 7: Update MCP tools to accept both layer and scope
|
|
927
|
-
|
|
928
|
-
**Files:**
|
|
929
|
-
- Modify: `src/lib/cortex/mcp/server.ts`
|
|
930
|
-
|
|
931
|
-
- [ ] **Step 1: Read current mcp/server.ts**
|
|
932
|
-
|
|
933
|
-
Read the `cortex_teach` tool schema and handler to understand what fields are accepted.
|
|
934
|
-
|
|
935
|
-
- [ ] **Step 2: Update cortex_teach tool**
|
|
936
|
-
|
|
937
|
-
1. Add `scope` as an optional property in the tool's input schema (alongside existing `layer`)
|
|
938
|
-
2. In the handler, apply the same layer↔scope resolution logic from Task 6
|
|
939
|
-
3. Add `sensitivity` and `origin` as optional properties
|
|
940
|
-
4. Include v2 fields in the unit construction, using `getConfidenceBase(type)` for evidence_score (not 0.5)
|
|
941
|
-
5. Derive `creator_entity_id` from the agent context (use `'person-default-user'` as placeholder since MCP tools don't have user auth context — the real entity ID will be resolved by Pillar 5)
|
|
942
|
-
|
|
943
|
-
The key change: `layer` remains supported but `scope` is preferred. If both are provided, `scope` wins.
|
|
944
|
-
|
|
945
|
-
Import compat functions:
|
|
946
|
-
```typescript
|
|
947
|
-
import { layerToScope, scopeToLayer, scopeToLayerKey } from '../knowledge/compat';
|
|
948
|
-
```
|
|
949
|
-
|
|
950
|
-
- [ ] **Step 3: Commit**
|
|
951
|
-
|
|
952
|
-
```bash
|
|
953
|
-
git add src/lib/cortex/mcp/server.ts
|
|
954
|
-
git commit -m "feat(cortex): accept both layer and scope in MCP cortex_teach tool"
|
|
955
|
-
```
|
|
956
|
-
|
|
957
|
-
---
|
|
958
|
-
|
|
959
|
-
### Task 8: Update learn hook for v2 compatibility
|
|
960
|
-
|
|
961
|
-
**Files:**
|
|
962
|
-
- Modify: `bin/cortex-learn-hook.js`
|
|
963
|
-
|
|
964
|
-
- [ ] **Step 1: Read current cortex-learn-hook.js**
|
|
965
|
-
|
|
966
|
-
- [ ] **Step 2: Add scope field alongside layer**
|
|
967
|
-
|
|
968
|
-
The hook currently POSTs `{ text, type, layer: 'personal' }`. Update both entries to also include:
|
|
969
|
-
|
|
970
|
-
```javascript
|
|
971
|
-
const scope = JSON.stringify({ level: 'personal', entity_id: 'person-default-user' });
|
|
972
|
-
|
|
973
|
-
// Question entry:
|
|
974
|
-
const questionEntry = JSON.stringify({
|
|
975
|
-
text: lastExchange.question,
|
|
976
|
-
type: 'context',
|
|
977
|
-
layer: 'personal',
|
|
978
|
-
scope: { level: 'personal', entity_id: 'person-default-user' },
|
|
979
|
-
origin: { source_type: 'conversation', source_ref: sessionId || '', creator_entity_id: 'person-default-user' },
|
|
980
|
-
});
|
|
981
|
-
|
|
982
|
-
// Answer entry:
|
|
983
|
-
const answerEntry = JSON.stringify({
|
|
984
|
-
text: `Q: ${lastExchange.question}\nA: ${condensedAnswer}`,
|
|
985
|
-
type: knowledgeType,
|
|
986
|
-
layer: 'personal',
|
|
987
|
-
scope: { level: 'personal', entity_id: 'person-default-user' },
|
|
988
|
-
origin: { source_type: 'conversation', source_ref: sessionId || '', creator_entity_id: 'person-default-user' },
|
|
989
|
-
});
|
|
990
|
-
```
|
|
991
|
-
|
|
992
|
-
Note: `sessionId` should be extracted from the input JSON if available (the hook receives `{ transcript_path }` — the session ID is the filename without extension).
|
|
993
|
-
|
|
994
|
-
- [ ] **Step 3: Commit**
|
|
995
|
-
|
|
996
|
-
```bash
|
|
997
|
-
git add bin/cortex-learn-hook.js
|
|
998
|
-
git commit -m "feat(cortex): add scope and origin to learn hook knowledge entries"
|
|
999
|
-
```
|
|
1000
|
-
|
|
1001
|
-
---
|
|
1002
|
-
|
|
1003
|
-
## Chunk 4: Pipeline Integration and Final Tests
|
|
1004
|
-
|
|
1005
|
-
### Task 9: Update ingestion pipeline for v2 fields
|
|
1006
|
-
|
|
1007
|
-
**Files:**
|
|
1008
|
-
- Modify: `src/lib/cortex/ingestion/pipeline.ts`
|
|
1009
|
-
- Modify: `src/lib/cortex/ingestion/chunker.ts`
|
|
1010
|
-
|
|
1011
|
-
- [ ] **Step 1: Read pipeline.ts and chunker.ts**
|
|
1012
|
-
|
|
1013
|
-
- [ ] **Step 2: Update RawChunk → KnowledgeUnit mapping in pipeline**
|
|
1014
|
-
|
|
1015
|
-
In the pipeline where KnowledgeUnit is constructed from RawChunk, add v2 fields with defaults:
|
|
1016
|
-
|
|
1017
|
-
```typescript
|
|
1018
|
-
const unit: KnowledgeUnit = {
|
|
1019
|
-
// ... existing v1 fields ...
|
|
1020
|
-
// v2 fields with defaults
|
|
1021
|
-
scope: layerToScope(chunk.layer, chunk.workspace_id),
|
|
1022
|
-
entity_links: [],
|
|
1023
|
-
evidence_score: getConfidenceBase(chunk.type), // start with base confidence
|
|
1024
|
-
corroborations: 0,
|
|
1025
|
-
contradiction_refs: [],
|
|
1026
|
-
sensitivity: 'internal',
|
|
1027
|
-
creator_scope: null,
|
|
1028
|
-
origin: {
|
|
1029
|
-
source_type: 'conversation',
|
|
1030
|
-
source_ref: chunk.session_id ?? '',
|
|
1031
|
-
creator_entity_id: 'person-default-user',
|
|
1032
|
-
},
|
|
1033
|
-
propagation_path: [],
|
|
1034
|
-
};
|
|
1035
|
-
```
|
|
1036
|
-
|
|
1037
|
-
Import the compat function:
|
|
1038
|
-
```typescript
|
|
1039
|
-
import { layerToScope } from '../knowledge/compat';
|
|
1040
|
-
```
|
|
1041
|
-
|
|
1042
|
-
- [ ] **Step 3: Run the full cortex test suite**
|
|
1043
|
-
|
|
1044
|
-
Run: `npx vitest run tests/lib/cortex/`
|
|
1045
|
-
Expected: All tests pass (including existing pipeline tests)
|
|
1046
|
-
|
|
1047
|
-
- [ ] **Step 4: Commit**
|
|
1048
|
-
|
|
1049
|
-
```bash
|
|
1050
|
-
git add src/lib/cortex/ingestion/pipeline.ts
|
|
1051
|
-
git commit -m "feat(cortex): add v2 fields to ingestion pipeline output"
|
|
1052
|
-
```
|
|
1053
|
-
|
|
1054
|
-
---
|
|
1055
|
-
|
|
1056
|
-
### Task 10: Export knowledge/compat and evidence from knowledge index
|
|
1057
|
-
|
|
1058
|
-
**Files:**
|
|
1059
|
-
- Modify or create: `src/lib/cortex/knowledge/index.ts` (if a barrel exists, update it; if not, create one)
|
|
1060
|
-
|
|
1061
|
-
- [ ] **Step 1: Check if knowledge/index.ts exists**
|
|
1062
|
-
|
|
1063
|
-
Read `src/lib/cortex/knowledge/` directory to see if there's a barrel export.
|
|
1064
|
-
|
|
1065
|
-
- [ ] **Step 2: Create or update barrel export**
|
|
1066
|
-
|
|
1067
|
-
Ensure these are re-exported:
|
|
1068
|
-
```typescript
|
|
1069
|
-
export { layerToScope, scopeToLayer, scopeToLayerKey, layerKeyToScope } from './compat';
|
|
1070
|
-
export { computeEvidenceScore, AUTHORITY_FACTORS } from './evidence';
|
|
1071
|
-
export type { EvidenceScoreInput } from './evidence';
|
|
1072
|
-
// Existing type exports from types.ts should already be accessible
|
|
1073
|
-
```
|
|
1074
|
-
|
|
1075
|
-
- [ ] **Step 3: Run the full test suite**
|
|
1076
|
-
|
|
1077
|
-
Run: `npx vitest run tests/lib/cortex/`
|
|
1078
|
-
Expected: All tests pass
|
|
1079
|
-
|
|
1080
|
-
- [ ] **Step 4: Commit**
|
|
1081
|
-
|
|
1082
|
-
```bash
|
|
1083
|
-
git add src/lib/cortex/knowledge/
|
|
1084
|
-
git commit -m "feat(cortex): export compat and evidence modules from knowledge barrel"
|
|
1085
|
-
```
|
|
1086
|
-
|
|
1087
|
-
---
|
|
1088
|
-
|
|
1089
|
-
## Summary
|
|
1090
|
-
|
|
1091
|
-
| Task | Component | Tests | Status |
|
|
1092
|
-
|------|-----------|-------|--------|
|
|
1093
|
-
| 1 | v2 type definitions | — | |
|
|
1094
|
-
| 2 | v1↔v2 compatibility layer | 10 | |
|
|
1095
|
-
| 3 | Evidence score computation | 8 | |
|
|
1096
|
-
| 4 | Arrow schema evolution | 2 | |
|
|
1097
|
-
| 5 | Search/scoring update | 2 | |
|
|
1098
|
-
| 6 | Knowledge API backward compat | — | |
|
|
1099
|
-
| 7 | MCP tool backward compat | — | |
|
|
1100
|
-
| 8 | Learn hook v2 fields | — | |
|
|
1101
|
-
| 9 | Pipeline v2 integration | regression | |
|
|
1102
|
-
| 10 | Barrel export | regression | |
|
|
1103
|
-
|
|
1104
|
-
**Total: 10 tasks, ~22 new tests, 4 chunks**
|
|
1105
|
-
|
|
1106
|
-
**Key design decisions:**
|
|
1107
|
-
- `layer` field is KEPT on KnowledgeUnit for backward compat (intentional spec deviation — spec says "removed" but we keep it as derived field)
|
|
1108
|
-
- v2 fields are all optional/nullable — existing LanceDB tables migrated in-place via `addColumns()` during `store.init()`
|
|
1109
|
-
- `updateAccessCount()` and `browse()` updated to preserve/parse v2 fields
|
|
1110
|
-
- API accepts both `layer` and `scope` params — `scope` takes precedence
|
|
1111
|
-
- Evidence score starts at `base_confidence` and evolves with access/corroboration (computed by Pillar 6: Gravity)
|
|
1112
|
-
|
|
1113
|
-
After this plan is complete, both the Entity Graph (Pillar 1) and Knowledge Unit Evolution (Pillar 2) will be in place — enabling Pillar 3 (Context Assembly Engine) and Pillar 4 (Boundary Engine) to build on them.
|
|
1
|
+
# Cortex v2 — Pillar 2: Knowledge Unit Schema Evolution
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Evolve the KnowledgeUnit schema from flat `layer` to graph-aware `scope`, add entity links, evidence tracking, sensitivity classification, and provenance — while maintaining full backward compatibility with the v1 API and existing ~542 knowledge units.
|
|
6
|
+
|
|
7
|
+
**Architecture:** The `layer` field is KEPT for backward compatibility (spec says "removed" but we intentionally keep it as a derived field to avoid breaking existing consumers — this is a documented deviation). New v2 fields (`scope`, `entity_links`, `evidence_score`, etc.) are added as nullable Arrow columns. Existing LanceDB tables are migrated in-place using LanceDB's `addColumns()` API in a migration step during `store.init()`. A compatibility layer maps v1 `layer` params to v2 `scope` in all API routes, MCP tools, and hooks.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** TypeScript, LanceDB (Arrow schema), vitest
|
|
10
|
+
|
|
11
|
+
**Spec:** `docs/superpowers/specs/2026-03-14-cortex-v2-design.md` — Pillar 2
|
|
12
|
+
|
|
13
|
+
**Depends on:** Pillar 1 (Entity Graph) — completed
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## File Structure
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
Modified files:
|
|
21
|
+
├── src/lib/cortex/knowledge/types.ts — Add v2 interfaces, keep v1 layer for compat (intentional spec deviation)
|
|
22
|
+
├── src/lib/cortex/knowledge/evidence.ts — NEW: evidence score computation
|
|
23
|
+
├── src/lib/cortex/knowledge/compat.ts — NEW: v1↔v2 layer/scope mapping
|
|
24
|
+
├── src/lib/cortex/store.ts — Evolve Arrow schema, add v2 fields, migrate existing tables
|
|
25
|
+
├── src/lib/cortex/store-migration.ts — NEW: LanceDB table schema migration (addColumns)
|
|
26
|
+
├── src/lib/cortex/retrieval/search.ts — Use scope instead of layer for weights
|
|
27
|
+
├── src/lib/cortex/retrieval/scoring.ts — Add evidence_score to formula
|
|
28
|
+
├── src/app/api/cortex/knowledge/route.ts — Accept both layer and scope params
|
|
29
|
+
├── src/lib/cortex/mcp/server.ts — Accept both layer and scope in tools
|
|
30
|
+
├── bin/cortex-learn-hook.js — Map 'personal' to scope format
|
|
31
|
+
|
|
32
|
+
Test files:
|
|
33
|
+
├── tests/lib/cortex/knowledge/evidence.test.ts — Evidence score computation
|
|
34
|
+
├── tests/lib/cortex/knowledge/compat.test.ts — v1↔v2 mapping
|
|
35
|
+
├── tests/lib/cortex/knowledge/types.test.ts — Updated for v2 types
|
|
36
|
+
├── tests/lib/cortex/store.test.ts — Updated for v2 schema
|
|
37
|
+
├── tests/lib/cortex/retrieval/search.test.ts — Updated for scope-based weights
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Chunk 1: New Types and Compatibility Layer
|
|
43
|
+
|
|
44
|
+
### Task 1: Add v2 type definitions
|
|
45
|
+
|
|
46
|
+
**Files:**
|
|
47
|
+
- Modify: `src/lib/cortex/knowledge/types.ts`
|
|
48
|
+
|
|
49
|
+
- [ ] **Step 1: Add new interfaces after existing ones (keep all v1 types for backward compat)**
|
|
50
|
+
|
|
51
|
+
Add these types to `src/lib/cortex/knowledge/types.ts` below the existing interfaces:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// --- v2 Schema Extensions ---
|
|
55
|
+
|
|
56
|
+
export const SCOPE_LEVELS = ['personal', 'team', 'department', 'organization'] as const;
|
|
57
|
+
export type ScopeLevel = typeof SCOPE_LEVELS[number];
|
|
58
|
+
|
|
59
|
+
export interface Scope {
|
|
60
|
+
level: ScopeLevel;
|
|
61
|
+
entity_id: string; // format: {type}-{slug}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface EntityLink {
|
|
65
|
+
entity_id: string;
|
|
66
|
+
entity_type: EntityType; // imported from graph module
|
|
67
|
+
relation: 'created_by' | 'about' | 'scoped_to' | 'derived_from';
|
|
68
|
+
weight: number; // 0-1
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Add this import at the top of types.ts:
|
|
73
|
+
```typescript
|
|
74
|
+
import type { EntityType } from '@/lib/cortex/graph/types';
|
|
75
|
+
|
|
76
|
+
export const SENSITIVITY_CLASSES = ['public', 'internal', 'restricted', 'confidential'] as const;
|
|
77
|
+
export type SensitivityClass = typeof SENSITIVITY_CLASSES[number];
|
|
78
|
+
|
|
79
|
+
export interface ScopeOverride {
|
|
80
|
+
max_level: ScopeLevel;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const ORIGIN_SOURCE_TYPES = [
|
|
84
|
+
'conversation', 'git_commit', 'pr_review', 'document',
|
|
85
|
+
'behavioral', 'distillation', 'manual',
|
|
86
|
+
] as const;
|
|
87
|
+
export type OriginSourceType = typeof ORIGIN_SOURCE_TYPES[number];
|
|
88
|
+
|
|
89
|
+
export interface Origin {
|
|
90
|
+
source_type: OriginSourceType;
|
|
91
|
+
source_ref: string;
|
|
92
|
+
creator_entity_id: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface PropHop {
|
|
96
|
+
from_scope: Scope;
|
|
97
|
+
to_scope: Scope;
|
|
98
|
+
reason: 'evidence_threshold' | 'policy_push' | 'manual_promote';
|
|
99
|
+
timestamp: string;
|
|
100
|
+
confidence_at_hop: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function isValidScopeLevel(s: string): s is ScopeLevel {
|
|
104
|
+
return SCOPE_LEVELS.includes(s as ScopeLevel);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function isValidSensitivity(s: string): s is SensitivityClass {
|
|
108
|
+
return SENSITIVITY_CLASSES.includes(s as SensitivityClass);
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
- [ ] **Step 1b: Update HOP_DECAY_FACTOR from 0.8 to 0.85**
|
|
113
|
+
|
|
114
|
+
Per spec Key Constants table, update the existing `HOP_DECAY_FACTOR` constant in types.ts from `0.8` to `0.85` (increased to preserve more signal across propagation hops).
|
|
115
|
+
|
|
116
|
+
- [ ] **Step 2: Extend KnowledgeUnit interface with optional v2 fields**
|
|
117
|
+
|
|
118
|
+
Add new optional fields to the existing `KnowledgeUnit` interface (keeping `layer` for backward compat):
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
export interface KnowledgeUnit {
|
|
122
|
+
// ... all existing v1 fields unchanged ...
|
|
123
|
+
layer: Layer; // KEPT for backward compat (derived from scope on read)
|
|
124
|
+
|
|
125
|
+
// v2 fields (optional — null/default when reading v1 data)
|
|
126
|
+
scope?: Scope;
|
|
127
|
+
entity_links?: EntityLink[];
|
|
128
|
+
evidence_score?: number;
|
|
129
|
+
corroborations?: number;
|
|
130
|
+
contradiction_refs?: string[];
|
|
131
|
+
sensitivity?: SensitivityClass;
|
|
132
|
+
creator_scope?: ScopeOverride | null;
|
|
133
|
+
origin?: Origin;
|
|
134
|
+
propagation_path?: PropHop[];
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- [ ] **Step 3: Commit**
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
git add src/lib/cortex/knowledge/types.ts
|
|
142
|
+
git commit -m "feat(cortex): add v2 knowledge unit type definitions"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### Task 2: Create v1↔v2 compatibility layer
|
|
148
|
+
|
|
149
|
+
**Files:**
|
|
150
|
+
- Create: `src/lib/cortex/knowledge/compat.ts`
|
|
151
|
+
- Create: `tests/lib/cortex/knowledge/compat.test.ts`
|
|
152
|
+
|
|
153
|
+
- [ ] **Step 1: Write failing tests**
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
// tests/lib/cortex/knowledge/compat.test.ts
|
|
157
|
+
import { describe, it, expect } from 'vitest';
|
|
158
|
+
import {
|
|
159
|
+
layerToScope,
|
|
160
|
+
scopeToLayer,
|
|
161
|
+
scopeToLayerKey,
|
|
162
|
+
layerKeyToScope,
|
|
163
|
+
} from '@/lib/cortex/knowledge/compat';
|
|
164
|
+
|
|
165
|
+
describe('v1↔v2 Compatibility', () => {
|
|
166
|
+
describe('layerToScope', () => {
|
|
167
|
+
it('maps personal to personal scope', () => {
|
|
168
|
+
const scope = layerToScope('personal', null, 'default-user');
|
|
169
|
+
expect(scope).toEqual({ level: 'personal', entity_id: 'person-default-user' });
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('maps workspace to team scope', () => {
|
|
173
|
+
const scope = layerToScope('workspace', 42);
|
|
174
|
+
expect(scope).toEqual({ level: 'team', entity_id: 'team-default' });
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('maps team to organization scope', () => {
|
|
178
|
+
const scope = layerToScope('team', null);
|
|
179
|
+
expect(scope).toEqual({ level: 'organization', entity_id: 'organization-default' });
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe('scopeToLayer', () => {
|
|
184
|
+
it('maps personal scope to personal layer', () => {
|
|
185
|
+
expect(scopeToLayer({ level: 'personal', entity_id: 'person-alice' })).toBe('personal');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('maps team scope to workspace layer', () => {
|
|
189
|
+
expect(scopeToLayer({ level: 'team', entity_id: 'team-platform' })).toBe('workspace');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('maps department scope to team layer', () => {
|
|
193
|
+
expect(scopeToLayer({ level: 'department', entity_id: 'dept-eng' })).toBe('team');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('maps organization scope to team layer', () => {
|
|
197
|
+
expect(scopeToLayer({ level: 'organization', entity_id: 'org-acme' })).toBe('team');
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe('scopeToLayerKey', () => {
|
|
202
|
+
it('maps personal scope to personal key', () => {
|
|
203
|
+
expect(scopeToLayerKey({ level: 'personal', entity_id: 'person-alice' })).toBe('personal');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('maps team scope with workspace_id to workspace/id key', () => {
|
|
207
|
+
expect(scopeToLayerKey({ level: 'team', entity_id: 'team-platform' }, 42)).toBe('workspace/42');
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('maps team scope without workspace_id to team key', () => {
|
|
211
|
+
expect(scopeToLayerKey({ level: 'team', entity_id: 'team-platform' })).toBe('team');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('maps organization scope to team key', () => {
|
|
215
|
+
expect(scopeToLayerKey({ level: 'organization', entity_id: 'org-acme' })).toBe('team');
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe('layerKeyToScope', () => {
|
|
220
|
+
it('maps personal key to personal scope', () => {
|
|
221
|
+
const scope = layerKeyToScope('personal', 'default-user');
|
|
222
|
+
expect(scope).toEqual({ level: 'personal', entity_id: 'person-default-user' });
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('maps workspace/id key to team scope', () => {
|
|
226
|
+
const scope = layerKeyToScope('workspace/42');
|
|
227
|
+
expect(scope).toEqual({ level: 'team', entity_id: 'team-default' });
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('maps team key to organization scope', () => {
|
|
231
|
+
const scope = layerKeyToScope('team');
|
|
232
|
+
expect(scope).toEqual({ level: 'organization', entity_id: 'organization-default' });
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
- [ ] **Step 2: Run tests to verify they fail**
|
|
239
|
+
|
|
240
|
+
Run: `npx vitest run tests/lib/cortex/knowledge/compat.test.ts`
|
|
241
|
+
|
|
242
|
+
- [ ] **Step 3: Implement compatibility layer**
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
// src/lib/cortex/knowledge/compat.ts
|
|
246
|
+
import type { Layer, Scope, ScopeLevel } from './types';
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Convert v1 layer to v2 scope.
|
|
250
|
+
* personal → personal scope, workspace → team scope, team → organization scope.
|
|
251
|
+
*/
|
|
252
|
+
export function layerToScope(
|
|
253
|
+
layer: Layer,
|
|
254
|
+
workspaceId?: number | null,
|
|
255
|
+
userId?: string,
|
|
256
|
+
): Scope {
|
|
257
|
+
switch (layer) {
|
|
258
|
+
case 'personal':
|
|
259
|
+
return { level: 'personal', entity_id: `person-${userId ?? 'default-user'}` };
|
|
260
|
+
case 'workspace':
|
|
261
|
+
return { level: 'team', entity_id: 'team-default' };
|
|
262
|
+
case 'team':
|
|
263
|
+
return { level: 'organization', entity_id: 'organization-default' };
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Convert v2 scope back to v1 layer (for backward compat).
|
|
269
|
+
* personal → personal, team → workspace, department/organization → team.
|
|
270
|
+
*/
|
|
271
|
+
export function scopeToLayer(scope: Scope): Layer {
|
|
272
|
+
switch (scope.level) {
|
|
273
|
+
case 'personal': return 'personal';
|
|
274
|
+
case 'team': return 'workspace';
|
|
275
|
+
case 'department':
|
|
276
|
+
case 'organization': return 'team';
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Convert v2 scope to a LanceDB layer key (storage path).
|
|
282
|
+
* Maintains compatibility with existing store.layerPath() logic.
|
|
283
|
+
*/
|
|
284
|
+
export function scopeToLayerKey(scope: Scope, workspaceId?: number | null): string {
|
|
285
|
+
switch (scope.level) {
|
|
286
|
+
case 'personal': return 'personal';
|
|
287
|
+
case 'team':
|
|
288
|
+
return workspaceId ? `workspace/${workspaceId}` : 'team';
|
|
289
|
+
case 'department':
|
|
290
|
+
case 'organization':
|
|
291
|
+
return 'team';
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Convert a LanceDB layer key back to a v2 scope.
|
|
297
|
+
*/
|
|
298
|
+
export function layerKeyToScope(layerKey: string, userId?: string): Scope {
|
|
299
|
+
if (layerKey === 'personal') {
|
|
300
|
+
return { level: 'personal', entity_id: `person-${userId ?? 'default-user'}` };
|
|
301
|
+
}
|
|
302
|
+
if (layerKey.startsWith('workspace/')) {
|
|
303
|
+
return { level: 'team', entity_id: 'team-default' };
|
|
304
|
+
}
|
|
305
|
+
// 'team' key → organization scope
|
|
306
|
+
return { level: 'organization', entity_id: 'organization-default' };
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
- [ ] **Step 4: Run tests to verify they pass**
|
|
311
|
+
|
|
312
|
+
Run: `npx vitest run tests/lib/cortex/knowledge/compat.test.ts`
|
|
313
|
+
Expected: PASS (10 tests)
|
|
314
|
+
|
|
315
|
+
- [ ] **Step 5: Commit**
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
git add src/lib/cortex/knowledge/compat.ts tests/lib/cortex/knowledge/compat.test.ts
|
|
319
|
+
git commit -m "feat(cortex): add v1↔v2 layer/scope compatibility mapping"
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
### Task 3: Evidence score computation
|
|
325
|
+
|
|
326
|
+
**Files:**
|
|
327
|
+
- Create: `src/lib/cortex/knowledge/evidence.ts`
|
|
328
|
+
- Create: `tests/lib/cortex/knowledge/evidence.test.ts`
|
|
329
|
+
|
|
330
|
+
- [ ] **Step 1: Write failing tests**
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
// tests/lib/cortex/knowledge/evidence.test.ts
|
|
334
|
+
import { describe, it, expect } from 'vitest';
|
|
335
|
+
import { computeEvidenceScore, AUTHORITY_FACTORS } from '@/lib/cortex/knowledge/evidence';
|
|
336
|
+
|
|
337
|
+
describe('computeEvidenceScore', () => {
|
|
338
|
+
it('returns base confidence for fresh unit with no interactions', () => {
|
|
339
|
+
const score = computeEvidenceScore({
|
|
340
|
+
baseConfidence: 0.8,
|
|
341
|
+
corroborations: 0,
|
|
342
|
+
accessCount: 0,
|
|
343
|
+
authorityFactor: 1.0,
|
|
344
|
+
contradictionCount: 0,
|
|
345
|
+
});
|
|
346
|
+
expect(score).toBeCloseTo(0.8);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('increases with corroborations', () => {
|
|
350
|
+
const base = computeEvidenceScore({
|
|
351
|
+
baseConfidence: 0.8, corroborations: 0, accessCount: 0, authorityFactor: 1.0, contradictionCount: 0,
|
|
352
|
+
});
|
|
353
|
+
const withCorr = computeEvidenceScore({
|
|
354
|
+
baseConfidence: 0.8, corroborations: 3, accessCount: 0, authorityFactor: 1.0, contradictionCount: 0,
|
|
355
|
+
});
|
|
356
|
+
expect(withCorr).toBeGreaterThan(base);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('increases with access count (diminishing returns)', () => {
|
|
360
|
+
const low = computeEvidenceScore({
|
|
361
|
+
baseConfidence: 0.8, corroborations: 0, accessCount: 5, authorityFactor: 1.0, contradictionCount: 0,
|
|
362
|
+
});
|
|
363
|
+
const high = computeEvidenceScore({
|
|
364
|
+
baseConfidence: 0.8, corroborations: 0, accessCount: 50, authorityFactor: 1.0, contradictionCount: 0,
|
|
365
|
+
});
|
|
366
|
+
expect(high).toBeGreaterThan(low);
|
|
367
|
+
// Capped at 50 — going higher has no effect
|
|
368
|
+
const over = computeEvidenceScore({
|
|
369
|
+
baseConfidence: 0.8, corroborations: 0, accessCount: 100, authorityFactor: 1.0, contradictionCount: 0,
|
|
370
|
+
});
|
|
371
|
+
expect(over).toBeCloseTo(high);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('decreases with contradictions', () => {
|
|
375
|
+
const clean = computeEvidenceScore({
|
|
376
|
+
baseConfidence: 0.8, corroborations: 2, accessCount: 10, authorityFactor: 1.0, contradictionCount: 0,
|
|
377
|
+
});
|
|
378
|
+
const contested = computeEvidenceScore({
|
|
379
|
+
baseConfidence: 0.8, corroborations: 2, accessCount: 10, authorityFactor: 1.0, contradictionCount: 2,
|
|
380
|
+
});
|
|
381
|
+
expect(contested).toBeLessThan(clean);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('caps corroboration contribution at 10', () => {
|
|
385
|
+
const at10 = computeEvidenceScore({
|
|
386
|
+
baseConfidence: 0.8, corroborations: 10, accessCount: 0, authorityFactor: 1.0, contradictionCount: 0,
|
|
387
|
+
});
|
|
388
|
+
const at20 = computeEvidenceScore({
|
|
389
|
+
baseConfidence: 0.8, corroborations: 20, accessCount: 0, authorityFactor: 1.0, contradictionCount: 0,
|
|
390
|
+
});
|
|
391
|
+
expect(at20).toBeCloseTo(at10); // no additional benefit beyond 10
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it('is boosted by authority factor', () => {
|
|
395
|
+
const conversation = computeEvidenceScore({
|
|
396
|
+
baseConfidence: 0.8, corroborations: 0, accessCount: 0, authorityFactor: AUTHORITY_FACTORS.conversation, contradictionCount: 0,
|
|
397
|
+
});
|
|
398
|
+
const document = computeEvidenceScore({
|
|
399
|
+
baseConfidence: 0.8, corroborations: 0, accessCount: 0, authorityFactor: AUTHORITY_FACTORS.document, contradictionCount: 0,
|
|
400
|
+
});
|
|
401
|
+
expect(document).toBeGreaterThan(conversation);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('is capped at 1.0', () => {
|
|
405
|
+
const score = computeEvidenceScore({
|
|
406
|
+
baseConfidence: 0.95, corroborations: 10, accessCount: 50, authorityFactor: 1.3, contradictionCount: 0,
|
|
407
|
+
});
|
|
408
|
+
expect(score).toBeLessThanOrEqual(1.0);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it('never goes below 0', () => {
|
|
412
|
+
const score = computeEvidenceScore({
|
|
413
|
+
baseConfidence: 0.1, corroborations: 0, accessCount: 0, authorityFactor: 1.0, contradictionCount: 10,
|
|
414
|
+
});
|
|
415
|
+
expect(score).toBeGreaterThanOrEqual(0);
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
- [ ] **Step 2: Run tests to verify they fail**
|
|
421
|
+
|
|
422
|
+
Run: `npx vitest run tests/lib/cortex/knowledge/evidence.test.ts`
|
|
423
|
+
|
|
424
|
+
- [ ] **Step 3: Implement evidence score**
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
// src/lib/cortex/knowledge/evidence.ts
|
|
428
|
+
|
|
429
|
+
export const AUTHORITY_FACTORS = {
|
|
430
|
+
conversation: 1.0,
|
|
431
|
+
git_commit: 1.1,
|
|
432
|
+
pr_review: 1.1,
|
|
433
|
+
document: 1.2,
|
|
434
|
+
behavioral: 1.0,
|
|
435
|
+
distillation: 1.1,
|
|
436
|
+
manual: 1.3,
|
|
437
|
+
} as const;
|
|
438
|
+
|
|
439
|
+
export interface EvidenceScoreInput {
|
|
440
|
+
baseConfidence: number;
|
|
441
|
+
corroborations: number;
|
|
442
|
+
accessCount: number;
|
|
443
|
+
authorityFactor: number;
|
|
444
|
+
contradictionCount: number;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Compute evidence score (0-1) from knowledge unit metrics.
|
|
449
|
+
*
|
|
450
|
+
* Formula from spec:
|
|
451
|
+
* evidence_score = min(1.0,
|
|
452
|
+
* base_confidence
|
|
453
|
+
* × (1 + 0.1 × corroborations)
|
|
454
|
+
* × (1 + 0.01 × min(access_count, 50))
|
|
455
|
+
* × authority_factor
|
|
456
|
+
* ÷ (1 + 0.5 × contradiction_count)
|
|
457
|
+
* )
|
|
458
|
+
*/
|
|
459
|
+
export function computeEvidenceScore(input: EvidenceScoreInput): number {
|
|
460
|
+
const { baseConfidence, corroborations, accessCount, authorityFactor, contradictionCount } = input;
|
|
461
|
+
|
|
462
|
+
const corroborationBoost = 1 + 0.1 * Math.min(corroborations, 10);
|
|
463
|
+
const accessBoost = 1 + 0.01 * Math.min(accessCount, 50);
|
|
464
|
+
const contradictionPenalty = 1 + 0.5 * contradictionCount;
|
|
465
|
+
|
|
466
|
+
const raw = (baseConfidence * corroborationBoost * accessBoost * authorityFactor) / contradictionPenalty;
|
|
467
|
+
|
|
468
|
+
return Math.max(0, Math.min(1.0, raw));
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
- [ ] **Step 4: Run tests to verify they pass**
|
|
473
|
+
|
|
474
|
+
Run: `npx vitest run tests/lib/cortex/knowledge/evidence.test.ts`
|
|
475
|
+
Expected: PASS (7 tests)
|
|
476
|
+
|
|
477
|
+
- [ ] **Step 5: Commit**
|
|
478
|
+
|
|
479
|
+
```bash
|
|
480
|
+
git add src/lib/cortex/knowledge/evidence.ts tests/lib/cortex/knowledge/evidence.test.ts
|
|
481
|
+
git commit -m "feat(cortex): add evidence score computation"
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
## Chunk 2: LanceDB Schema Evolution and Store Updates
|
|
487
|
+
|
|
488
|
+
### Task 4: Evolve Arrow schema with v2 fields and table migration
|
|
489
|
+
|
|
490
|
+
**Files:**
|
|
491
|
+
- Create: `src/lib/cortex/store-migration.ts`
|
|
492
|
+
- Modify: `src/lib/cortex/store.ts`
|
|
493
|
+
- Modify: `tests/lib/cortex/store.test.ts`
|
|
494
|
+
|
|
495
|
+
> **CRITICAL:** LanceDB is strict about schema — you cannot add records with columns that don't exist in the table's schema. Existing v1 tables lack the 9 new v2 columns. We must migrate existing tables by adding the new columns before any v2 writes.
|
|
496
|
+
|
|
497
|
+
- [ ] **Step 1: Read the current store.ts**
|
|
498
|
+
|
|
499
|
+
Read `src/lib/cortex/store.ts` to understand:
|
|
500
|
+
- The `buildSchema(dimensions)` function (Arrow field definitions)
|
|
501
|
+
- The `unitToRecord` serialization
|
|
502
|
+
- The deserialization in `search()` and `browse()`
|
|
503
|
+
- The `updateAccessCount()` method (delete + re-add pattern)
|
|
504
|
+
- How `getConnection()` opens tables
|
|
505
|
+
|
|
506
|
+
- [ ] **Step 1b: Create store-migration.ts**
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
// src/lib/cortex/store-migration.ts
|
|
510
|
+
import type { Table } from '@lancedb/lancedb';
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* V2 columns to add to existing LanceDB tables.
|
|
514
|
+
* Each entry: [column_name, sql_expression_for_default_value]
|
|
515
|
+
*/
|
|
516
|
+
const V2_COLUMNS: [string, string][] = [
|
|
517
|
+
['scope', "'null'"], // JSON string, null for v1 data
|
|
518
|
+
['entity_links', "'[]'"], // JSON array string
|
|
519
|
+
['evidence_score', '0.5'], // float default
|
|
520
|
+
['corroborations', '0'], // int default
|
|
521
|
+
['contradiction_refs', "'[]'"], // JSON array string
|
|
522
|
+
['sensitivity', "'internal'"], // string default
|
|
523
|
+
['creator_scope', "'null'"], // JSON string, null
|
|
524
|
+
['origin', "'null'"], // JSON string, null
|
|
525
|
+
['propagation_path', "'[]'"], // JSON array string
|
|
526
|
+
];
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Migrate a LanceDB table to v2 schema by adding missing columns.
|
|
530
|
+
* Safe to call repeatedly — checks column existence before adding.
|
|
531
|
+
*/
|
|
532
|
+
export async function migrateTableToV2(table: Table): Promise<void> {
|
|
533
|
+
const schema = await table.schema;
|
|
534
|
+
const existingFields = new Set(schema.fields.map(f => f.name));
|
|
535
|
+
|
|
536
|
+
for (const [colName, defaultExpr] of V2_COLUMNS) {
|
|
537
|
+
if (!existingFields.has(colName)) {
|
|
538
|
+
try {
|
|
539
|
+
await table.addColumns([{ name: colName, valueSql: defaultExpr }]);
|
|
540
|
+
} catch {
|
|
541
|
+
// Column may have been added concurrently, ignore
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
This uses LanceDB's `table.addColumns()` API to add nullable columns with default values to existing tables in-place, without needing to read/drop/recreate.
|
|
549
|
+
|
|
550
|
+
- [ ] **Step 2: Write a failing test for v2 fields**
|
|
551
|
+
|
|
552
|
+
Add to `tests/lib/cortex/store.test.ts`:
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
describe('CortexStore — v2 fields', () => {
|
|
556
|
+
let tmpDir: string;
|
|
557
|
+
let store: CortexStore;
|
|
558
|
+
|
|
559
|
+
beforeEach(async () => {
|
|
560
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cortex-store-'));
|
|
561
|
+
store = new CortexStore(tmpDir);
|
|
562
|
+
await store.init(384);
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
afterEach(async () => {
|
|
566
|
+
await store.close();
|
|
567
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it('stores and retrieves v2 fields', async () => {
|
|
571
|
+
const vector = new Array(384).fill(0).map(() => Math.random());
|
|
572
|
+
const unit = {
|
|
573
|
+
id: 'v2-test-1',
|
|
574
|
+
vector,
|
|
575
|
+
text: 'Auth uses JWT with refresh tokens',
|
|
576
|
+
type: 'decision' as const,
|
|
577
|
+
layer: 'personal' as const,
|
|
578
|
+
workspace_id: null,
|
|
579
|
+
session_id: 'sess-1',
|
|
580
|
+
agent_type: 'claude' as const,
|
|
581
|
+
project_path: '/project',
|
|
582
|
+
file_refs: ['src/auth.ts'],
|
|
583
|
+
confidence: 0.85,
|
|
584
|
+
created: new Date().toISOString(),
|
|
585
|
+
source_timestamp: new Date().toISOString(),
|
|
586
|
+
stale_score: 0,
|
|
587
|
+
access_count: 0,
|
|
588
|
+
last_accessed: null,
|
|
589
|
+
metadata: {},
|
|
590
|
+
// v2 fields
|
|
591
|
+
scope: { level: 'personal' as const, entity_id: 'person-alice' },
|
|
592
|
+
entity_links: [
|
|
593
|
+
{ entity_id: 'topic-auth', entity_type: 'topic', relation: 'about' as const, weight: 0.9 },
|
|
594
|
+
],
|
|
595
|
+
evidence_score: 0.72,
|
|
596
|
+
corroborations: 2,
|
|
597
|
+
contradiction_refs: ['other-id-1'],
|
|
598
|
+
sensitivity: 'internal' as const,
|
|
599
|
+
creator_scope: null,
|
|
600
|
+
origin: { source_type: 'conversation' as const, source_ref: 'sess-1', creator_entity_id: 'person-alice' },
|
|
601
|
+
propagation_path: [],
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
await store.add('personal', unit);
|
|
605
|
+
const results = await store.search('personal', vector, 5);
|
|
606
|
+
expect(results).toHaveLength(1);
|
|
607
|
+
|
|
608
|
+
const result = results[0];
|
|
609
|
+
expect(result.scope).toEqual({ level: 'personal', entity_id: 'person-alice' });
|
|
610
|
+
expect(result.entity_links).toHaveLength(1);
|
|
611
|
+
expect(result.entity_links![0].entity_id).toBe('topic-auth');
|
|
612
|
+
expect(result.evidence_score).toBeCloseTo(0.72);
|
|
613
|
+
expect(result.corroborations).toBe(2);
|
|
614
|
+
expect(result.contradiction_refs).toEqual(['other-id-1']);
|
|
615
|
+
expect(result.sensitivity).toBe('internal');
|
|
616
|
+
expect(result.origin?.source_type).toBe('conversation');
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
it('reads v1 data with default v2 fields', async () => {
|
|
620
|
+
const vector = new Array(384).fill(0).map(() => Math.random());
|
|
621
|
+
// Store a unit WITHOUT v2 fields (simulating v1 data)
|
|
622
|
+
const unit = {
|
|
623
|
+
id: 'v1-test-1',
|
|
624
|
+
vector,
|
|
625
|
+
text: 'Old v1 knowledge',
|
|
626
|
+
type: 'context' as const,
|
|
627
|
+
layer: 'personal' as const,
|
|
628
|
+
workspace_id: null,
|
|
629
|
+
session_id: null,
|
|
630
|
+
agent_type: 'claude' as const,
|
|
631
|
+
project_path: null,
|
|
632
|
+
file_refs: [],
|
|
633
|
+
confidence: 0.6,
|
|
634
|
+
created: new Date().toISOString(),
|
|
635
|
+
source_timestamp: new Date().toISOString(),
|
|
636
|
+
stale_score: 0,
|
|
637
|
+
access_count: 0,
|
|
638
|
+
last_accessed: null,
|
|
639
|
+
metadata: {},
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
await store.add('personal', unit);
|
|
643
|
+
const results = await store.search('personal', vector, 5);
|
|
644
|
+
const result = results[0];
|
|
645
|
+
|
|
646
|
+
// v2 fields should have sensible defaults
|
|
647
|
+
expect(result.evidence_score).toBe(0.5);
|
|
648
|
+
expect(result.corroborations).toBe(0);
|
|
649
|
+
expect(result.contradiction_refs).toEqual([]);
|
|
650
|
+
expect(result.sensitivity).toBe('internal');
|
|
651
|
+
expect(result.entity_links).toEqual([]);
|
|
652
|
+
expect(result.propagation_path).toEqual([]);
|
|
653
|
+
});
|
|
654
|
+
});
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
- [ ] **Step 3: Run tests to verify they fail**
|
|
658
|
+
|
|
659
|
+
Run: `npx vitest run tests/lib/cortex/store.test.ts`
|
|
660
|
+
|
|
661
|
+
- [ ] **Step 4: Evolve the Arrow schema**
|
|
662
|
+
|
|
663
|
+
In `src/lib/cortex/store.ts`, add new Arrow fields to the schema builder function. These are all nullable Utf8 fields (JSON-serialized for complex types) or nullable Float64/Int32 for numbers:
|
|
664
|
+
|
|
665
|
+
```typescript
|
|
666
|
+
// Add after existing fields in the schema:
|
|
667
|
+
new arrow.Field('scope', new arrow.Utf8(), true), // JSON: { level, entity_id }
|
|
668
|
+
new arrow.Field('entity_links', new arrow.Utf8(), true), // JSON array
|
|
669
|
+
new arrow.Field('evidence_score', new arrow.Float64(), true), // nullable
|
|
670
|
+
new arrow.Field('corroborations', new arrow.Int32(), true), // nullable
|
|
671
|
+
new arrow.Field('contradiction_refs', new arrow.Utf8(), true), // JSON array
|
|
672
|
+
new arrow.Field('sensitivity', new arrow.Utf8(), true), // nullable
|
|
673
|
+
new arrow.Field('creator_scope', new arrow.Utf8(), true), // JSON or null
|
|
674
|
+
new arrow.Field('origin', new arrow.Utf8(), true), // JSON or null
|
|
675
|
+
new arrow.Field('propagation_path', new arrow.Utf8(), true), // JSON array
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
In the `add()` method, after opening/creating the table but before `table.add([record])`, call migration:
|
|
679
|
+
```typescript
|
|
680
|
+
import { migrateTableToV2 } from './store-migration';
|
|
681
|
+
|
|
682
|
+
// In add(), after getting/creating the table:
|
|
683
|
+
await migrateTableToV2(table);
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
Also call `migrateTableToV2` in `search()` and `browse()` after getting the table, to ensure reads from v1 tables also get the new columns. Cache a Set of already-migrated table paths to avoid repeated migration checks.
|
|
687
|
+
|
|
688
|
+
Update the serialization (unitToRecord) to include v2 fields:
|
|
689
|
+
```typescript
|
|
690
|
+
scope: unit.scope ? JSON.stringify(unit.scope) : null,
|
|
691
|
+
entity_links: JSON.stringify(unit.entity_links ?? []),
|
|
692
|
+
evidence_score: unit.evidence_score ?? 0.5,
|
|
693
|
+
corroborations: unit.corroborations ?? 0,
|
|
694
|
+
contradiction_refs: JSON.stringify(unit.contradiction_refs ?? []),
|
|
695
|
+
sensitivity: unit.sensitivity ?? 'internal',
|
|
696
|
+
creator_scope: unit.creator_scope ? JSON.stringify(unit.creator_scope) : null,
|
|
697
|
+
origin: unit.origin ? JSON.stringify(unit.origin) : null,
|
|
698
|
+
propagation_path: JSON.stringify(unit.propagation_path ?? []),
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
**CRITICAL: Also update `updateAccessCount()`** — this method uses a delete+re-add pattern that reconstructs the record from raw fields. It must include all v2 fields in the reconstruction to avoid dropping them:
|
|
702
|
+
|
|
703
|
+
```typescript
|
|
704
|
+
// In updateAccessCount(), when reconstructing the record, add:
|
|
705
|
+
scope: raw.scope ?? null,
|
|
706
|
+
entity_links: raw.entity_links ?? '[]',
|
|
707
|
+
evidence_score: raw.evidence_score ?? 0.5,
|
|
708
|
+
corroborations: raw.corroborations ?? 0,
|
|
709
|
+
contradiction_refs: raw.contradiction_refs ?? '[]',
|
|
710
|
+
sensitivity: raw.sensitivity ?? 'internal',
|
|
711
|
+
creator_scope: raw.creator_scope ?? null,
|
|
712
|
+
origin: raw.origin ?? null,
|
|
713
|
+
propagation_path: raw.propagation_path ?? '[]',
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
**Also update `browse()` deserialization** — `browse()` has its own row-mapping logic separate from `search()`. Apply the same v2 field parsing to `browse()` results.
|
|
717
|
+
|
|
718
|
+
Update deserialization in BOTH `search()` and `browse()` to parse v2 fields with defaults:
|
|
719
|
+
```typescript
|
|
720
|
+
scope: row.scope ? JSON.parse(row.scope) : undefined,
|
|
721
|
+
entity_links: JSON.parse(row.entity_links || '[]'),
|
|
722
|
+
evidence_score: row.evidence_score ?? 0.5,
|
|
723
|
+
corroborations: row.corroborations ?? 0,
|
|
724
|
+
contradiction_refs: JSON.parse(row.contradiction_refs || '[]'),
|
|
725
|
+
sensitivity: row.sensitivity || 'internal',
|
|
726
|
+
creator_scope: row.creator_scope ? JSON.parse(row.creator_scope) : null,
|
|
727
|
+
origin: row.origin ? JSON.parse(row.origin) : undefined,
|
|
728
|
+
propagation_path: JSON.parse(row.propagation_path || '[]'),
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
- [ ] **Step 5: Run tests to verify they pass**
|
|
732
|
+
|
|
733
|
+
Run: `npx vitest run tests/lib/cortex/store.test.ts`
|
|
734
|
+
Expected: All tests pass (existing + 2 new)
|
|
735
|
+
|
|
736
|
+
- [ ] **Step 6: Commit**
|
|
737
|
+
|
|
738
|
+
```bash
|
|
739
|
+
git add src/lib/cortex/store.ts tests/lib/cortex/store.test.ts
|
|
740
|
+
git commit -m "feat(cortex): evolve LanceDB schema with v2 knowledge unit fields"
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
---
|
|
744
|
+
|
|
745
|
+
### Task 5: Update search and scoring for v2
|
|
746
|
+
|
|
747
|
+
**Files:**
|
|
748
|
+
- Modify: `src/lib/cortex/retrieval/scoring.ts`
|
|
749
|
+
- Modify: `src/lib/cortex/retrieval/search.ts`
|
|
750
|
+
- Modify: `tests/lib/cortex/retrieval/search.test.ts`
|
|
751
|
+
|
|
752
|
+
- [ ] **Step 1: Read current search.ts and scoring.ts**
|
|
753
|
+
|
|
754
|
+
Read both files to understand the current scoring formula and search layer iteration.
|
|
755
|
+
|
|
756
|
+
- [ ] **Step 2: Write a failing test for evidence-aware scoring**
|
|
757
|
+
|
|
758
|
+
Add to `tests/lib/cortex/retrieval/scoring.test.ts` (or the existing scoring test file):
|
|
759
|
+
|
|
760
|
+
```typescript
|
|
761
|
+
import { computeRelevanceScore } from '@/lib/cortex/retrieval/scoring';
|
|
762
|
+
|
|
763
|
+
it('uses evidence_score when provided instead of confidence', () => {
|
|
764
|
+
const withConfidenceOnly = computeRelevanceScore({
|
|
765
|
+
similarity: 0.9, confidence: 0.8, stale_score: 0, created: new Date().toISOString(),
|
|
766
|
+
});
|
|
767
|
+
const withHighEvidence = computeRelevanceScore({
|
|
768
|
+
similarity: 0.9, confidence: 0.8, stale_score: 0, created: new Date().toISOString(),
|
|
769
|
+
evidence_score: 0.95,
|
|
770
|
+
});
|
|
771
|
+
const withLowEvidence = computeRelevanceScore({
|
|
772
|
+
similarity: 0.9, confidence: 0.8, stale_score: 0, created: new Date().toISOString(),
|
|
773
|
+
evidence_score: 0.3,
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
// evidence_score should override confidence in the formula
|
|
777
|
+
expect(withHighEvidence).toBeGreaterThan(withConfidenceOnly);
|
|
778
|
+
expect(withLowEvidence).toBeLessThan(withConfidenceOnly);
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
it('falls back to confidence when evidence_score is undefined', () => {
|
|
782
|
+
const result = computeRelevanceScore({
|
|
783
|
+
similarity: 0.9, confidence: 0.8, stale_score: 0, created: new Date().toISOString(),
|
|
784
|
+
});
|
|
785
|
+
// Should use confidence (0.8) as the evidence factor
|
|
786
|
+
expect(result).toBeGreaterThan(0);
|
|
787
|
+
});
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
- [ ] **Step 3: Update scoring.ts to incorporate evidence_score**
|
|
791
|
+
|
|
792
|
+
Add `evidence_score` as an optional parameter to `computeRelevanceScore`:
|
|
793
|
+
|
|
794
|
+
```typescript
|
|
795
|
+
export function computeRelevanceScore(params: {
|
|
796
|
+
similarity: number;
|
|
797
|
+
confidence: number;
|
|
798
|
+
stale_score: number;
|
|
799
|
+
created: string;
|
|
800
|
+
evidence_score?: number; // NEW: v2 evidence score
|
|
801
|
+
}): number {
|
|
802
|
+
const recencyBoost = computeRecencyBoost(params.created);
|
|
803
|
+
const evidence = params.evidence_score ?? params.confidence; // fallback to confidence for v1 data
|
|
804
|
+
return Math.min(1.0, params.similarity * evidence * (1 - params.stale_score) * recencyBoost);
|
|
805
|
+
}
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
- [ ] **Step 4: Update search.ts to pass evidence_score in ALL scoring calls**
|
|
809
|
+
|
|
810
|
+
In the search method, pass `evidence_score` in BOTH the initial scoring AND the staleness recomputation:
|
|
811
|
+
|
|
812
|
+
```typescript
|
|
813
|
+
// Initial scoring (in the per-layer loop):
|
|
814
|
+
const relevance = computeRelevanceScore({
|
|
815
|
+
similarity,
|
|
816
|
+
confidence: unit.confidence,
|
|
817
|
+
stale_score: unit.stale_score,
|
|
818
|
+
created: unit.created,
|
|
819
|
+
evidence_score: unit.evidence_score, // NEW
|
|
820
|
+
}) * weight;
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
Also find the staleness recomputation block (where `computeStaleness` results are used to re-score) and pass `evidence_score` there too:
|
|
824
|
+
```typescript
|
|
825
|
+
// After staleness recomputation:
|
|
826
|
+
r.relevance_score = computeRelevanceScore({
|
|
827
|
+
similarity: r.similarity,
|
|
828
|
+
confidence: r.confidence,
|
|
829
|
+
stale_score: r.stale_score,
|
|
830
|
+
created: r.created,
|
|
831
|
+
evidence_score: r.evidence_score, // NEW — must be here too
|
|
832
|
+
}) * (LAYER_WEIGHTS[r.layer] ?? 0.5);
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
- [ ] **Step 5: Run tests to verify they pass**
|
|
836
|
+
|
|
837
|
+
Run: `npx vitest run tests/lib/cortex/retrieval/`
|
|
838
|
+
Expected: All tests pass
|
|
839
|
+
|
|
840
|
+
- [ ] **Step 6: Commit**
|
|
841
|
+
|
|
842
|
+
```bash
|
|
843
|
+
git add src/lib/cortex/retrieval/scoring.ts src/lib/cortex/retrieval/search.ts tests/lib/cortex/retrieval/search.test.ts
|
|
844
|
+
git commit -m "feat(cortex): incorporate evidence_score into retrieval scoring"
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
---
|
|
848
|
+
|
|
849
|
+
## Chunk 3: API Backward Compatibility
|
|
850
|
+
|
|
851
|
+
### Task 6: Update knowledge API to accept both layer and scope
|
|
852
|
+
|
|
853
|
+
**Files:**
|
|
854
|
+
- Modify: `src/app/api/cortex/knowledge/route.ts`
|
|
855
|
+
|
|
856
|
+
- [ ] **Step 1: Read current knowledge/route.ts**
|
|
857
|
+
|
|
858
|
+
- [ ] **Step 2: Update POST handler to accept both formats**
|
|
859
|
+
|
|
860
|
+
The POST handler currently requires `layer`. Update it to:
|
|
861
|
+
1. Accept `scope` as an alternative to `layer`
|
|
862
|
+
2. If `scope` is provided, use it directly and derive `layer` for backward compat
|
|
863
|
+
3. If only `layer` is provided, derive `scope` from it
|
|
864
|
+
4. Accept new v2 fields: `sensitivity`, `origin`, `entity_links`
|
|
865
|
+
|
|
866
|
+
```typescript
|
|
867
|
+
// In POST handler, after parsing body:
|
|
868
|
+
const { text, type, workspace_id } = body;
|
|
869
|
+
let { layer, scope, sensitivity, origin, entity_links } = body;
|
|
870
|
+
|
|
871
|
+
// Validate: need either layer or scope
|
|
872
|
+
if (!text || !type) {
|
|
873
|
+
return NextResponse.json({ error: 'text and type are required' }, { status: 400 });
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
if (!layer && !scope) {
|
|
877
|
+
return NextResponse.json({ error: 'layer or scope is required' }, { status: 400 });
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// Resolve layer ↔ scope
|
|
881
|
+
if (scope && !layer) {
|
|
882
|
+
layer = scopeToLayer(scope);
|
|
883
|
+
} else if (layer && !scope) {
|
|
884
|
+
scope = layerToScope(layer, workspace_id);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// Compute layerKey from scope (or fallback to old method)
|
|
888
|
+
const layerKey = scope
|
|
889
|
+
? scopeToLayerKey(scope, workspace_id)
|
|
890
|
+
: (layer === 'workspace' && workspace_id ? `workspace/${workspace_id}` : layer);
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
Import the compat functions:
|
|
894
|
+
```typescript
|
|
895
|
+
import { layerToScope, scopeToLayer, scopeToLayerKey } from '@/lib/cortex/knowledge/compat';
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
Add v2 fields to the unit construction. Use `getConfidenceBase(type)` for initial evidence_score (not a flat 0.5 — per spec, evidence starts from the type's base confidence). Derive `creator_entity_id` from the authenticated user:
|
|
899
|
+
|
|
900
|
+
```typescript
|
|
901
|
+
import { getConfidenceBase } from '@/lib/cortex/knowledge/types';
|
|
902
|
+
|
|
903
|
+
// In unit construction:
|
|
904
|
+
scope,
|
|
905
|
+
entity_links: entity_links ?? [],
|
|
906
|
+
evidence_score: getConfidenceBase(type), // NOT 0.5 — matches spec
|
|
907
|
+
corroborations: 0,
|
|
908
|
+
contradiction_refs: [],
|
|
909
|
+
sensitivity: sensitivity ?? 'internal',
|
|
910
|
+
creator_scope: null,
|
|
911
|
+
origin: origin ?? { source_type: 'manual', source_ref: '', creator_entity_id: `person-${user}` },
|
|
912
|
+
propagation_path: [],
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
Note: `user` is already available from `getAuthUser(request)` at the top of the handler.
|
|
916
|
+
|
|
917
|
+
- [ ] **Step 3: Commit**
|
|
918
|
+
|
|
919
|
+
```bash
|
|
920
|
+
git add src/app/api/cortex/knowledge/route.ts
|
|
921
|
+
git commit -m "feat(cortex): accept both layer and scope in knowledge API"
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
---
|
|
925
|
+
|
|
926
|
+
### Task 7: Update MCP tools to accept both layer and scope
|
|
927
|
+
|
|
928
|
+
**Files:**
|
|
929
|
+
- Modify: `src/lib/cortex/mcp/server.ts`
|
|
930
|
+
|
|
931
|
+
- [ ] **Step 1: Read current mcp/server.ts**
|
|
932
|
+
|
|
933
|
+
Read the `cortex_teach` tool schema and handler to understand what fields are accepted.
|
|
934
|
+
|
|
935
|
+
- [ ] **Step 2: Update cortex_teach tool**
|
|
936
|
+
|
|
937
|
+
1. Add `scope` as an optional property in the tool's input schema (alongside existing `layer`)
|
|
938
|
+
2. In the handler, apply the same layer↔scope resolution logic from Task 6
|
|
939
|
+
3. Add `sensitivity` and `origin` as optional properties
|
|
940
|
+
4. Include v2 fields in the unit construction, using `getConfidenceBase(type)` for evidence_score (not 0.5)
|
|
941
|
+
5. Derive `creator_entity_id` from the agent context (use `'person-default-user'` as placeholder since MCP tools don't have user auth context — the real entity ID will be resolved by Pillar 5)
|
|
942
|
+
|
|
943
|
+
The key change: `layer` remains supported but `scope` is preferred. If both are provided, `scope` wins.
|
|
944
|
+
|
|
945
|
+
Import compat functions:
|
|
946
|
+
```typescript
|
|
947
|
+
import { layerToScope, scopeToLayer, scopeToLayerKey } from '../knowledge/compat';
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
- [ ] **Step 3: Commit**
|
|
951
|
+
|
|
952
|
+
```bash
|
|
953
|
+
git add src/lib/cortex/mcp/server.ts
|
|
954
|
+
git commit -m "feat(cortex): accept both layer and scope in MCP cortex_teach tool"
|
|
955
|
+
```
|
|
956
|
+
|
|
957
|
+
---
|
|
958
|
+
|
|
959
|
+
### Task 8: Update learn hook for v2 compatibility
|
|
960
|
+
|
|
961
|
+
**Files:**
|
|
962
|
+
- Modify: `bin/cortex-learn-hook.js`
|
|
963
|
+
|
|
964
|
+
- [ ] **Step 1: Read current cortex-learn-hook.js**
|
|
965
|
+
|
|
966
|
+
- [ ] **Step 2: Add scope field alongside layer**
|
|
967
|
+
|
|
968
|
+
The hook currently POSTs `{ text, type, layer: 'personal' }`. Update both entries to also include:
|
|
969
|
+
|
|
970
|
+
```javascript
|
|
971
|
+
const scope = JSON.stringify({ level: 'personal', entity_id: 'person-default-user' });
|
|
972
|
+
|
|
973
|
+
// Question entry:
|
|
974
|
+
const questionEntry = JSON.stringify({
|
|
975
|
+
text: lastExchange.question,
|
|
976
|
+
type: 'context',
|
|
977
|
+
layer: 'personal',
|
|
978
|
+
scope: { level: 'personal', entity_id: 'person-default-user' },
|
|
979
|
+
origin: { source_type: 'conversation', source_ref: sessionId || '', creator_entity_id: 'person-default-user' },
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
// Answer entry:
|
|
983
|
+
const answerEntry = JSON.stringify({
|
|
984
|
+
text: `Q: ${lastExchange.question}\nA: ${condensedAnswer}`,
|
|
985
|
+
type: knowledgeType,
|
|
986
|
+
layer: 'personal',
|
|
987
|
+
scope: { level: 'personal', entity_id: 'person-default-user' },
|
|
988
|
+
origin: { source_type: 'conversation', source_ref: sessionId || '', creator_entity_id: 'person-default-user' },
|
|
989
|
+
});
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
Note: `sessionId` should be extracted from the input JSON if available (the hook receives `{ transcript_path }` — the session ID is the filename without extension).
|
|
993
|
+
|
|
994
|
+
- [ ] **Step 3: Commit**
|
|
995
|
+
|
|
996
|
+
```bash
|
|
997
|
+
git add bin/cortex-learn-hook.js
|
|
998
|
+
git commit -m "feat(cortex): add scope and origin to learn hook knowledge entries"
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
---
|
|
1002
|
+
|
|
1003
|
+
## Chunk 4: Pipeline Integration and Final Tests
|
|
1004
|
+
|
|
1005
|
+
### Task 9: Update ingestion pipeline for v2 fields
|
|
1006
|
+
|
|
1007
|
+
**Files:**
|
|
1008
|
+
- Modify: `src/lib/cortex/ingestion/pipeline.ts`
|
|
1009
|
+
- Modify: `src/lib/cortex/ingestion/chunker.ts`
|
|
1010
|
+
|
|
1011
|
+
- [ ] **Step 1: Read pipeline.ts and chunker.ts**
|
|
1012
|
+
|
|
1013
|
+
- [ ] **Step 2: Update RawChunk → KnowledgeUnit mapping in pipeline**
|
|
1014
|
+
|
|
1015
|
+
In the pipeline where KnowledgeUnit is constructed from RawChunk, add v2 fields with defaults:
|
|
1016
|
+
|
|
1017
|
+
```typescript
|
|
1018
|
+
const unit: KnowledgeUnit = {
|
|
1019
|
+
// ... existing v1 fields ...
|
|
1020
|
+
// v2 fields with defaults
|
|
1021
|
+
scope: layerToScope(chunk.layer, chunk.workspace_id),
|
|
1022
|
+
entity_links: [],
|
|
1023
|
+
evidence_score: getConfidenceBase(chunk.type), // start with base confidence
|
|
1024
|
+
corroborations: 0,
|
|
1025
|
+
contradiction_refs: [],
|
|
1026
|
+
sensitivity: 'internal',
|
|
1027
|
+
creator_scope: null,
|
|
1028
|
+
origin: {
|
|
1029
|
+
source_type: 'conversation',
|
|
1030
|
+
source_ref: chunk.session_id ?? '',
|
|
1031
|
+
creator_entity_id: 'person-default-user',
|
|
1032
|
+
},
|
|
1033
|
+
propagation_path: [],
|
|
1034
|
+
};
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
Import the compat function:
|
|
1038
|
+
```typescript
|
|
1039
|
+
import { layerToScope } from '../knowledge/compat';
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
- [ ] **Step 3: Run the full cortex test suite**
|
|
1043
|
+
|
|
1044
|
+
Run: `npx vitest run tests/lib/cortex/`
|
|
1045
|
+
Expected: All tests pass (including existing pipeline tests)
|
|
1046
|
+
|
|
1047
|
+
- [ ] **Step 4: Commit**
|
|
1048
|
+
|
|
1049
|
+
```bash
|
|
1050
|
+
git add src/lib/cortex/ingestion/pipeline.ts
|
|
1051
|
+
git commit -m "feat(cortex): add v2 fields to ingestion pipeline output"
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
---
|
|
1055
|
+
|
|
1056
|
+
### Task 10: Export knowledge/compat and evidence from knowledge index
|
|
1057
|
+
|
|
1058
|
+
**Files:**
|
|
1059
|
+
- Modify or create: `src/lib/cortex/knowledge/index.ts` (if a barrel exists, update it; if not, create one)
|
|
1060
|
+
|
|
1061
|
+
- [ ] **Step 1: Check if knowledge/index.ts exists**
|
|
1062
|
+
|
|
1063
|
+
Read `src/lib/cortex/knowledge/` directory to see if there's a barrel export.
|
|
1064
|
+
|
|
1065
|
+
- [ ] **Step 2: Create or update barrel export**
|
|
1066
|
+
|
|
1067
|
+
Ensure these are re-exported:
|
|
1068
|
+
```typescript
|
|
1069
|
+
export { layerToScope, scopeToLayer, scopeToLayerKey, layerKeyToScope } from './compat';
|
|
1070
|
+
export { computeEvidenceScore, AUTHORITY_FACTORS } from './evidence';
|
|
1071
|
+
export type { EvidenceScoreInput } from './evidence';
|
|
1072
|
+
// Existing type exports from types.ts should already be accessible
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
- [ ] **Step 3: Run the full test suite**
|
|
1076
|
+
|
|
1077
|
+
Run: `npx vitest run tests/lib/cortex/`
|
|
1078
|
+
Expected: All tests pass
|
|
1079
|
+
|
|
1080
|
+
- [ ] **Step 4: Commit**
|
|
1081
|
+
|
|
1082
|
+
```bash
|
|
1083
|
+
git add src/lib/cortex/knowledge/
|
|
1084
|
+
git commit -m "feat(cortex): export compat and evidence modules from knowledge barrel"
|
|
1085
|
+
```
|
|
1086
|
+
|
|
1087
|
+
---
|
|
1088
|
+
|
|
1089
|
+
## Summary
|
|
1090
|
+
|
|
1091
|
+
| Task | Component | Tests | Status |
|
|
1092
|
+
|------|-----------|-------|--------|
|
|
1093
|
+
| 1 | v2 type definitions | — | |
|
|
1094
|
+
| 2 | v1↔v2 compatibility layer | 10 | |
|
|
1095
|
+
| 3 | Evidence score computation | 8 | |
|
|
1096
|
+
| 4 | Arrow schema evolution | 2 | |
|
|
1097
|
+
| 5 | Search/scoring update | 2 | |
|
|
1098
|
+
| 6 | Knowledge API backward compat | — | |
|
|
1099
|
+
| 7 | MCP tool backward compat | — | |
|
|
1100
|
+
| 8 | Learn hook v2 fields | — | |
|
|
1101
|
+
| 9 | Pipeline v2 integration | regression | |
|
|
1102
|
+
| 10 | Barrel export | regression | |
|
|
1103
|
+
|
|
1104
|
+
**Total: 10 tasks, ~22 new tests, 4 chunks**
|
|
1105
|
+
|
|
1106
|
+
**Key design decisions:**
|
|
1107
|
+
- `layer` field is KEPT on KnowledgeUnit for backward compat (intentional spec deviation — spec says "removed" but we keep it as derived field)
|
|
1108
|
+
- v2 fields are all optional/nullable — existing LanceDB tables migrated in-place via `addColumns()` during `store.init()`
|
|
1109
|
+
- `updateAccessCount()` and `browse()` updated to preserve/parse v2 fields
|
|
1110
|
+
- API accepts both `layer` and `scope` params — `scope` takes precedence
|
|
1111
|
+
- Evidence score starts at `base_confidence` and evolves with access/corroboration (computed by Pillar 6: Gravity)
|
|
1112
|
+
|
|
1113
|
+
After this plan is complete, both the Entity Graph (Pillar 1) and Knowledge Unit Evolution (Pillar 2) will be in place — enabling Pillar 3 (Context Assembly Engine) and Pillar 4 (Boundary Engine) to build on them.
|