@jlongo78/agent-spaces 0.9.6 → 0.9.8
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/.claude/settings.local.json +68 -0
- package/.next/standalone/.claude/spaces-env.json +1 -0
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +1 -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 +6 -0
- 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_client-reference-manifest.js +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_client-reference-manifest.js +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_client-reference-manifest.js +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.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_client-reference-manifest.js +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.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_client-reference-manifest.js +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_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 +2 -2
- package/.next/standalone/.next/server/app/admin/analytics.segments/!KGRlc2t0b3Ap/admin/analytics/__PAGE__.segment.rsc +1 -1
- 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 +1 -1
- package/.next/standalone/.next/server/app/admin/analytics.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- package/.next/standalone/.next/server/app/admin/users.segments/!KGRlc2t0b3Ap/admin/users/__PAGE__.segment.rsc +1 -1
- 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 +1 -1
- package/.next/standalone/.next/server/app/admin/users.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- package/.next/standalone/.next/server/app/analytics.segments/!KGRlc2t0b3Ap/analytics/__PAGE__.segment.rsc +1 -1
- 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 +1 -1
- package/.next/standalone/.next/server/app/analytics.segments/_full.segment.rsc +2 -2
- 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 +1 -1
- package/.next/standalone/.next/server/app/api/analytics/overview/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/lobes/route.js +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/lobes/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/run/route.js +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/runs/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/runs/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/runs/route.js +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/runs/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/status/route.js +1 -1
- package/.next/standalone/.next/server/app/api/benchmark/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bulk/route.js +1 -1
- package/.next/standalone/.next/server/app/api/bulk/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/config/route.js +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 +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.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/assess/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/publish/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/refine/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/review/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/curation/seed/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/export/route.js +1 -1
- 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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/marketplace/browse/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route.js +1 -1
- package/.next/standalone/.next/server/app/api/cortex/marketplace/preview/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/cortex/mcp/call/route.js +1 -1
- 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 +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 +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 +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 +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 +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 +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 +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 +1 -1
- package/.next/standalone/.next/server/app/api/events/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/files/route.js +1 -1
- package/.next/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/folders/route.js +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 +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/panes/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/panes/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/panes/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/panes/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/projects/route.js +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 +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 +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 +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 +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 +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]/diff/route.js +1 -1
- package/.next/standalone/.next/server/app/api/panes/[id]/diff/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/panes/[id]/route.js +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 +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 +1 -1
- package/.next/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/[...path]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/status/route.js +1 -1
- package/.next/standalone/.next/server/app/api/proxy/models/[modelId]/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/search/route.js +2 -2
- 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 +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 +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 +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 +2 -2
- package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sync/route.js +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 +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 +1 -1
- package/.next/standalone/.next/server/app/api/tier/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/whisper/config/route.js +1 -1
- package/.next/standalone/.next/server/app/api/whisper/config/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/whisper/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wizard/chart/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/wizard/chart/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/wizard/chart/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/wizard/chart/route.js +7 -0
- package/.next/standalone/.next/server/app/api/wizard/chart/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/wizard/chart/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/wizard/chart/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/wizard/chat/route.js +1 -1
- package/.next/standalone/.next/server/app/api/wizard/chat/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/workspaces/[id]/context/[key]/route.js +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 +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 +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 +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 +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 +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 +2 -2
- 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 +3 -3
- 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 +1 -1
- package/.next/standalone/.next/server/app/cortex.segments/_full.segment.rsc +3 -3
- 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_client-reference-manifest.js +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 +2 -2
- package/.next/standalone/.next/server/app/m/projects.segments/_full.segment.rsc +2 -2
- 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 +1 -1
- 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 +1 -1
- 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_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 +2 -2
- package/.next/standalone/.next/server/app/m/sessions.segments/_full.segment.rsc +2 -2
- 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 +1 -1
- 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 +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 +2 -2
- package/.next/standalone/.next/server/app/m/settings.segments/_full.segment.rsc +2 -2
- 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 +1 -1
- 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 +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 +3 -3
- package/.next/standalone/.next/server/app/m/terminal.segments/_full.segment.rsc +3 -3
- 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 +1 -1
- package/.next/standalone/.next/server/app/m.html +1 -1
- package/.next/standalone/.next/server/app/m.rsc +2 -2
- package/.next/standalone/.next/server/app/m.segments/_full.segment.rsc +2 -2
- 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 +1 -1
- package/.next/standalone/.next/server/app/m.segments/m.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/network.html +1 -1
- package/.next/standalone/.next/server/app/network.rsc +2 -2
- package/.next/standalone/.next/server/app/network.segments/!KGRlc2t0b3Ap/network/__PAGE__.segment.rsc +1 -1
- 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 +1 -1
- package/.next/standalone/.next/server/app/network.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- package/.next/standalone/.next/server/app/projects.segments/!KGRlc2t0b3Ap/projects/__PAGE__.segment.rsc +1 -1
- 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 +1 -1
- package/.next/standalone/.next/server/app/projects.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- package/.next/standalone/.next/server/app/sessions.segments/!KGRlc2t0b3Ap/sessions/__PAGE__.segment.rsc +1 -1
- 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 +1 -1
- package/.next/standalone/.next/server/app/sessions.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- package/.next/standalone/.next/server/app/settings.segments/!KGRlc2t0b3Ap/settings/__PAGE__.segment.rsc +1 -1
- 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 +1 -1
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +2 -2
- 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 +3 -3
- 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 +1 -1
- package/.next/standalone/.next/server/app/terminal.segments/_full.segment.rsc +3 -3
- 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/vr/page/react-loadable-manifest.json +1 -1
- package/.next/standalone/.next/server/app/vr/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/vr.html +1 -1
- package/.next/standalone/.next/server/app/vr.rsc +3 -3
- package/.next/standalone/.next/server/app/vr.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/vr.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/vr.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/vr.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/vr.segments/vr/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/vr.segments/vr.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/workspaces.html +1 -1
- package/.next/standalone/.next/server/app/workspaces.rsc +2 -2
- package/.next/standalone/.next/server/app/workspaces.segments/!KGRlc2t0b3Ap/workspaces/__PAGE__.segment.rsc +1 -1
- 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 +1 -1
- package/.next/standalone/.next/server/app/workspaces.segments/_full.segment.rsc +2 -2
- 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 +1 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__00e90fc6._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__01ab8675._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__03974f05._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__bb331da9._.js → [root-of-the-server]__046c9b91._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__04ae6bf0._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__056fa416._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ac4ea3f._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0b8e64cb._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__d95165f0._.js → [root-of-the-server]__0facd39e._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__10bc76a3._.js +3 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__9d68157b._.js → [root-of-the-server]__115f3934._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__11f155f1._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__160e7c73._.js +22 -33
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__91e23c96._.js → [root-of-the-server]__17a3b966._.js} +3 -3
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__277d9445._.js → [root-of-the-server]__17d3a2b2._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1a86c055._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__20b5e9c4._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__28d6fbd8._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__04f04898._.js → [root-of-the-server]__2a3f866b._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__a95bb38b._.js → [root-of-the-server]__316617e7._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__32ad8f71._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__6fe5e6c8._.js → [root-of-the-server]__35457394._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__1f054c65._.js → [root-of-the-server]__35de78e6._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__3685ffcb._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__614458b7._.js → [root-of-the-server]__38954988._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__426ad936._.js +106 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__4985c034._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__5c6ce9ed._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__5cebe58a._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__5d5e4789._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__65676930._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__67cab326._.js +58 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__698c6f01._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__6c64af29._.js +131 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__73aed9f5._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__ac84b704._.js → [root-of-the-server]__79b6a9bb._.js} +3 -3
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__e56abacf._.js → [root-of-the-server]__7db704c6._.js} +4 -4
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__812ca02b._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__821f50fa._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__8716b86e._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__884ef754._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__88cdbd68._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__89d9aba9._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__8d536cb5._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__8df8c5d1._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__2d7a454e._.js → [root-of-the-server]__8f2ccc41._.js} +3 -3
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__2a2c5fc5._.js → [root-of-the-server]__95c9d682._.js} +4 -4
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__9e5d7774._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__cc6e2885._.js → [root-of-the-server]__9edcff87._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__a049dfc2._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__a5b4bb9a._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__929ea03a._.js → [root-of-the-server]__a83262a1._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__a9cd1240._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__a9d7f822._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ad08c221._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ad585f2f._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__afcb8f7d._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__bc250d43._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__bce2a6e7._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c011bf91._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c0ac2895._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c130a00c._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c37d6380._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__cae392eb._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__cc2616bb._.js +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d501fa9b._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d59c6c15._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d5c1db32._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d5d92527._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__dba60c86._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__2384f98e._.js → [root-of-the-server]__de14b9ae._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__e10643d1._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__00fdfbda._.js → [root-of-the-server]__e2a996e5._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__4d903941._.js → [root-of-the-server]__e3477417._.js} +3 -3
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__32dc5513._.js → [root-of-the-server]__e4db362e._.js} +2 -2
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__49e42a3a._.js → [root-of-the-server]__e4e70b86._.js} +3 -3
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__ac39ecc7._.js → [root-of-the-server]__e54925af._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__e8edc5b0._.js +98 -0
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__eafd040b._.js → [root-of-the-server]__eab4d83b._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ead29015._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__194955d4._.js → [root-of-the-server]__f056fd83._.js} +3 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__f0e99572._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__fe1e16d0._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ff9cd277._.js +98 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ffaea2ce._.js +98 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_wizard_chart_route_actions_888e2ec1.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__2cffc362._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__f1050870._.js → [root-of-the-server]__47c97637._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__66aca5d4._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_17b946fd._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_2a1d79e7._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_5c3c4cfa._.js +7 -5
- package/.next/standalone/.next/server/chunks/ssr/_ba432382._.js +7 -5
- package/.next/standalone/.next/server/chunks/ssr/src_app_(desktop)_cortex_page_tsx_0f33d8b3._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_app_(desktop)_terminal_page_tsx_de5e8d85._.js +4 -4
- package/.next/standalone/.next/server/edge/chunks/[root-of-the-server]__90eeddae._.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/0852575eb90c1e8d.js +85 -0
- package/.next/standalone/.next/static/chunks/{74d0ac0b1d0a1b79.js → 10b00b4f66102dcf.js} +1 -1
- package/.next/standalone/.next/static/chunks/{16eb77953dee9ea3.js → 19c71a8376c23c58.js} +1 -1
- package/.next/standalone/.next/static/chunks/2b769d1597e4fc1c.css +3 -0
- package/.next/standalone/.next/static/chunks/{3e91fc608659c524.js → 350271fe79509caf.js} +1 -1
- package/.next/standalone/.next/static/chunks/{70423c7afd8abf5f.js → 597847c22200c212.js} +1 -1
- package/.next/standalone/.next/static/chunks/{fb0abd1933b2b2e1.js → 7f6a14f1849fa94d.js} +1 -1
- package/.next/standalone/.next/static/chunks/{7ecd9bbb0ce4d68a.js → b7c8fe9b7275a84f.js} +1 -1
- package/.next/standalone/.next/static/chunks/{6245135a7afb8c7b.js → c9b10fc55516d142.js} +8 -6
- package/.next/standalone/.next/static/chunks/d0065f48eab94944.js +1 -0
- package/.next/standalone/.next/static/chunks/{180c1b9ff31b979f.js → f7b34c807badf95d.js} +8 -6
- package/.next/standalone/.spaces/cortex-context.md +50 -144
- package/.next/standalone/LICENSE +661 -661
- package/.next/standalone/NOTICE +5 -5
- package/.next/standalone/README.md +131 -131
- package/.next/standalone/bin/cortex-hook.js +79 -79
- package/.next/standalone/bin/cortex-hook.sh +62 -62
- package/.next/standalone/bin/cortex-learn-hook.js +138 -138
- package/.next/standalone/bin/cortex-mcp.js +60 -60
- package/.next/standalone/bin/cortex-pi-extension.ts +170 -170
- package/.next/standalone/bin/fix-standalone-externals.js +79 -79
- package/.next/standalone/bin/lib/auto-setup.js +110 -110
- package/.next/standalone/bin/mdns-service.js +171 -171
- package/.next/standalone/bin/postinstall.js +35 -35
- package/.next/standalone/bin/setup-admin.js +195 -195
- package/.next/standalone/bin/spaces-dev.js +247 -247
- package/.next/standalone/bin/spaces-install.js +660 -660
- package/.next/standalone/bin/spaces-postinstall.js +50 -50
- package/.next/standalone/bin/spaces-reset-totp.js +50 -50
- package/.next/standalone/bin/spaces-service.js +1046 -1046
- package/.next/standalone/bin/spaces-setup.js +253 -253
- package/.next/standalone/bin/spaces.js +808 -805
- package/.next/standalone/bin/ssh-auth-keys.sh +68 -68
- package/.next/standalone/bin/terminal-server.js +2819 -2781
- package/.next/standalone/cortex-hook-debug.log +57 -23
- package/.next/standalone/docker-compose.yml +28 -28
- package/.next/standalone/docs/architecture.md +387 -387
- package/.next/standalone/docs/cortex-integration-reference.md +268 -268
- package/.next/standalone/docs/cortex.md +293 -293
- package/.next/standalone/docs/getting-started.md +96 -96
- package/.next/standalone/docs/plans/2026-02-24-multi-agent-sessions-design.md +133 -133
- package/.next/standalone/docs/plans/2026-02-24-multi-agent-sessions-plan.md +959 -959
- package/.next/standalone/docs/plans/2026-03-02-security-audit.md +229 -229
- package/.next/standalone/docs/plans/2026-03-07-service-command-design.md +146 -146
- package/.next/standalone/docs/plans/2026-03-07-service-command-plan.md +254 -254
- package/.next/standalone/docs/server-install.md +564 -564
- package/.next/standalone/docs/social-card.html +150 -150
- package/.next/standalone/docs/superpowers/plans/2026-03-12-spaces-cortex.md +5270 -5270
- 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 -1846
- package/.next/standalone/docs/superpowers/plans/2026-03-19-vr-phase1-shell.md +1639 -1639
- package/.next/standalone/docs/superpowers/plans/2026-03-27-dockview-pane-layout.md +98 -98
- package/.next/standalone/docs/superpowers/specs/2026-03-11-universe-view-design.md +320 -320
- package/.next/standalone/docs/superpowers/specs/2026-03-12-spaces-brain-design.md +720 -720
- 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-16-pane-ux-design.md +77 -77
- package/.next/standalone/docs/superpowers/specs/2026-03-18-cortex-ui-integration-design.md +341 -341
- package/.next/standalone/docs/superpowers/specs/2026-03-19-vr-phase1-shell-design.md +288 -288
- package/.next/standalone/docs/superpowers/specs/2026-03-27-pane-diff-review-and-project-wizard-design.md +322 -322
- package/.next/standalone/docs/tiers.md +104 -104
- package/.next/standalone/eslint.config.mjs +18 -18
- package/.next/standalone/next.config.ts +20 -20
- package/.next/standalone/nginx.conf +53 -53
- package/.next/standalone/node_modules/@img/sharp-win32-x64/lib/sharp-win32-x64.node +0 -0
- package/.next/standalone/node_modules/@img/{sharp-linux-x64 → sharp-win32-x64}/package.json +39 -46
- package/.next/standalone/package-lock.json +14985 -14986
- package/.next/standalone/package.json +111 -111
- package/.next/standalone/postcss.config.mjs +7 -7
- package/.next/standalone/scripts/rebuild.cmd +65 -65
- package/.next/standalone/scripts/rebuild.sh +59 -59
- package/.next/standalone/server.js +1 -1
- package/.next/standalone/src/app/(desktop)/admin/analytics/page.tsx +266 -266
- package/.next/standalone/src/app/(desktop)/admin/users/page.tsx +399 -399
- package/.next/standalone/src/app/(desktop)/analytics/page.tsx +166 -166
- package/.next/standalone/src/app/(desktop)/cortex/page.tsx +81 -81
- package/.next/standalone/src/app/(desktop)/dashboard-client.tsx +56 -56
- package/.next/standalone/src/app/(desktop)/layout.tsx +18 -18
- package/.next/standalone/src/app/(desktop)/network/page.tsx +137 -137
- package/.next/standalone/src/app/(desktop)/page.tsx +17 -17
- package/.next/standalone/src/app/(desktop)/projects/page.tsx +68 -68
- package/.next/standalone/src/app/(desktop)/sessions/[id]/page.tsx +519 -519
- package/.next/standalone/src/app/(desktop)/sessions/page.tsx +145 -145
- package/.next/standalone/src/app/(desktop)/settings/page.tsx +446 -446
- package/.next/standalone/src/app/(desktop)/terminal/layout.tsx +7 -7
- package/.next/standalone/src/app/(desktop)/terminal/page.tsx +1330 -1291
- package/.next/standalone/src/app/(desktop)/terminal/pane/[id]/page.tsx +211 -211
- package/.next/standalone/src/app/(desktop)/terminal/remote/[nodeId]/[workspaceId]/page.tsx +252 -252
- package/.next/standalone/src/app/(desktop)/workspaces/page.tsx +12 -12
- package/.next/standalone/src/app/api/admin/analytics/route.ts +10 -10
- package/.next/standalone/src/app/api/admin/users/[id]/route.ts +20 -20
- package/.next/standalone/src/app/api/admin/users/route.ts +15 -15
- package/.next/standalone/src/app/api/analytics/overview/route.ts +80 -80
- package/.next/standalone/src/app/api/auth/login/route.ts +10 -10
- package/.next/standalone/src/app/api/auth/logout/route.ts +9 -9
- package/.next/standalone/src/app/api/auth/me/route.ts +22 -22
- package/.next/standalone/src/app/api/auth/totp/setup/route.ts +10 -10
- package/.next/standalone/src/app/api/auth/totp/status/route.ts +10 -10
- package/.next/standalone/src/app/api/auth/totp/verify/route.ts +10 -10
- package/.next/standalone/src/app/api/benchmark/lobes/route.ts +16 -16
- package/.next/standalone/src/app/api/benchmark/run/route.ts +113 -92
- package/.next/standalone/src/app/api/benchmark/runs/[id]/route.ts +26 -26
- package/.next/standalone/src/app/api/benchmark/runs/route.ts +16 -16
- package/.next/standalone/src/app/api/benchmark/status/route.ts +35 -35
- package/.next/standalone/src/app/api/bulk/route.ts +34 -34
- package/.next/standalone/src/app/api/chat/route.ts +85 -85
- package/.next/standalone/src/app/api/config/route.ts +30 -30
- package/.next/standalone/src/app/api/cortex/context/route.ts +78 -78
- package/.next/standalone/src/app/api/cortex/curation/assess/route.ts +27 -27
- package/.next/standalone/src/app/api/cortex/curation/publish/route.ts +23 -23
- package/.next/standalone/src/app/api/cortex/curation/refine/route.ts +23 -23
- package/.next/standalone/src/app/api/cortex/curation/review/route.ts +29 -29
- package/.next/standalone/src/app/api/cortex/curation/seed/route.ts +23 -23
- 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 -75
- package/.next/standalone/src/app/api/cortex/import/status/route.ts +15 -15
- package/.next/standalone/src/app/api/cortex/ingest/bootstrap/route.ts +39 -39
- 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 +97 -97
- 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 -43
- package/.next/standalone/src/app/api/cortex/marketplace/preview/route.ts +46 -46
- package/.next/standalone/src/app/api/cortex/mcp/call/route.ts +11 -11
- package/.next/standalone/src/app/api/cortex/mcp/tools/route.ts +8 -8
- package/.next/standalone/src/app/api/cortex/search/route.ts +57 -45
- package/.next/standalone/src/app/api/cortex/settings/route.ts +35 -35
- package/.next/standalone/src/app/api/cortex/status/route.ts +169 -169
- 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/app/api/events/route.ts +40 -40
- package/.next/standalone/src/app/api/files/route.ts +187 -187
- package/.next/standalone/src/app/api/folders/route.ts +107 -97
- package/.next/standalone/src/app/api/network/connect-callback/route.ts +11 -11
- package/.next/standalone/src/app/api/network/connect-request/[id]/route.ts +11 -11
- package/.next/standalone/src/app/api/network/connect-request/route.ts +17 -17
- package/.next/standalone/src/app/api/network/discovered/route.ts +9 -9
- package/.next/standalone/src/app/api/network/handshake/route.ts +25 -25
- package/.next/standalone/src/app/api/network/health/route.ts +10 -10
- package/.next/standalone/src/app/api/network/identity/route.ts +15 -15
- package/.next/standalone/src/app/api/network/keys/[id]/route.ts +10 -10
- package/.next/standalone/src/app/api/network/keys/route.ts +15 -15
- package/.next/standalone/src/app/api/network/nodes/[id]/route.ts +15 -15
- package/.next/standalone/src/app/api/network/nodes/check/route.ts +9 -9
- package/.next/standalone/src/app/api/network/nodes/route.ts +15 -15
- package/.next/standalone/src/app/api/network/panes/[id]/route.ts +78 -62
- package/.next/standalone/src/app/api/network/panes/route.ts +61 -50
- package/.next/standalone/src/app/api/network/projects/route.ts +32 -25
- package/.next/standalone/src/app/api/network/proxy/[nodeId]/[...path]/route.ts +25 -25
- package/.next/standalone/src/app/api/network/search/route.ts +45 -38
- package/.next/standalone/src/app/api/network/sessions/[id]/messages/route.ts +43 -36
- package/.next/standalone/src/app/api/network/sessions/[id]/route.ts +41 -34
- package/.next/standalone/src/app/api/network/sessions/route.ts +50 -43
- package/.next/standalone/src/app/api/network/terminal/token/route.ts +10 -10
- package/.next/standalone/src/app/api/network/workspaces/[id]/route.ts +80 -71
- package/.next/standalone/src/app/api/network/workspaces/route.ts +87 -85
- package/.next/standalone/src/app/api/panes/[id]/diff/route.ts +121 -121
- package/.next/standalone/src/app/api/panes/[id]/route.ts +60 -60
- package/.next/standalone/src/app/api/panes/route.ts +39 -39
- package/.next/standalone/src/app/api/projects/route.ts +13 -13
- package/.next/standalone/src/app/api/proxy/models/[modelId]/[...path]/route.ts +80 -80
- package/.next/standalone/src/app/api/proxy/models/[modelId]/status/route.ts +33 -33
- package/.next/standalone/src/app/api/search/route.ts +47 -47
- package/.next/standalone/src/app/api/sessions/[id]/chat/route.ts +120 -120
- package/.next/standalone/src/app/api/sessions/[id]/messages/route.ts +34 -34
- package/.next/standalone/src/app/api/sessions/[id]/route.ts +73 -73
- package/.next/standalone/src/app/api/sessions/route.ts +64 -64
- package/.next/standalone/src/app/api/sync/route.ts +24 -24
- package/.next/standalone/src/app/api/tags/route.ts +35 -35
- package/.next/standalone/src/app/api/tier/route.ts +16 -16
- package/.next/standalone/src/app/api/updates/route.ts +65 -65
- package/.next/standalone/src/app/api/whisper/config/route.ts +50 -42
- package/.next/standalone/src/app/api/whisper/route.ts +91 -91
- package/.next/standalone/src/app/api/wizard/chart/route.ts +129 -0
- package/.next/standalone/src/app/api/wizard/chat/route.ts +113 -113
- package/.next/standalone/src/app/api/workspaces/[id]/context/[key]/route.ts +39 -39
- package/.next/standalone/src/app/api/workspaces/[id]/context/route.ts +28 -28
- package/.next/standalone/src/app/api/workspaces/[id]/messages/[msgId]/route.ts +17 -17
- package/.next/standalone/src/app/api/workspaces/[id]/messages/route.ts +39 -39
- package/.next/standalone/src/app/api/workspaces/[id]/route.ts +47 -47
- package/.next/standalone/src/app/api/workspaces/[id]/sessions/route.ts +62 -62
- package/.next/standalone/src/app/api/workspaces/route.ts +79 -79
- package/.next/standalone/src/app/globals.css +88 -88
- package/.next/standalone/src/app/layout.tsx +33 -33
- package/.next/standalone/src/app/login/layout.tsx +7 -7
- package/.next/standalone/src/app/login/page.tsx +315 -315
- package/.next/standalone/src/app/m/layout.tsx +16 -16
- package/.next/standalone/src/app/m/page.tsx +118 -118
- package/.next/standalone/src/app/m/projects/page.tsx +64 -64
- package/.next/standalone/src/app/m/sessions/[id]/page.tsx +168 -168
- package/.next/standalone/src/app/m/sessions/page.tsx +177 -177
- package/.next/standalone/src/app/m/settings/page.tsx +230 -230
- package/.next/standalone/src/app/m/terminal/page.tsx +413 -413
- package/.next/standalone/src/app/vr/page.tsx +21 -21
- package/.next/standalone/src/app/vr/vr-app.tsx +163 -163
- package/.next/standalone/src/app/vr/vr-controls.tsx +139 -139
- package/.next/standalone/src/app/vr/vr-door.tsx +82 -82
- package/.next/standalone/src/app/vr/vr-environment.tsx +71 -71
- package/.next/standalone/src/app/vr/vr-gaze.tsx +89 -89
- package/.next/standalone/src/app/vr/vr-layout.ts +49 -49
- package/.next/standalone/src/app/vr/vr-lobby.tsx +97 -97
- package/.next/standalone/src/app/vr/vr-pane.tsx +195 -195
- package/.next/standalone/src/app/vr/vr-room.tsx +79 -79
- package/.next/standalone/src/app/vr/vr-terminal.tsx +303 -303
- package/.next/standalone/src/components/auth/totp-gate.tsx +183 -183
- package/.next/standalone/src/components/bus/activity-panel.tsx +261 -261
- package/.next/standalone/src/components/common/color-picker.tsx +35 -35
- package/.next/standalone/src/components/common/dev-directory-picker.tsx +339 -339
- package/.next/standalone/src/components/common/folder-picker.tsx +200 -200
- package/.next/standalone/src/components/common/tag-picker.tsx +190 -190
- package/.next/standalone/src/components/common/workspace-picker.tsx +113 -113
- package/.next/standalone/src/components/cortex/benchmark-tab.tsx +894 -880
- package/.next/standalone/src/components/cortex/constants.ts +29 -29
- package/.next/standalone/src/components/cortex/cortex-dashboard.tsx +304 -304
- 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 +280 -280
- package/.next/standalone/src/components/cortex/curation-tab.tsx +810 -810
- 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 -212
- package/.next/standalone/src/components/cortex/injection-badge.tsx +72 -72
- package/.next/standalone/src/components/cortex/knowledge-card.tsx +109 -109
- package/.next/standalone/src/components/cortex/knowledge-tab.tsx +158 -158
- package/.next/standalone/src/components/cortex/lobe-settings.tsx +215 -215
- package/.next/standalone/src/components/cortex/marketplace-card.tsx +126 -126
- package/.next/standalone/src/components/cortex/marketplace-tab.tsx +113 -113
- package/.next/standalone/src/components/dashboard/activity-chart.tsx +41 -41
- package/.next/standalone/src/components/dashboard/model-usage-chart.tsx +61 -61
- package/.next/standalone/src/components/dashboard/recent-sessions.tsx +68 -68
- package/.next/standalone/src/components/dashboard/stats-cards.tsx +36 -36
- package/.next/standalone/src/components/files/file-explorer.tsx +703 -703
- package/.next/standalone/src/components/layout/providers.tsx +38 -38
- package/.next/standalone/src/components/layout/sidebar.tsx +170 -170
- package/.next/standalone/src/components/layout/tier-provider.tsx +53 -53
- package/.next/standalone/src/components/layout/update-banner.tsx +92 -92
- package/.next/standalone/src/components/mobile/bottom-nav.tsx +46 -46
- package/.next/standalone/src/components/mobile/immersive-voice-button.tsx +123 -123
- package/.next/standalone/src/components/mobile/mobile-chat-input.tsx +244 -244
- package/.next/standalone/src/components/mobile/mobile-header.tsx +44 -44
- package/.next/standalone/src/components/mobile/mobile-session-card.tsx +56 -56
- package/.next/standalone/src/components/mobile/mobile-terminal-input.tsx +74 -74
- package/.next/standalone/src/components/mobile/mobile-terminal-pane.tsx +302 -302
- package/.next/standalone/src/components/mobile/mobile-terminal-toolbar.tsx +76 -76
- package/.next/standalone/src/components/mobile/pull-to-refresh.tsx +82 -82
- package/.next/standalone/src/components/mobile/voice-input.tsx +53 -53
- package/.next/standalone/src/components/network/api-key-list.tsx +190 -190
- package/.next/standalone/src/components/network/connection-requests.tsx +94 -94
- package/.next/standalone/src/components/network/node-add-dialog.tsx +131 -131
- package/.next/standalone/src/components/network/node-badge.tsx +26 -26
- package/.next/standalone/src/components/network/node-list.tsx +207 -207
- package/.next/standalone/src/components/network/node-selector.tsx +49 -49
- package/.next/standalone/src/components/sessions/session-filters.tsx +116 -116
- package/.next/standalone/src/components/sessions/session-list.tsx +485 -485
- package/.next/standalone/src/components/terminal/pane-diff-panel.tsx +179 -179
- package/.next/standalone/src/components/terminal/terminal-pane.tsx +1530 -1464
- package/.next/standalone/src/components/viewer/chat-input.tsx +275 -275
- package/.next/standalone/src/components/viewer/message-renderer.tsx +551 -551
- package/.next/standalone/src/components/wizard/chart-wizard.tsx +405 -0
- package/.next/standalone/src/components/wizard/project-wizard.tsx +153 -153
- package/.next/standalone/src/components/wizard/wizard-chat.tsx +99 -99
- package/.next/standalone/src/components/wizard/wizard-plan-summary.tsx +103 -103
- package/.next/standalone/src/components/wizard/wizard-review.tsx +225 -225
- package/.next/standalone/src/components/workspace/universe-cluster.tsx +131 -131
- package/.next/standalone/src/components/workspace/universe-orb.tsx +128 -128
- package/.next/standalone/src/components/workspace/universe-types.ts +22 -22
- package/.next/standalone/src/components/workspace/universe-utils.ts +11 -11
- package/.next/standalone/src/components/workspace/universe-view.tsx +397 -397
- package/.next/standalone/src/components/workspace/workspace-chooser.tsx +634 -634
- package/.next/standalone/src/hooks/use-benchmark.ts +72 -71
- package/.next/standalone/src/hooks/use-bus.ts +147 -147
- package/.next/standalone/src/hooks/use-idle-detection.ts +79 -79
- package/.next/standalone/src/hooks/use-network.ts +229 -229
- package/.next/standalone/src/hooks/use-sessions.ts +437 -437
- package/.next/standalone/src/hooks/use-speech-recognition.ts +114 -113
- package/.next/standalone/src/hooks/use-sse.ts +35 -35
- package/.next/standalone/src/hooks/use-tier.ts +39 -39
- package/.next/standalone/src/lib/agents.ts +97 -97
- package/.next/standalone/src/lib/aider/parser.ts +111 -111
- package/.next/standalone/src/lib/api.ts +19 -19
- package/.next/standalone/src/lib/auth.ts +47 -47
- package/.next/standalone/src/lib/claude/parser.ts +212 -212
- package/.next/standalone/src/lib/claude/stats.ts +204 -204
- package/.next/standalone/src/lib/codex/parser.test.ts +111 -111
- package/.next/standalone/src/lib/codex/parser.ts +287 -287
- package/.next/standalone/src/lib/config.ts +132 -132
- package/.next/standalone/src/lib/cortex/benchmark.ts +83 -67
- package/.next/standalone/src/lib/cortex/config.ts +42 -42
- 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 +109 -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 +12 -12
- 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 -39
- package/.next/standalone/src/lib/cost-calculator.ts +48 -48
- package/.next/standalone/src/lib/db/init.ts +71 -71
- package/.next/standalone/src/lib/db/queries.ts +740 -827
- package/.next/standalone/src/lib/db/schema.ts +206 -206
- package/.next/standalone/src/lib/events/sse.ts +36 -36
- package/.next/standalone/src/lib/forge/parser.ts +52 -52
- package/.next/standalone/src/lib/gemini/parser.ts +258 -258
- package/.next/standalone/src/lib/license.ts +56 -56
- package/.next/standalone/src/lib/pro.ts +31 -31
- package/.next/standalone/src/lib/shell-user.ts +101 -0
- package/.next/standalone/src/lib/sync/indexer.ts +504 -504
- package/.next/standalone/src/lib/sync/watcher.ts +64 -64
- package/.next/standalone/src/lib/teams.ts +31 -31
- package/.next/standalone/src/lib/telemetry.ts +75 -75
- package/.next/standalone/src/lib/terminal/server.ts +188 -188
- package/.next/standalone/src/lib/tier.ts +38 -38
- package/.next/standalone/src/lib/utils.ts +72 -72
- package/.next/standalone/src/lib/vms/manager.ts +121 -121
- package/.next/standalone/src/middleware.ts +133 -133
- package/.next/standalone/src/types/claude.ts +208 -208
- package/.next/standalone/src/types/network.ts +61 -61
- package/.next/standalone/tests/setup.ts +8 -8
- package/.next/standalone/tsconfig.json +34 -34
- package/.next/standalone/vitest.config.ts +24 -24
- package/LICENSE +661 -661
- package/README.md +131 -131
- package/bin/cortex-hook.js +79 -79
- package/bin/cortex-hook.sh +62 -62
- package/bin/cortex-learn-hook.js +138 -138
- package/bin/cortex-mcp.js +60 -60
- package/bin/cortex-pi-extension.ts +170 -170
- 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 +247 -247
- package/bin/spaces-install.js +660 -660
- package/bin/spaces-postinstall.js +50 -50
- package/bin/spaces-reset-totp.js +50 -50
- package/bin/spaces-service.js +1046 -1046
- package/bin/spaces-setup.js +253 -253
- package/bin/spaces.js +808 -805
- package/bin/ssh-auth-keys.sh +68 -68
- package/bin/terminal-server.js +2819 -2781
- package/package.json +111 -111
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0903a426._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__09e8ccc9._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__11c684b1._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1572d4ef._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__186cd0bb._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__212760e6._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__228595ec._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__283c890f._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__2f300a68._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__2f452778._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__35f8e77e._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__379fc2e9._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__3b40d79f._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__3d3dca2b._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__4d5b78d2._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__54163e52._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__563c0817._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__5812f90a._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__5c5e87f5._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__60d15b16._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__69d315e5._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__71f29038._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__74084e07._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__7921aa80._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__7e077dd8._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__7ebc4280._.js +0 -131
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__857c60bb._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__874fe565._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__8e2171f7._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__95659b2d._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__9679b91e._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__a90729a1._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ad4346fa._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b0862d69._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b43306ee._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b689ff5e._.js +0 -106
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ba87daaa._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c0461005._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c1deb5f3._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c8a62f42._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__cabaac2b._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__cb027619._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__cf608218._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__cfc1290d._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d0109b9b._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d0125483._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d048ee6b._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d12644e7._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__e3cc946c._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__e6fd27f8._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__efb8251e._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__f44c6882._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__f85283de._.js +0 -98
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__feceb3e4._.js +0 -98
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__843070a6._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_e84a0c06._.js +0 -3
- package/.next/standalone/.next/static/chunks/470cade58d4eceeb.css +0 -3
- package/.next/standalone/.next/static/chunks/9d4164833c2c1fd6.js +0 -85
- package/.next/standalone/.next/static/chunks/f091f4bf8d80fd07.js +0 -1
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/README.md +0 -46
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/glib-2.0/include/glibconfig.h +0 -221
- package/.next/standalone/node_modules/@img/sharp-libvips-linux-x64/lib/index.js +0 -1
- 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 +0 -42
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/README.md +0 -46
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/glib-2.0/include/glibconfig.h +0 -221
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/index.js +0 -1
- 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 +0 -42
- package/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/versions.json +0 -30
- package/.next/standalone/node_modules/@img/sharp-linux-x64/lib/sharp-linux-x64.node +0 -0
- 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 +0 -46
- /package/.next/standalone/.next/static/{5S4TviTCiNiTjf6KjXjBo → u1pHON3drz1mBi7owkbBP}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{5S4TviTCiNiTjf6KjXjBo → u1pHON3drz1mBi7owkbBP}/_clientMiddlewareManifest.json +0 -0
- /package/.next/standalone/.next/static/{5S4TviTCiNiTjf6KjXjBo → u1pHON3drz1mBi7owkbBP}/_ssgManifest.js +0 -0
- /package/.next/standalone/node_modules/@img/{sharp-libvips-linux-x64 → sharp-win32-x64}/versions.json +0 -0
|
@@ -1,853 +1,853 @@
|
|
|
1
|
-
# Cortex v2 — Pillar 4: Boundary Engine
|
|
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:** Add three-layer access control to Cortex: auto-classification of sensitivity, organizational policies, and creator overrides — ensuring confidential knowledge never leaks across scope boundaries.
|
|
6
|
-
|
|
7
|
-
**Architecture:** A new `src/lib/cortex/boundary/` module with three components: a `Classifier` (regex-based sensitivity detection), a `PolicyEngine` (rule evaluation), and an `AccessFilter` (query-time enforcement). The AccessFilter integrates with the ContextEngine to pre-filter scopes before search. An audit log records all access decisions in the entity graph's SQLite database.
|
|
8
|
-
|
|
9
|
-
**Tech Stack:** TypeScript, better-sqlite3 (existing graph DB), vitest
|
|
10
|
-
|
|
11
|
-
**Spec:** `docs/superpowers/specs/2026-03-14-cortex-v2-design.md` — Pillar 4
|
|
12
|
-
|
|
13
|
-
**Depends on:** Pillar 1 (Entity Graph) + Pillar 2 (Knowledge Unit Evolution) — both completed
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## File Structure
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
New files:
|
|
21
|
-
├── src/lib/cortex/boundary/classifier.ts — Auto-classification (Layer 1)
|
|
22
|
-
├── src/lib/cortex/boundary/policy.ts — Policy engine (Layer 2)
|
|
23
|
-
├── src/lib/cortex/boundary/access.ts — Query-time access filter (Layer 3 + enforcement)
|
|
24
|
-
├── src/lib/cortex/boundary/audit.ts — Audit trail logging
|
|
25
|
-
├── src/lib/cortex/boundary/index.ts — Barrel export
|
|
26
|
-
|
|
27
|
-
Modified files:
|
|
28
|
-
├── src/lib/cortex/ingestion/pipeline.ts — Auto-classify on ingestion
|
|
29
|
-
├── src/lib/cortex/retrieval/context-engine.ts — Pre-filter with AccessFilter
|
|
30
|
-
├── src/lib/cortex/config.ts — Add policies to config
|
|
31
|
-
|
|
32
|
-
Test files:
|
|
33
|
-
├── tests/lib/cortex/boundary/classifier.test.ts
|
|
34
|
-
├── tests/lib/cortex/boundary/policy.test.ts
|
|
35
|
-
├── tests/lib/cortex/boundary/access.test.ts
|
|
36
|
-
├── tests/lib/cortex/boundary/audit.test.ts
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## Chunk 1: Auto-Classification and Policy Engine
|
|
42
|
-
|
|
43
|
-
### Task 1: Sensitivity auto-classifier
|
|
44
|
-
|
|
45
|
-
**Files:**
|
|
46
|
-
- Create: `src/lib/cortex/boundary/classifier.ts`
|
|
47
|
-
- Create: `tests/lib/cortex/boundary/classifier.test.ts`
|
|
48
|
-
|
|
49
|
-
- [ ] **Step 1: Write failing tests**
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
// tests/lib/cortex/boundary/classifier.test.ts
|
|
53
|
-
import { describe, it, expect } from 'vitest';
|
|
54
|
-
import { classifySensitivity } from '@/lib/cortex/boundary/classifier';
|
|
55
|
-
|
|
56
|
-
describe('classifySensitivity', () => {
|
|
57
|
-
it('classifies secrets as confidential', () => {
|
|
58
|
-
expect(classifySensitivity('Set API_KEY=sk-ant-abc123 in .env')).toBe('confidential');
|
|
59
|
-
expect(classifySensitivity('password: hunter2')).toBe('confidential');
|
|
60
|
-
expect(classifySensitivity('DATABASE_URL=postgres://user:pass@host')).toBe('confidential');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('classifies personnel content as confidential', () => {
|
|
64
|
-
expect(classifySensitivity('Alice performance review: exceeds expectations')).toBe('confidential');
|
|
65
|
-
expect(classifySensitivity('salary adjustment from 120k to 140k')).toBe('confidential');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('classifies security content as restricted', () => {
|
|
69
|
-
expect(classifySensitivity('Found SQL injection vulnerability in login endpoint')).toBe('restricted');
|
|
70
|
-
expect(classifySensitivity('CVE-2024-1234 affects our auth library')).toBe('restricted');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('classifies business content as restricted', () => {
|
|
74
|
-
expect(classifySensitivity('Q3 revenue was $2.5M, below target')).toBe('restricted');
|
|
75
|
-
expect(classifySensitivity('Unreleased product launch planned for April')).toBe('restricted');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('classifies technical content as internal', () => {
|
|
79
|
-
expect(classifySensitivity('We decided to use PostgreSQL for the new service')).toBe('internal');
|
|
80
|
-
expect(classifySensitivity('The auth middleware handles JWT validation')).toBe('internal');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('classifies general content as public', () => {
|
|
84
|
-
expect(classifySensitivity('How to use git rebase')).toBe('public');
|
|
85
|
-
expect(classifySensitivity('JavaScript array methods')).toBe('public');
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('returns most restrictive when multiple detectors match', () => {
|
|
89
|
-
// Has both secret (confidential) and security (restricted) signals
|
|
90
|
-
expect(classifySensitivity('API_KEY leaked in CVE-2024-1234')).toBe('confidential');
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
- [ ] **Step 2: Implement classifier**
|
|
96
|
-
|
|
97
|
-
```typescript
|
|
98
|
-
// src/lib/cortex/boundary/classifier.ts
|
|
99
|
-
import type { SensitivityClass } from '../knowledge/types';
|
|
100
|
-
|
|
101
|
-
interface Detector {
|
|
102
|
-
sensitivity: SensitivityClass;
|
|
103
|
-
patterns: RegExp[];
|
|
104
|
-
priority: number; // higher = more restrictive
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const DETECTORS: Detector[] = [
|
|
108
|
-
{
|
|
109
|
-
sensitivity: 'confidential',
|
|
110
|
-
priority: 4,
|
|
111
|
-
patterns: [
|
|
112
|
-
// Secrets
|
|
113
|
-
/\b(api[_-]?key|secret[_-]?key|access[_-]?token|auth[_-]?token)\s*[=:]/i,
|
|
114
|
-
/\b(password|passwd|pwd)\s*[=:]/i,
|
|
115
|
-
/\bsk-[a-z]{2,4}-[a-zA-Z0-9]{10,}/, // Anthropic/OpenAI key format
|
|
116
|
-
/\b(DATABASE_URL|REDIS_URL|MONGO_URI)\s*=/i,
|
|
117
|
-
/\bpostgres:\/\/\w+:\w+@/i,
|
|
118
|
-
/\b(private[_-]?key|ssh[_-]?key)\b/i,
|
|
119
|
-
// Personnel
|
|
120
|
-
/\b(performance\s+review|annual\s+review)\b/i,
|
|
121
|
-
/\b(salary|compensation|pay\s+raise|pay\s+cut)\b/i,
|
|
122
|
-
/\b(hiring|termination|fired|let\s+go)\b/i,
|
|
123
|
-
/\b(1:1\s+notes?|one[\s-]on[\s-]one)\b/i,
|
|
124
|
-
],
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
sensitivity: 'restricted',
|
|
128
|
-
priority: 3,
|
|
129
|
-
patterns: [
|
|
130
|
-
// Security
|
|
131
|
-
/\b(vulnerab|exploit|attack\s+vector|injection|xss|csrf|ssrf)\b/i,
|
|
132
|
-
/\bCVE-\d{4}-\d+/i,
|
|
133
|
-
/\b(incident\s+report|security\s+breach|data\s+leak)\b/i,
|
|
134
|
-
// Business
|
|
135
|
-
/\b(revenue|profit|loss|earnings|ARR|MRR)\b/i,
|
|
136
|
-
/\b(unreleased|pre-launch|confidential\s+plan|roadmap)\b/i,
|
|
137
|
-
/\bcustomer\s+(data|records|PII)\b/i,
|
|
138
|
-
],
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
sensitivity: 'internal',
|
|
142
|
-
priority: 2,
|
|
143
|
-
patterns: [
|
|
144
|
-
/\b(we\s+decided|architecture|design\s+pattern|refactor)\b/i,
|
|
145
|
-
/\b(middleware|service|endpoint|database|schema)\b/i,
|
|
146
|
-
/\b(deployment|CI\/CD|pipeline|infrastructure)\b/i,
|
|
147
|
-
/\b(bug\s+fix|pull\s+request|code\s+review)\b/i,
|
|
148
|
-
],
|
|
149
|
-
},
|
|
150
|
-
];
|
|
151
|
-
|
|
152
|
-
export function classifySensitivity(text: string): SensitivityClass {
|
|
153
|
-
let highestPriority = 0;
|
|
154
|
-
let result: SensitivityClass = 'public';
|
|
155
|
-
|
|
156
|
-
for (const detector of DETECTORS) {
|
|
157
|
-
if (detector.priority <= highestPriority) continue;
|
|
158
|
-
for (const pattern of detector.patterns) {
|
|
159
|
-
if (pattern.test(text)) {
|
|
160
|
-
highestPriority = detector.priority;
|
|
161
|
-
result = detector.sensitivity;
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return result;
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
- [ ] **Step 3: Run tests, commit**
|
|
172
|
-
|
|
173
|
-
```bash
|
|
174
|
-
git commit -m "feat(cortex): add sensitivity auto-classifier for boundary engine"
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
---
|
|
178
|
-
|
|
179
|
-
### Task 2: Policy engine
|
|
180
|
-
|
|
181
|
-
**Files:**
|
|
182
|
-
- Create: `src/lib/cortex/boundary/policy.ts`
|
|
183
|
-
- Create: `tests/lib/cortex/boundary/policy.test.ts`
|
|
184
|
-
|
|
185
|
-
- [ ] **Step 1: Write failing tests**
|
|
186
|
-
|
|
187
|
-
```typescript
|
|
188
|
-
// tests/lib/cortex/boundary/policy.test.ts
|
|
189
|
-
import { describe, it, expect } from 'vitest';
|
|
190
|
-
import { PolicyEngine } from '@/lib/cortex/boundary/policy';
|
|
191
|
-
import type { Policy } from '@/lib/cortex/boundary/policy';
|
|
192
|
-
|
|
193
|
-
describe('PolicyEngine', () => {
|
|
194
|
-
it('returns empty actions when no policies match', () => {
|
|
195
|
-
const engine = new PolicyEngine([]);
|
|
196
|
-
const actions = engine.evaluate({ type: 'decision', sensitivity: 'internal' });
|
|
197
|
-
expect(actions).toEqual([]);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it('matches by knowledge type', () => {
|
|
201
|
-
const policies: Policy[] = [{
|
|
202
|
-
name: 'arch-decisions-propagate',
|
|
203
|
-
match: { type: 'decision' },
|
|
204
|
-
action: { propagate_to: [{ level: 'department' }] },
|
|
205
|
-
}];
|
|
206
|
-
const engine = new PolicyEngine(policies);
|
|
207
|
-
const actions = engine.evaluate({ type: 'decision', sensitivity: 'internal' });
|
|
208
|
-
expect(actions).toHaveLength(1);
|
|
209
|
-
expect(actions[0].propagate_to).toEqual([{ level: 'department' }]);
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it('matches by sensitivity', () => {
|
|
213
|
-
const policies: Policy[] = [{
|
|
214
|
-
name: 'lock-confidential',
|
|
215
|
-
match: { sensitivity: 'confidential' },
|
|
216
|
-
action: { cannot_propagate: true },
|
|
217
|
-
}];
|
|
218
|
-
const engine = new PolicyEngine(policies);
|
|
219
|
-
const actions = engine.evaluate({ type: 'pattern', sensitivity: 'confidential' });
|
|
220
|
-
expect(actions).toHaveLength(1);
|
|
221
|
-
expect(actions[0].cannot_propagate).toBe(true);
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it('matches by topics', () => {
|
|
225
|
-
const policies: Policy[] = [{
|
|
226
|
-
name: 'security-routing',
|
|
227
|
-
match: { topics: ['security', 'vulnerability'] },
|
|
228
|
-
action: { max_scope: 'department', propagate_to: [{ level: 'team', entity_id: 'team-security' }] },
|
|
229
|
-
}];
|
|
230
|
-
const engine = new PolicyEngine(policies);
|
|
231
|
-
const actions = engine.evaluate({ type: 'error_fix', sensitivity: 'restricted', topics: ['security'] });
|
|
232
|
-
expect(actions).toHaveLength(1);
|
|
233
|
-
expect(actions[0].max_scope).toBe('department');
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('returns multiple matching policies', () => {
|
|
237
|
-
const policies: Policy[] = [
|
|
238
|
-
{ name: 'p1', match: { type: 'decision' }, action: { trickle_down: true } },
|
|
239
|
-
{ name: 'p2', match: { sensitivity: 'internal' }, action: { max_scope: 'organization' } },
|
|
240
|
-
];
|
|
241
|
-
const engine = new PolicyEngine(policies);
|
|
242
|
-
const actions = engine.evaluate({ type: 'decision', sensitivity: 'internal' });
|
|
243
|
-
expect(actions).toHaveLength(2);
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it('does not match when criteria do not overlap', () => {
|
|
247
|
-
const policies: Policy[] = [{
|
|
248
|
-
name: 'security-only',
|
|
249
|
-
match: { topics: ['security'] },
|
|
250
|
-
action: { cannot_propagate: true },
|
|
251
|
-
}];
|
|
252
|
-
const engine = new PolicyEngine(policies);
|
|
253
|
-
const actions = engine.evaluate({ type: 'decision', sensitivity: 'internal', topics: ['architecture'] });
|
|
254
|
-
expect(actions).toHaveLength(0);
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
- [ ] **Step 2: Implement policy engine**
|
|
260
|
-
|
|
261
|
-
```typescript
|
|
262
|
-
// src/lib/cortex/boundary/policy.ts
|
|
263
|
-
import type { KnowledgeType, SensitivityClass, ScopeLevel } from '../knowledge/types';
|
|
264
|
-
|
|
265
|
-
export interface PropagationTarget {
|
|
266
|
-
level: ScopeLevel;
|
|
267
|
-
entity_id?: string;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
export interface PolicyAction {
|
|
271
|
-
max_scope?: ScopeLevel;
|
|
272
|
-
propagate_to?: PropagationTarget[];
|
|
273
|
-
trickle_down?: boolean;
|
|
274
|
-
cannot_propagate?: boolean;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
export interface Policy {
|
|
278
|
-
name: string;
|
|
279
|
-
match: {
|
|
280
|
-
type?: KnowledgeType;
|
|
281
|
-
topics?: string[];
|
|
282
|
-
sensitivity?: SensitivityClass;
|
|
283
|
-
scope_level?: ScopeLevel;
|
|
284
|
-
};
|
|
285
|
-
action: PolicyAction;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
export interface PolicyMatchInput {
|
|
289
|
-
type: KnowledgeType | string;
|
|
290
|
-
sensitivity: SensitivityClass | string;
|
|
291
|
-
topics?: string[];
|
|
292
|
-
scope_level?: ScopeLevel | string;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
export class PolicyEngine {
|
|
296
|
-
constructor(private policies: Policy[]) {}
|
|
297
|
-
|
|
298
|
-
evaluate(input: PolicyMatchInput): PolicyAction[] {
|
|
299
|
-
const matched: PolicyAction[] = [];
|
|
300
|
-
|
|
301
|
-
for (const policy of this.policies) {
|
|
302
|
-
if (this.matches(policy, input)) {
|
|
303
|
-
matched.push(policy.action);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return matched;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
private matches(policy: Policy, input: PolicyMatchInput): boolean {
|
|
311
|
-
const { match } = policy;
|
|
312
|
-
|
|
313
|
-
if (match.type && match.type !== input.type) return false;
|
|
314
|
-
if (match.sensitivity && match.sensitivity !== input.sensitivity) return false;
|
|
315
|
-
if (match.scope_level && match.scope_level !== input.scope_level) return false;
|
|
316
|
-
|
|
317
|
-
if (match.topics && match.topics.length > 0) {
|
|
318
|
-
const inputTopics = input.topics ?? [];
|
|
319
|
-
const hasOverlap = match.topics.some(t => inputTopics.includes(t));
|
|
320
|
-
if (!hasOverlap) return false;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
return true;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
- [ ] **Step 3: Run tests, commit**
|
|
329
|
-
|
|
330
|
-
```bash
|
|
331
|
-
git commit -m "feat(cortex): add policy engine for boundary enforcement"
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
---
|
|
335
|
-
|
|
336
|
-
## Chunk 2: Access Filter and Audit Trail
|
|
337
|
-
|
|
338
|
-
### Task 3: Query-time access filter
|
|
339
|
-
|
|
340
|
-
**Files:**
|
|
341
|
-
- Create: `src/lib/cortex/boundary/access.ts`
|
|
342
|
-
- Create: `tests/lib/cortex/boundary/access.test.ts`
|
|
343
|
-
|
|
344
|
-
- [ ] **Step 1: Write failing tests**
|
|
345
|
-
|
|
346
|
-
```typescript
|
|
347
|
-
// tests/lib/cortex/boundary/access.test.ts
|
|
348
|
-
import { describe, it, expect } from 'vitest';
|
|
349
|
-
import { AccessFilter } from '@/lib/cortex/boundary/access';
|
|
350
|
-
import type { ScoredKnowledge } from '@/lib/cortex/knowledge/types';
|
|
351
|
-
|
|
352
|
-
function makeUnit(overrides: Partial<ScoredKnowledge> = {}): ScoredKnowledge {
|
|
353
|
-
return {
|
|
354
|
-
id: 'k1', vector: [], text: 'test', type: 'decision', layer: 'personal',
|
|
355
|
-
workspace_id: null, session_id: null, agent_type: 'claude',
|
|
356
|
-
project_path: null, file_refs: [], confidence: 0.8,
|
|
357
|
-
created: new Date().toISOString(), source_timestamp: new Date().toISOString(),
|
|
358
|
-
stale_score: 0, access_count: 0, last_accessed: null, metadata: {},
|
|
359
|
-
relevance_score: 0.9, similarity: 0.9,
|
|
360
|
-
sensitivity: 'internal',
|
|
361
|
-
scope: { level: 'personal', entity_id: 'person-alice' },
|
|
362
|
-
origin: { source_type: 'conversation', source_ref: '', creator_entity_id: 'person-alice' },
|
|
363
|
-
...overrides,
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
describe('AccessFilter', () => {
|
|
368
|
-
const filter = new AccessFilter({
|
|
369
|
-
requesterId: 'person-alice',
|
|
370
|
-
requesterScope: { level: 'team', entity_id: 'team-platform' },
|
|
371
|
-
requesterOrg: 'organization-acme',
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
it('allows public knowledge from anywhere in org', () => {
|
|
375
|
-
const unit = makeUnit({ sensitivity: 'public', scope: { level: 'organization', entity_id: 'org-acme' } });
|
|
376
|
-
expect(filter.canAccess(unit)).toBe(true);
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
it('allows internal knowledge within org', () => {
|
|
380
|
-
const unit = makeUnit({ sensitivity: 'internal', scope: { level: 'team', entity_id: 'team-other' } });
|
|
381
|
-
expect(filter.canAccess(unit)).toBe(true);
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
it('allows restricted knowledge within same scope', () => {
|
|
385
|
-
const unit = makeUnit({
|
|
386
|
-
sensitivity: 'restricted',
|
|
387
|
-
scope: { level: 'team', entity_id: 'team-platform' },
|
|
388
|
-
});
|
|
389
|
-
expect(filter.canAccess(unit)).toBe(true);
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
it('denies restricted knowledge from different department', () => {
|
|
393
|
-
const unit = makeUnit({
|
|
394
|
-
sensitivity: 'restricted',
|
|
395
|
-
scope: { level: 'department', entity_id: 'department-sales' },
|
|
396
|
-
});
|
|
397
|
-
// Alice is in team-platform, not department-sales
|
|
398
|
-
expect(filter.canAccess(unit)).toBe(false);
|
|
399
|
-
});
|
|
400
|
-
|
|
401
|
-
it('allows confidential knowledge from self', () => {
|
|
402
|
-
const unit = makeUnit({
|
|
403
|
-
sensitivity: 'confidential',
|
|
404
|
-
scope: { level: 'personal', entity_id: 'person-alice' },
|
|
405
|
-
origin: { source_type: 'conversation', source_ref: '', creator_entity_id: 'person-alice' },
|
|
406
|
-
});
|
|
407
|
-
expect(filter.canAccess(unit)).toBe(true);
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
it('denies confidential knowledge from others', () => {
|
|
411
|
-
const unit = makeUnit({
|
|
412
|
-
sensitivity: 'confidential',
|
|
413
|
-
scope: { level: 'personal', entity_id: 'person-bob' },
|
|
414
|
-
origin: { source_type: 'conversation', source_ref: '', creator_entity_id: 'person-bob' },
|
|
415
|
-
});
|
|
416
|
-
expect(filter.canAccess(unit)).toBe(false);
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
it('respects creator_scope override (further restriction)', () => {
|
|
420
|
-
const unit = makeUnit({
|
|
421
|
-
sensitivity: 'internal', // normally accessible org-wide
|
|
422
|
-
creator_scope: { max_level: 'personal' }, // but creator restricted to personal only
|
|
423
|
-
scope: { level: 'personal', entity_id: 'person-bob' },
|
|
424
|
-
});
|
|
425
|
-
// Alice can't access Bob's personal-restricted knowledge even though it's internal
|
|
426
|
-
expect(filter.canAccess(unit)).toBe(false);
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
it('filters a list of results', () => {
|
|
430
|
-
const results = [
|
|
431
|
-
makeUnit({ id: 'a', sensitivity: 'public' }),
|
|
432
|
-
makeUnit({ id: 'b', sensitivity: 'confidential', origin: { source_type: 'conversation', source_ref: '', creator_entity_id: 'person-bob' } }),
|
|
433
|
-
makeUnit({ id: 'c', sensitivity: 'internal' }),
|
|
434
|
-
];
|
|
435
|
-
const filtered = filter.filterResults(results);
|
|
436
|
-
expect(filtered).toHaveLength(2); // a and c pass, b denied
|
|
437
|
-
expect(filtered.map(r => r.id)).toEqual(['a', 'c']);
|
|
438
|
-
});
|
|
439
|
-
});
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
- [ ] **Step 2: Implement access filter**
|
|
443
|
-
|
|
444
|
-
```typescript
|
|
445
|
-
// src/lib/cortex/boundary/access.ts
|
|
446
|
-
import type { ScoredKnowledge, Scope, ScopeLevel, SensitivityClass } from '../knowledge/types';
|
|
447
|
-
|
|
448
|
-
const SCOPE_HIERARCHY: Record<ScopeLevel, number> = {
|
|
449
|
-
personal: 0,
|
|
450
|
-
team: 1,
|
|
451
|
-
department: 2,
|
|
452
|
-
organization: 3,
|
|
453
|
-
};
|
|
454
|
-
|
|
455
|
-
export interface AccessFilterConfig {
|
|
456
|
-
requesterId: string;
|
|
457
|
-
requesterScope: Scope; // requester's team scope
|
|
458
|
-
requesterOrg: string; // requester's org entity_id
|
|
459
|
-
grants?: Set<string>; // knowledge IDs the requester has been granted access to
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
export class AccessFilter {
|
|
463
|
-
private config: AccessFilterConfig;
|
|
464
|
-
|
|
465
|
-
constructor(config: AccessFilterConfig) {
|
|
466
|
-
this.config = config;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
canAccess(unit: ScoredKnowledge): boolean {
|
|
470
|
-
const sensitivity = (unit.sensitivity ?? 'internal') as SensitivityClass;
|
|
471
|
-
const unitScope = unit.scope ?? { level: 'personal' as ScopeLevel, entity_id: '' };
|
|
472
|
-
const creatorId = unit.origin?.creator_entity_id ?? '';
|
|
473
|
-
|
|
474
|
-
// Check creator_scope override first — most restrictive wins
|
|
475
|
-
if (unit.creator_scope) {
|
|
476
|
-
const maxLevel = SCOPE_HIERARCHY[unit.creator_scope.max_level] ?? 0;
|
|
477
|
-
const requesterLevel = this.getRequesterProximityLevel(unitScope);
|
|
478
|
-
if (requesterLevel > maxLevel) return false;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
switch (sensitivity) {
|
|
482
|
-
case 'public':
|
|
483
|
-
return true;
|
|
484
|
-
|
|
485
|
-
case 'internal':
|
|
486
|
-
// Accessible to anyone in the same org
|
|
487
|
-
return true;
|
|
488
|
-
|
|
489
|
-
case 'restricted':
|
|
490
|
-
// Accessible within same scope or if policy grants cross-scope access
|
|
491
|
-
return this.isWithinScope(unitScope);
|
|
492
|
-
|
|
493
|
-
case 'confidential':
|
|
494
|
-
// Only creator or explicit grant
|
|
495
|
-
if (this.config.requesterId === creatorId) return true;
|
|
496
|
-
if (this.config.grants?.has(unit.id)) return true;
|
|
497
|
-
return false;
|
|
498
|
-
|
|
499
|
-
default:
|
|
500
|
-
return false;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
filterResults(results: ScoredKnowledge[]): ScoredKnowledge[] {
|
|
505
|
-
return results.filter(r => this.canAccess(r));
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
private isWithinScope(unitScope: Scope): boolean {
|
|
509
|
-
// Same entity_id = same scope
|
|
510
|
-
if (unitScope.entity_id === this.config.requesterScope.entity_id) return true;
|
|
511
|
-
if (unitScope.entity_id === this.config.requesterId) return true;
|
|
512
|
-
// Same org for org-level scopes
|
|
513
|
-
if (unitScope.level === 'organization' && unitScope.entity_id === this.config.requesterOrg) return true;
|
|
514
|
-
return false;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
private getRequesterProximityLevel(unitScope: Scope): number {
|
|
518
|
-
// How "far" is the requester from the unit's scope?
|
|
519
|
-
// 0 = same entity, 1 = same scope level, 2+ = further
|
|
520
|
-
if (unitScope.entity_id === this.config.requesterId) return 0;
|
|
521
|
-
if (unitScope.entity_id === this.config.requesterScope.entity_id) return 0;
|
|
522
|
-
return SCOPE_HIERARCHY[unitScope.level] ?? 3;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
- [ ] **Step 3: Run tests, commit**
|
|
528
|
-
|
|
529
|
-
```bash
|
|
530
|
-
git commit -m "feat(cortex): add query-time access filter for boundary enforcement"
|
|
531
|
-
```
|
|
532
|
-
|
|
533
|
-
---
|
|
534
|
-
|
|
535
|
-
### Task 4: Audit trail
|
|
536
|
-
|
|
537
|
-
**Files:**
|
|
538
|
-
- Create: `src/lib/cortex/boundary/audit.ts`
|
|
539
|
-
- Create: `tests/lib/cortex/boundary/audit.test.ts`
|
|
540
|
-
|
|
541
|
-
- [ ] **Step 1: Write failing tests**
|
|
542
|
-
|
|
543
|
-
```typescript
|
|
544
|
-
// tests/lib/cortex/boundary/audit.test.ts
|
|
545
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
546
|
-
import fs from 'fs';
|
|
547
|
-
import path from 'path';
|
|
548
|
-
import os from 'os';
|
|
549
|
-
import Database from 'better-sqlite3';
|
|
550
|
-
import { AuditLog } from '@/lib/cortex/boundary/audit';
|
|
551
|
-
|
|
552
|
-
describe('AuditLog', () => {
|
|
553
|
-
let tmpDir: string;
|
|
554
|
-
let audit: AuditLog;
|
|
555
|
-
|
|
556
|
-
beforeEach(() => {
|
|
557
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cortex-audit-'));
|
|
558
|
-
const db = new Database(path.join(tmpDir, 'graph.db'));
|
|
559
|
-
audit = new AuditLog(db);
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
afterEach(() => {
|
|
563
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
it('logs an access decision', () => {
|
|
567
|
-
audit.log({
|
|
568
|
-
requesterId: 'person-alice',
|
|
569
|
-
knowledgeId: 'k1',
|
|
570
|
-
action: 'allowed',
|
|
571
|
-
reason: 'public sensitivity',
|
|
572
|
-
});
|
|
573
|
-
const entries = audit.query({ requesterId: 'person-alice' });
|
|
574
|
-
expect(entries).toHaveLength(1);
|
|
575
|
-
expect(entries[0].action).toBe('allowed');
|
|
576
|
-
});
|
|
577
|
-
|
|
578
|
-
it('logs denied access', () => {
|
|
579
|
-
audit.log({
|
|
580
|
-
requesterId: 'person-alice',
|
|
581
|
-
knowledgeId: 'k2',
|
|
582
|
-
action: 'denied',
|
|
583
|
-
reason: 'confidential, not creator',
|
|
584
|
-
});
|
|
585
|
-
const entries = audit.query({ requesterId: 'person-alice' });
|
|
586
|
-
expect(entries).toHaveLength(1);
|
|
587
|
-
expect(entries[0].action).toBe('denied');
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
it('queries by time range', () => {
|
|
591
|
-
audit.log({ requesterId: 'person-alice', knowledgeId: 'k1', action: 'allowed', reason: 'public' });
|
|
592
|
-
const recent = audit.query({ since: new Date(Date.now() - 60000).toISOString() });
|
|
593
|
-
expect(recent).toHaveLength(1);
|
|
594
|
-
|
|
595
|
-
const future = audit.query({ since: new Date(Date.now() + 60000).toISOString() });
|
|
596
|
-
expect(future).toHaveLength(0);
|
|
597
|
-
});
|
|
598
|
-
|
|
599
|
-
it('supports retention cleanup', () => {
|
|
600
|
-
audit.log({ requesterId: 'person-alice', knowledgeId: 'k1', action: 'allowed', reason: 'test' });
|
|
601
|
-
// Cleanup entries older than 0 days (everything)
|
|
602
|
-
audit.cleanup(0);
|
|
603
|
-
const entries = audit.query({});
|
|
604
|
-
expect(entries).toHaveLength(0);
|
|
605
|
-
});
|
|
606
|
-
});
|
|
607
|
-
```
|
|
608
|
-
|
|
609
|
-
- [ ] **Step 2: Implement audit log**
|
|
610
|
-
|
|
611
|
-
```typescript
|
|
612
|
-
// src/lib/cortex/boundary/audit.ts
|
|
613
|
-
import type Database from 'better-sqlite3';
|
|
614
|
-
|
|
615
|
-
export interface AuditEntry {
|
|
616
|
-
requesterId: string;
|
|
617
|
-
knowledgeId: string;
|
|
618
|
-
action: 'allowed' | 'denied';
|
|
619
|
-
reason: string;
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
export interface AuditQueryFilter {
|
|
623
|
-
requesterId?: string;
|
|
624
|
-
since?: string; // ISO timestamp
|
|
625
|
-
limit?: number;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
export interface AuditRecord extends AuditEntry {
|
|
629
|
-
timestamp: string;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
export class AuditLog {
|
|
633
|
-
private db: InstanceType<typeof Database>;
|
|
634
|
-
|
|
635
|
-
constructor(db: InstanceType<typeof Database>) {
|
|
636
|
-
this.db = db;
|
|
637
|
-
this.db.exec(`
|
|
638
|
-
CREATE TABLE IF NOT EXISTS audit_log (
|
|
639
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
640
|
-
requester_id TEXT NOT NULL,
|
|
641
|
-
knowledge_id TEXT NOT NULL,
|
|
642
|
-
action TEXT NOT NULL,
|
|
643
|
-
reason TEXT NOT NULL,
|
|
644
|
-
timestamp TEXT NOT NULL
|
|
645
|
-
);
|
|
646
|
-
CREATE INDEX IF NOT EXISTS idx_audit_requester ON audit_log(requester_id);
|
|
647
|
-
CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_log(timestamp);
|
|
648
|
-
`);
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
log(entry: AuditEntry): void {
|
|
652
|
-
this.db.prepare(`
|
|
653
|
-
INSERT INTO audit_log (requester_id, knowledge_id, action, reason, timestamp)
|
|
654
|
-
VALUES (?, ?, ?, ?, ?)
|
|
655
|
-
`).run(entry.requesterId, entry.knowledgeId, entry.action, entry.reason, new Date().toISOString());
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
query(filter: AuditQueryFilter): AuditRecord[] {
|
|
659
|
-
let sql = 'SELECT * FROM audit_log WHERE 1=1';
|
|
660
|
-
const params: any[] = [];
|
|
661
|
-
|
|
662
|
-
if (filter.requesterId) {
|
|
663
|
-
sql += ' AND requester_id = ?';
|
|
664
|
-
params.push(filter.requesterId);
|
|
665
|
-
}
|
|
666
|
-
if (filter.since) {
|
|
667
|
-
sql += ' AND timestamp >= ?';
|
|
668
|
-
params.push(filter.since);
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
sql += ' ORDER BY timestamp DESC';
|
|
672
|
-
|
|
673
|
-
if (filter.limit) {
|
|
674
|
-
sql += ' LIMIT ?';
|
|
675
|
-
params.push(filter.limit);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
return (this.db.prepare(sql).all(...params) as any[]).map(row => ({
|
|
679
|
-
requesterId: row.requester_id,
|
|
680
|
-
knowledgeId: row.knowledge_id,
|
|
681
|
-
action: row.action,
|
|
682
|
-
reason: row.reason,
|
|
683
|
-
timestamp: row.timestamp,
|
|
684
|
-
}));
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
cleanup(retentionDays: number): void {
|
|
688
|
-
const cutoff = new Date(Date.now() - retentionDays * 86400000).toISOString();
|
|
689
|
-
this.db.prepare('DELETE FROM audit_log WHERE timestamp < ?').run(cutoff);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
```
|
|
693
|
-
|
|
694
|
-
- [ ] **Step 3: Run tests, commit**
|
|
695
|
-
|
|
696
|
-
```bash
|
|
697
|
-
git commit -m "feat(cortex): add audit trail for access decisions"
|
|
698
|
-
```
|
|
699
|
-
|
|
700
|
-
---
|
|
701
|
-
|
|
702
|
-
## Chunk 3: Integration
|
|
703
|
-
|
|
704
|
-
### Task 5: Integrate with ingestion pipeline
|
|
705
|
-
|
|
706
|
-
**Files:**
|
|
707
|
-
- Modify: `src/lib/cortex/ingestion/pipeline.ts`
|
|
708
|
-
|
|
709
|
-
- [ ] **Step 1: Read pipeline.ts**
|
|
710
|
-
|
|
711
|
-
- [ ] **Step 2: Add auto-classification during ingestion**
|
|
712
|
-
|
|
713
|
-
Import the classifier:
|
|
714
|
-
```typescript
|
|
715
|
-
import { classifySensitivity } from '../boundary/classifier';
|
|
716
|
-
```
|
|
717
|
-
|
|
718
|
-
In the KnowledgeUnit construction (where v2 fields are set), replace the hardcoded `sensitivity: 'internal'` with:
|
|
719
|
-
```typescript
|
|
720
|
-
sensitivity: classifySensitivity(chunk.text),
|
|
721
|
-
```
|
|
722
|
-
|
|
723
|
-
This ensures every ingested knowledge unit gets an auto-classified sensitivity level.
|
|
724
|
-
|
|
725
|
-
- [ ] **Step 3: Run pipeline tests to verify no regressions**
|
|
726
|
-
|
|
727
|
-
```bash
|
|
728
|
-
npx vitest run tests/lib/cortex/ingestion/
|
|
729
|
-
```
|
|
730
|
-
|
|
731
|
-
- [ ] **Step 4: Commit**
|
|
732
|
-
|
|
733
|
-
```bash
|
|
734
|
-
git commit -m "feat(cortex): auto-classify sensitivity during ingestion"
|
|
735
|
-
```
|
|
736
|
-
|
|
737
|
-
---
|
|
738
|
-
|
|
739
|
-
### Task 6: Integrate AccessFilter with ContextEngine
|
|
740
|
-
|
|
741
|
-
**Files:**
|
|
742
|
-
- Modify: `src/lib/cortex/retrieval/context-engine.ts`
|
|
743
|
-
|
|
744
|
-
- [ ] **Step 1: Read context-engine.ts**
|
|
745
|
-
|
|
746
|
-
- [ ] **Step 2: Add AccessFilter to the pipeline**
|
|
747
|
-
|
|
748
|
-
Import AccessFilter:
|
|
749
|
-
```typescript
|
|
750
|
-
import { AccessFilter } from '../boundary/access';
|
|
751
|
-
```
|
|
752
|
-
|
|
753
|
-
Add to `ContextEngineDeps`:
|
|
754
|
-
```typescript
|
|
755
|
-
accessFilter?: AccessFilter;
|
|
756
|
-
```
|
|
757
|
-
|
|
758
|
-
In the `assemble()` method, after fusion/ranking (Stage 5) but before conflict detection (Stage 6), apply the access filter:
|
|
759
|
-
|
|
760
|
-
```typescript
|
|
761
|
-
// Stage 5.5: Access control filtering
|
|
762
|
-
let accessible = fused;
|
|
763
|
-
if (this.deps.accessFilter) {
|
|
764
|
-
accessible = this.deps.accessFilter.filterResults(fused);
|
|
765
|
-
}
|
|
766
|
-
```
|
|
767
|
-
|
|
768
|
-
Then pass `accessible` (not `fused`) to conflict detection and formatting.
|
|
769
|
-
|
|
770
|
-
- [ ] **Step 3: Run context-engine tests to verify no regressions**
|
|
771
|
-
|
|
772
|
-
```bash
|
|
773
|
-
npx vitest run tests/lib/cortex/retrieval/context-engine.test.ts
|
|
774
|
-
```
|
|
775
|
-
|
|
776
|
-
- [ ] **Step 4: Commit**
|
|
777
|
-
|
|
778
|
-
```bash
|
|
779
|
-
git commit -m "feat(cortex): integrate access filter into context assembly pipeline"
|
|
780
|
-
```
|
|
781
|
-
|
|
782
|
-
---
|
|
783
|
-
|
|
784
|
-
### Task 7: Barrel export and config integration
|
|
785
|
-
|
|
786
|
-
**Files:**
|
|
787
|
-
- Create: `src/lib/cortex/boundary/index.ts`
|
|
788
|
-
- Modify: `src/lib/cortex/config.ts` (add policies array to config)
|
|
789
|
-
|
|
790
|
-
- [ ] **Step 1: Create barrel export**
|
|
791
|
-
|
|
792
|
-
```typescript
|
|
793
|
-
// src/lib/cortex/boundary/index.ts
|
|
794
|
-
export { classifySensitivity } from './classifier';
|
|
795
|
-
export { PolicyEngine } from './policy';
|
|
796
|
-
export type { Policy, PolicyAction, PropagationTarget } from './policy';
|
|
797
|
-
export { AccessFilter } from './access';
|
|
798
|
-
export type { AccessFilterConfig } from './access';
|
|
799
|
-
export { AuditLog } from './audit';
|
|
800
|
-
export type { AuditEntry, AuditRecord, AuditQueryFilter } from './audit';
|
|
801
|
-
```
|
|
802
|
-
|
|
803
|
-
- [ ] **Step 2: Add policies to CortexConfig**
|
|
804
|
-
|
|
805
|
-
In `src/lib/cortex/config.ts`, add to the `CortexConfig` interface:
|
|
806
|
-
```typescript
|
|
807
|
-
policies?: Policy[]; // Organizational boundary policies
|
|
808
|
-
```
|
|
809
|
-
|
|
810
|
-
And in `DEFAULT_CORTEX_CONFIG`:
|
|
811
|
-
```typescript
|
|
812
|
-
policies: [],
|
|
813
|
-
```
|
|
814
|
-
|
|
815
|
-
Import the Policy type:
|
|
816
|
-
```typescript
|
|
817
|
-
import type { Policy } from './boundary/policy';
|
|
818
|
-
```
|
|
819
|
-
|
|
820
|
-
- [ ] **Step 3: Run full test suite**
|
|
821
|
-
|
|
822
|
-
```bash
|
|
823
|
-
npx vitest run tests/lib/cortex/
|
|
824
|
-
```
|
|
825
|
-
|
|
826
|
-
- [ ] **Step 4: Commit**
|
|
827
|
-
|
|
828
|
-
```bash
|
|
829
|
-
git commit -m "feat(cortex): add boundary module barrel export and config integration"
|
|
830
|
-
```
|
|
831
|
-
|
|
832
|
-
---
|
|
833
|
-
|
|
834
|
-
## Summary
|
|
835
|
-
|
|
836
|
-
| Task | Component | Tests | Status |
|
|
837
|
-
|------|-----------|-------|--------|
|
|
838
|
-
| 1 | Sensitivity classifier | 7 | |
|
|
839
|
-
| 2 | Policy engine | 6 | |
|
|
840
|
-
| 3 | Access filter | 8 | |
|
|
841
|
-
| 4 | Audit trail | 4 | |
|
|
842
|
-
| 5 | Pipeline integration | regression | |
|
|
843
|
-
| 6 | ContextEngine integration | regression | |
|
|
844
|
-
| 7 | Barrel export + config | regression | |
|
|
845
|
-
|
|
846
|
-
**Total: 7 tasks, ~25 new tests, 3 chunks**
|
|
847
|
-
|
|
848
|
-
**Key design decisions:**
|
|
849
|
-
- Auto-classification is regex-based (no LLM call) — fast, deterministic, auditable
|
|
850
|
-
- Policies are stored in CortexConfig (admin-editable JSON) — not code
|
|
851
|
-
- AccessFilter is a separate class that can be composed with ContextEngine or used standalone
|
|
852
|
-
- Audit log uses the entity graph's SQLite DB (same file, new table) — no new database
|
|
853
|
-
- Most restrictive classification always wins (confidential > restricted > internal > public)
|
|
1
|
+
# Cortex v2 — Pillar 4: Boundary Engine
|
|
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:** Add three-layer access control to Cortex: auto-classification of sensitivity, organizational policies, and creator overrides — ensuring confidential knowledge never leaks across scope boundaries.
|
|
6
|
+
|
|
7
|
+
**Architecture:** A new `src/lib/cortex/boundary/` module with three components: a `Classifier` (regex-based sensitivity detection), a `PolicyEngine` (rule evaluation), and an `AccessFilter` (query-time enforcement). The AccessFilter integrates with the ContextEngine to pre-filter scopes before search. An audit log records all access decisions in the entity graph's SQLite database.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** TypeScript, better-sqlite3 (existing graph DB), vitest
|
|
10
|
+
|
|
11
|
+
**Spec:** `docs/superpowers/specs/2026-03-14-cortex-v2-design.md` — Pillar 4
|
|
12
|
+
|
|
13
|
+
**Depends on:** Pillar 1 (Entity Graph) + Pillar 2 (Knowledge Unit Evolution) — both completed
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## File Structure
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
New files:
|
|
21
|
+
├── src/lib/cortex/boundary/classifier.ts — Auto-classification (Layer 1)
|
|
22
|
+
├── src/lib/cortex/boundary/policy.ts — Policy engine (Layer 2)
|
|
23
|
+
├── src/lib/cortex/boundary/access.ts — Query-time access filter (Layer 3 + enforcement)
|
|
24
|
+
├── src/lib/cortex/boundary/audit.ts — Audit trail logging
|
|
25
|
+
├── src/lib/cortex/boundary/index.ts — Barrel export
|
|
26
|
+
|
|
27
|
+
Modified files:
|
|
28
|
+
├── src/lib/cortex/ingestion/pipeline.ts — Auto-classify on ingestion
|
|
29
|
+
├── src/lib/cortex/retrieval/context-engine.ts — Pre-filter with AccessFilter
|
|
30
|
+
├── src/lib/cortex/config.ts — Add policies to config
|
|
31
|
+
|
|
32
|
+
Test files:
|
|
33
|
+
├── tests/lib/cortex/boundary/classifier.test.ts
|
|
34
|
+
├── tests/lib/cortex/boundary/policy.test.ts
|
|
35
|
+
├── tests/lib/cortex/boundary/access.test.ts
|
|
36
|
+
├── tests/lib/cortex/boundary/audit.test.ts
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Chunk 1: Auto-Classification and Policy Engine
|
|
42
|
+
|
|
43
|
+
### Task 1: Sensitivity auto-classifier
|
|
44
|
+
|
|
45
|
+
**Files:**
|
|
46
|
+
- Create: `src/lib/cortex/boundary/classifier.ts`
|
|
47
|
+
- Create: `tests/lib/cortex/boundary/classifier.test.ts`
|
|
48
|
+
|
|
49
|
+
- [ ] **Step 1: Write failing tests**
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// tests/lib/cortex/boundary/classifier.test.ts
|
|
53
|
+
import { describe, it, expect } from 'vitest';
|
|
54
|
+
import { classifySensitivity } from '@/lib/cortex/boundary/classifier';
|
|
55
|
+
|
|
56
|
+
describe('classifySensitivity', () => {
|
|
57
|
+
it('classifies secrets as confidential', () => {
|
|
58
|
+
expect(classifySensitivity('Set API_KEY=sk-ant-abc123 in .env')).toBe('confidential');
|
|
59
|
+
expect(classifySensitivity('password: hunter2')).toBe('confidential');
|
|
60
|
+
expect(classifySensitivity('DATABASE_URL=postgres://user:pass@host')).toBe('confidential');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('classifies personnel content as confidential', () => {
|
|
64
|
+
expect(classifySensitivity('Alice performance review: exceeds expectations')).toBe('confidential');
|
|
65
|
+
expect(classifySensitivity('salary adjustment from 120k to 140k')).toBe('confidential');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('classifies security content as restricted', () => {
|
|
69
|
+
expect(classifySensitivity('Found SQL injection vulnerability in login endpoint')).toBe('restricted');
|
|
70
|
+
expect(classifySensitivity('CVE-2024-1234 affects our auth library')).toBe('restricted');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('classifies business content as restricted', () => {
|
|
74
|
+
expect(classifySensitivity('Q3 revenue was $2.5M, below target')).toBe('restricted');
|
|
75
|
+
expect(classifySensitivity('Unreleased product launch planned for April')).toBe('restricted');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('classifies technical content as internal', () => {
|
|
79
|
+
expect(classifySensitivity('We decided to use PostgreSQL for the new service')).toBe('internal');
|
|
80
|
+
expect(classifySensitivity('The auth middleware handles JWT validation')).toBe('internal');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('classifies general content as public', () => {
|
|
84
|
+
expect(classifySensitivity('How to use git rebase')).toBe('public');
|
|
85
|
+
expect(classifySensitivity('JavaScript array methods')).toBe('public');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('returns most restrictive when multiple detectors match', () => {
|
|
89
|
+
// Has both secret (confidential) and security (restricted) signals
|
|
90
|
+
expect(classifySensitivity('API_KEY leaked in CVE-2024-1234')).toBe('confidential');
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- [ ] **Step 2: Implement classifier**
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// src/lib/cortex/boundary/classifier.ts
|
|
99
|
+
import type { SensitivityClass } from '../knowledge/types';
|
|
100
|
+
|
|
101
|
+
interface Detector {
|
|
102
|
+
sensitivity: SensitivityClass;
|
|
103
|
+
patterns: RegExp[];
|
|
104
|
+
priority: number; // higher = more restrictive
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const DETECTORS: Detector[] = [
|
|
108
|
+
{
|
|
109
|
+
sensitivity: 'confidential',
|
|
110
|
+
priority: 4,
|
|
111
|
+
patterns: [
|
|
112
|
+
// Secrets
|
|
113
|
+
/\b(api[_-]?key|secret[_-]?key|access[_-]?token|auth[_-]?token)\s*[=:]/i,
|
|
114
|
+
/\b(password|passwd|pwd)\s*[=:]/i,
|
|
115
|
+
/\bsk-[a-z]{2,4}-[a-zA-Z0-9]{10,}/, // Anthropic/OpenAI key format
|
|
116
|
+
/\b(DATABASE_URL|REDIS_URL|MONGO_URI)\s*=/i,
|
|
117
|
+
/\bpostgres:\/\/\w+:\w+@/i,
|
|
118
|
+
/\b(private[_-]?key|ssh[_-]?key)\b/i,
|
|
119
|
+
// Personnel
|
|
120
|
+
/\b(performance\s+review|annual\s+review)\b/i,
|
|
121
|
+
/\b(salary|compensation|pay\s+raise|pay\s+cut)\b/i,
|
|
122
|
+
/\b(hiring|termination|fired|let\s+go)\b/i,
|
|
123
|
+
/\b(1:1\s+notes?|one[\s-]on[\s-]one)\b/i,
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
sensitivity: 'restricted',
|
|
128
|
+
priority: 3,
|
|
129
|
+
patterns: [
|
|
130
|
+
// Security
|
|
131
|
+
/\b(vulnerab|exploit|attack\s+vector|injection|xss|csrf|ssrf)\b/i,
|
|
132
|
+
/\bCVE-\d{4}-\d+/i,
|
|
133
|
+
/\b(incident\s+report|security\s+breach|data\s+leak)\b/i,
|
|
134
|
+
// Business
|
|
135
|
+
/\b(revenue|profit|loss|earnings|ARR|MRR)\b/i,
|
|
136
|
+
/\b(unreleased|pre-launch|confidential\s+plan|roadmap)\b/i,
|
|
137
|
+
/\bcustomer\s+(data|records|PII)\b/i,
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
sensitivity: 'internal',
|
|
142
|
+
priority: 2,
|
|
143
|
+
patterns: [
|
|
144
|
+
/\b(we\s+decided|architecture|design\s+pattern|refactor)\b/i,
|
|
145
|
+
/\b(middleware|service|endpoint|database|schema)\b/i,
|
|
146
|
+
/\b(deployment|CI\/CD|pipeline|infrastructure)\b/i,
|
|
147
|
+
/\b(bug\s+fix|pull\s+request|code\s+review)\b/i,
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
export function classifySensitivity(text: string): SensitivityClass {
|
|
153
|
+
let highestPriority = 0;
|
|
154
|
+
let result: SensitivityClass = 'public';
|
|
155
|
+
|
|
156
|
+
for (const detector of DETECTORS) {
|
|
157
|
+
if (detector.priority <= highestPriority) continue;
|
|
158
|
+
for (const pattern of detector.patterns) {
|
|
159
|
+
if (pattern.test(text)) {
|
|
160
|
+
highestPriority = detector.priority;
|
|
161
|
+
result = detector.sensitivity;
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
- [ ] **Step 3: Run tests, commit**
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
git commit -m "feat(cortex): add sensitivity auto-classifier for boundary engine"
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
### Task 2: Policy engine
|
|
180
|
+
|
|
181
|
+
**Files:**
|
|
182
|
+
- Create: `src/lib/cortex/boundary/policy.ts`
|
|
183
|
+
- Create: `tests/lib/cortex/boundary/policy.test.ts`
|
|
184
|
+
|
|
185
|
+
- [ ] **Step 1: Write failing tests**
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
// tests/lib/cortex/boundary/policy.test.ts
|
|
189
|
+
import { describe, it, expect } from 'vitest';
|
|
190
|
+
import { PolicyEngine } from '@/lib/cortex/boundary/policy';
|
|
191
|
+
import type { Policy } from '@/lib/cortex/boundary/policy';
|
|
192
|
+
|
|
193
|
+
describe('PolicyEngine', () => {
|
|
194
|
+
it('returns empty actions when no policies match', () => {
|
|
195
|
+
const engine = new PolicyEngine([]);
|
|
196
|
+
const actions = engine.evaluate({ type: 'decision', sensitivity: 'internal' });
|
|
197
|
+
expect(actions).toEqual([]);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('matches by knowledge type', () => {
|
|
201
|
+
const policies: Policy[] = [{
|
|
202
|
+
name: 'arch-decisions-propagate',
|
|
203
|
+
match: { type: 'decision' },
|
|
204
|
+
action: { propagate_to: [{ level: 'department' }] },
|
|
205
|
+
}];
|
|
206
|
+
const engine = new PolicyEngine(policies);
|
|
207
|
+
const actions = engine.evaluate({ type: 'decision', sensitivity: 'internal' });
|
|
208
|
+
expect(actions).toHaveLength(1);
|
|
209
|
+
expect(actions[0].propagate_to).toEqual([{ level: 'department' }]);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('matches by sensitivity', () => {
|
|
213
|
+
const policies: Policy[] = [{
|
|
214
|
+
name: 'lock-confidential',
|
|
215
|
+
match: { sensitivity: 'confidential' },
|
|
216
|
+
action: { cannot_propagate: true },
|
|
217
|
+
}];
|
|
218
|
+
const engine = new PolicyEngine(policies);
|
|
219
|
+
const actions = engine.evaluate({ type: 'pattern', sensitivity: 'confidential' });
|
|
220
|
+
expect(actions).toHaveLength(1);
|
|
221
|
+
expect(actions[0].cannot_propagate).toBe(true);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('matches by topics', () => {
|
|
225
|
+
const policies: Policy[] = [{
|
|
226
|
+
name: 'security-routing',
|
|
227
|
+
match: { topics: ['security', 'vulnerability'] },
|
|
228
|
+
action: { max_scope: 'department', propagate_to: [{ level: 'team', entity_id: 'team-security' }] },
|
|
229
|
+
}];
|
|
230
|
+
const engine = new PolicyEngine(policies);
|
|
231
|
+
const actions = engine.evaluate({ type: 'error_fix', sensitivity: 'restricted', topics: ['security'] });
|
|
232
|
+
expect(actions).toHaveLength(1);
|
|
233
|
+
expect(actions[0].max_scope).toBe('department');
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('returns multiple matching policies', () => {
|
|
237
|
+
const policies: Policy[] = [
|
|
238
|
+
{ name: 'p1', match: { type: 'decision' }, action: { trickle_down: true } },
|
|
239
|
+
{ name: 'p2', match: { sensitivity: 'internal' }, action: { max_scope: 'organization' } },
|
|
240
|
+
];
|
|
241
|
+
const engine = new PolicyEngine(policies);
|
|
242
|
+
const actions = engine.evaluate({ type: 'decision', sensitivity: 'internal' });
|
|
243
|
+
expect(actions).toHaveLength(2);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('does not match when criteria do not overlap', () => {
|
|
247
|
+
const policies: Policy[] = [{
|
|
248
|
+
name: 'security-only',
|
|
249
|
+
match: { topics: ['security'] },
|
|
250
|
+
action: { cannot_propagate: true },
|
|
251
|
+
}];
|
|
252
|
+
const engine = new PolicyEngine(policies);
|
|
253
|
+
const actions = engine.evaluate({ type: 'decision', sensitivity: 'internal', topics: ['architecture'] });
|
|
254
|
+
expect(actions).toHaveLength(0);
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
- [ ] **Step 2: Implement policy engine**
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// src/lib/cortex/boundary/policy.ts
|
|
263
|
+
import type { KnowledgeType, SensitivityClass, ScopeLevel } from '../knowledge/types';
|
|
264
|
+
|
|
265
|
+
export interface PropagationTarget {
|
|
266
|
+
level: ScopeLevel;
|
|
267
|
+
entity_id?: string;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export interface PolicyAction {
|
|
271
|
+
max_scope?: ScopeLevel;
|
|
272
|
+
propagate_to?: PropagationTarget[];
|
|
273
|
+
trickle_down?: boolean;
|
|
274
|
+
cannot_propagate?: boolean;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export interface Policy {
|
|
278
|
+
name: string;
|
|
279
|
+
match: {
|
|
280
|
+
type?: KnowledgeType;
|
|
281
|
+
topics?: string[];
|
|
282
|
+
sensitivity?: SensitivityClass;
|
|
283
|
+
scope_level?: ScopeLevel;
|
|
284
|
+
};
|
|
285
|
+
action: PolicyAction;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export interface PolicyMatchInput {
|
|
289
|
+
type: KnowledgeType | string;
|
|
290
|
+
sensitivity: SensitivityClass | string;
|
|
291
|
+
topics?: string[];
|
|
292
|
+
scope_level?: ScopeLevel | string;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export class PolicyEngine {
|
|
296
|
+
constructor(private policies: Policy[]) {}
|
|
297
|
+
|
|
298
|
+
evaluate(input: PolicyMatchInput): PolicyAction[] {
|
|
299
|
+
const matched: PolicyAction[] = [];
|
|
300
|
+
|
|
301
|
+
for (const policy of this.policies) {
|
|
302
|
+
if (this.matches(policy, input)) {
|
|
303
|
+
matched.push(policy.action);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return matched;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
private matches(policy: Policy, input: PolicyMatchInput): boolean {
|
|
311
|
+
const { match } = policy;
|
|
312
|
+
|
|
313
|
+
if (match.type && match.type !== input.type) return false;
|
|
314
|
+
if (match.sensitivity && match.sensitivity !== input.sensitivity) return false;
|
|
315
|
+
if (match.scope_level && match.scope_level !== input.scope_level) return false;
|
|
316
|
+
|
|
317
|
+
if (match.topics && match.topics.length > 0) {
|
|
318
|
+
const inputTopics = input.topics ?? [];
|
|
319
|
+
const hasOverlap = match.topics.some(t => inputTopics.includes(t));
|
|
320
|
+
if (!hasOverlap) return false;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
- [ ] **Step 3: Run tests, commit**
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
git commit -m "feat(cortex): add policy engine for boundary enforcement"
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Chunk 2: Access Filter and Audit Trail
|
|
337
|
+
|
|
338
|
+
### Task 3: Query-time access filter
|
|
339
|
+
|
|
340
|
+
**Files:**
|
|
341
|
+
- Create: `src/lib/cortex/boundary/access.ts`
|
|
342
|
+
- Create: `tests/lib/cortex/boundary/access.test.ts`
|
|
343
|
+
|
|
344
|
+
- [ ] **Step 1: Write failing tests**
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
// tests/lib/cortex/boundary/access.test.ts
|
|
348
|
+
import { describe, it, expect } from 'vitest';
|
|
349
|
+
import { AccessFilter } from '@/lib/cortex/boundary/access';
|
|
350
|
+
import type { ScoredKnowledge } from '@/lib/cortex/knowledge/types';
|
|
351
|
+
|
|
352
|
+
function makeUnit(overrides: Partial<ScoredKnowledge> = {}): ScoredKnowledge {
|
|
353
|
+
return {
|
|
354
|
+
id: 'k1', vector: [], text: 'test', type: 'decision', layer: 'personal',
|
|
355
|
+
workspace_id: null, session_id: null, agent_type: 'claude',
|
|
356
|
+
project_path: null, file_refs: [], confidence: 0.8,
|
|
357
|
+
created: new Date().toISOString(), source_timestamp: new Date().toISOString(),
|
|
358
|
+
stale_score: 0, access_count: 0, last_accessed: null, metadata: {},
|
|
359
|
+
relevance_score: 0.9, similarity: 0.9,
|
|
360
|
+
sensitivity: 'internal',
|
|
361
|
+
scope: { level: 'personal', entity_id: 'person-alice' },
|
|
362
|
+
origin: { source_type: 'conversation', source_ref: '', creator_entity_id: 'person-alice' },
|
|
363
|
+
...overrides,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
describe('AccessFilter', () => {
|
|
368
|
+
const filter = new AccessFilter({
|
|
369
|
+
requesterId: 'person-alice',
|
|
370
|
+
requesterScope: { level: 'team', entity_id: 'team-platform' },
|
|
371
|
+
requesterOrg: 'organization-acme',
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('allows public knowledge from anywhere in org', () => {
|
|
375
|
+
const unit = makeUnit({ sensitivity: 'public', scope: { level: 'organization', entity_id: 'org-acme' } });
|
|
376
|
+
expect(filter.canAccess(unit)).toBe(true);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('allows internal knowledge within org', () => {
|
|
380
|
+
const unit = makeUnit({ sensitivity: 'internal', scope: { level: 'team', entity_id: 'team-other' } });
|
|
381
|
+
expect(filter.canAccess(unit)).toBe(true);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('allows restricted knowledge within same scope', () => {
|
|
385
|
+
const unit = makeUnit({
|
|
386
|
+
sensitivity: 'restricted',
|
|
387
|
+
scope: { level: 'team', entity_id: 'team-platform' },
|
|
388
|
+
});
|
|
389
|
+
expect(filter.canAccess(unit)).toBe(true);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('denies restricted knowledge from different department', () => {
|
|
393
|
+
const unit = makeUnit({
|
|
394
|
+
sensitivity: 'restricted',
|
|
395
|
+
scope: { level: 'department', entity_id: 'department-sales' },
|
|
396
|
+
});
|
|
397
|
+
// Alice is in team-platform, not department-sales
|
|
398
|
+
expect(filter.canAccess(unit)).toBe(false);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it('allows confidential knowledge from self', () => {
|
|
402
|
+
const unit = makeUnit({
|
|
403
|
+
sensitivity: 'confidential',
|
|
404
|
+
scope: { level: 'personal', entity_id: 'person-alice' },
|
|
405
|
+
origin: { source_type: 'conversation', source_ref: '', creator_entity_id: 'person-alice' },
|
|
406
|
+
});
|
|
407
|
+
expect(filter.canAccess(unit)).toBe(true);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('denies confidential knowledge from others', () => {
|
|
411
|
+
const unit = makeUnit({
|
|
412
|
+
sensitivity: 'confidential',
|
|
413
|
+
scope: { level: 'personal', entity_id: 'person-bob' },
|
|
414
|
+
origin: { source_type: 'conversation', source_ref: '', creator_entity_id: 'person-bob' },
|
|
415
|
+
});
|
|
416
|
+
expect(filter.canAccess(unit)).toBe(false);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('respects creator_scope override (further restriction)', () => {
|
|
420
|
+
const unit = makeUnit({
|
|
421
|
+
sensitivity: 'internal', // normally accessible org-wide
|
|
422
|
+
creator_scope: { max_level: 'personal' }, // but creator restricted to personal only
|
|
423
|
+
scope: { level: 'personal', entity_id: 'person-bob' },
|
|
424
|
+
});
|
|
425
|
+
// Alice can't access Bob's personal-restricted knowledge even though it's internal
|
|
426
|
+
expect(filter.canAccess(unit)).toBe(false);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it('filters a list of results', () => {
|
|
430
|
+
const results = [
|
|
431
|
+
makeUnit({ id: 'a', sensitivity: 'public' }),
|
|
432
|
+
makeUnit({ id: 'b', sensitivity: 'confidential', origin: { source_type: 'conversation', source_ref: '', creator_entity_id: 'person-bob' } }),
|
|
433
|
+
makeUnit({ id: 'c', sensitivity: 'internal' }),
|
|
434
|
+
];
|
|
435
|
+
const filtered = filter.filterResults(results);
|
|
436
|
+
expect(filtered).toHaveLength(2); // a and c pass, b denied
|
|
437
|
+
expect(filtered.map(r => r.id)).toEqual(['a', 'c']);
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
- [ ] **Step 2: Implement access filter**
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
// src/lib/cortex/boundary/access.ts
|
|
446
|
+
import type { ScoredKnowledge, Scope, ScopeLevel, SensitivityClass } from '../knowledge/types';
|
|
447
|
+
|
|
448
|
+
const SCOPE_HIERARCHY: Record<ScopeLevel, number> = {
|
|
449
|
+
personal: 0,
|
|
450
|
+
team: 1,
|
|
451
|
+
department: 2,
|
|
452
|
+
organization: 3,
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
export interface AccessFilterConfig {
|
|
456
|
+
requesterId: string;
|
|
457
|
+
requesterScope: Scope; // requester's team scope
|
|
458
|
+
requesterOrg: string; // requester's org entity_id
|
|
459
|
+
grants?: Set<string>; // knowledge IDs the requester has been granted access to
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
export class AccessFilter {
|
|
463
|
+
private config: AccessFilterConfig;
|
|
464
|
+
|
|
465
|
+
constructor(config: AccessFilterConfig) {
|
|
466
|
+
this.config = config;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
canAccess(unit: ScoredKnowledge): boolean {
|
|
470
|
+
const sensitivity = (unit.sensitivity ?? 'internal') as SensitivityClass;
|
|
471
|
+
const unitScope = unit.scope ?? { level: 'personal' as ScopeLevel, entity_id: '' };
|
|
472
|
+
const creatorId = unit.origin?.creator_entity_id ?? '';
|
|
473
|
+
|
|
474
|
+
// Check creator_scope override first — most restrictive wins
|
|
475
|
+
if (unit.creator_scope) {
|
|
476
|
+
const maxLevel = SCOPE_HIERARCHY[unit.creator_scope.max_level] ?? 0;
|
|
477
|
+
const requesterLevel = this.getRequesterProximityLevel(unitScope);
|
|
478
|
+
if (requesterLevel > maxLevel) return false;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
switch (sensitivity) {
|
|
482
|
+
case 'public':
|
|
483
|
+
return true;
|
|
484
|
+
|
|
485
|
+
case 'internal':
|
|
486
|
+
// Accessible to anyone in the same org
|
|
487
|
+
return true;
|
|
488
|
+
|
|
489
|
+
case 'restricted':
|
|
490
|
+
// Accessible within same scope or if policy grants cross-scope access
|
|
491
|
+
return this.isWithinScope(unitScope);
|
|
492
|
+
|
|
493
|
+
case 'confidential':
|
|
494
|
+
// Only creator or explicit grant
|
|
495
|
+
if (this.config.requesterId === creatorId) return true;
|
|
496
|
+
if (this.config.grants?.has(unit.id)) return true;
|
|
497
|
+
return false;
|
|
498
|
+
|
|
499
|
+
default:
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
filterResults(results: ScoredKnowledge[]): ScoredKnowledge[] {
|
|
505
|
+
return results.filter(r => this.canAccess(r));
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
private isWithinScope(unitScope: Scope): boolean {
|
|
509
|
+
// Same entity_id = same scope
|
|
510
|
+
if (unitScope.entity_id === this.config.requesterScope.entity_id) return true;
|
|
511
|
+
if (unitScope.entity_id === this.config.requesterId) return true;
|
|
512
|
+
// Same org for org-level scopes
|
|
513
|
+
if (unitScope.level === 'organization' && unitScope.entity_id === this.config.requesterOrg) return true;
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
private getRequesterProximityLevel(unitScope: Scope): number {
|
|
518
|
+
// How "far" is the requester from the unit's scope?
|
|
519
|
+
// 0 = same entity, 1 = same scope level, 2+ = further
|
|
520
|
+
if (unitScope.entity_id === this.config.requesterId) return 0;
|
|
521
|
+
if (unitScope.entity_id === this.config.requesterScope.entity_id) return 0;
|
|
522
|
+
return SCOPE_HIERARCHY[unitScope.level] ?? 3;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
- [ ] **Step 3: Run tests, commit**
|
|
528
|
+
|
|
529
|
+
```bash
|
|
530
|
+
git commit -m "feat(cortex): add query-time access filter for boundary enforcement"
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
### Task 4: Audit trail
|
|
536
|
+
|
|
537
|
+
**Files:**
|
|
538
|
+
- Create: `src/lib/cortex/boundary/audit.ts`
|
|
539
|
+
- Create: `tests/lib/cortex/boundary/audit.test.ts`
|
|
540
|
+
|
|
541
|
+
- [ ] **Step 1: Write failing tests**
|
|
542
|
+
|
|
543
|
+
```typescript
|
|
544
|
+
// tests/lib/cortex/boundary/audit.test.ts
|
|
545
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
546
|
+
import fs from 'fs';
|
|
547
|
+
import path from 'path';
|
|
548
|
+
import os from 'os';
|
|
549
|
+
import Database from 'better-sqlite3';
|
|
550
|
+
import { AuditLog } from '@/lib/cortex/boundary/audit';
|
|
551
|
+
|
|
552
|
+
describe('AuditLog', () => {
|
|
553
|
+
let tmpDir: string;
|
|
554
|
+
let audit: AuditLog;
|
|
555
|
+
|
|
556
|
+
beforeEach(() => {
|
|
557
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cortex-audit-'));
|
|
558
|
+
const db = new Database(path.join(tmpDir, 'graph.db'));
|
|
559
|
+
audit = new AuditLog(db);
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
afterEach(() => {
|
|
563
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
it('logs an access decision', () => {
|
|
567
|
+
audit.log({
|
|
568
|
+
requesterId: 'person-alice',
|
|
569
|
+
knowledgeId: 'k1',
|
|
570
|
+
action: 'allowed',
|
|
571
|
+
reason: 'public sensitivity',
|
|
572
|
+
});
|
|
573
|
+
const entries = audit.query({ requesterId: 'person-alice' });
|
|
574
|
+
expect(entries).toHaveLength(1);
|
|
575
|
+
expect(entries[0].action).toBe('allowed');
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
it('logs denied access', () => {
|
|
579
|
+
audit.log({
|
|
580
|
+
requesterId: 'person-alice',
|
|
581
|
+
knowledgeId: 'k2',
|
|
582
|
+
action: 'denied',
|
|
583
|
+
reason: 'confidential, not creator',
|
|
584
|
+
});
|
|
585
|
+
const entries = audit.query({ requesterId: 'person-alice' });
|
|
586
|
+
expect(entries).toHaveLength(1);
|
|
587
|
+
expect(entries[0].action).toBe('denied');
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
it('queries by time range', () => {
|
|
591
|
+
audit.log({ requesterId: 'person-alice', knowledgeId: 'k1', action: 'allowed', reason: 'public' });
|
|
592
|
+
const recent = audit.query({ since: new Date(Date.now() - 60000).toISOString() });
|
|
593
|
+
expect(recent).toHaveLength(1);
|
|
594
|
+
|
|
595
|
+
const future = audit.query({ since: new Date(Date.now() + 60000).toISOString() });
|
|
596
|
+
expect(future).toHaveLength(0);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
it('supports retention cleanup', () => {
|
|
600
|
+
audit.log({ requesterId: 'person-alice', knowledgeId: 'k1', action: 'allowed', reason: 'test' });
|
|
601
|
+
// Cleanup entries older than 0 days (everything)
|
|
602
|
+
audit.cleanup(0);
|
|
603
|
+
const entries = audit.query({});
|
|
604
|
+
expect(entries).toHaveLength(0);
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
- [ ] **Step 2: Implement audit log**
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
// src/lib/cortex/boundary/audit.ts
|
|
613
|
+
import type Database from 'better-sqlite3';
|
|
614
|
+
|
|
615
|
+
export interface AuditEntry {
|
|
616
|
+
requesterId: string;
|
|
617
|
+
knowledgeId: string;
|
|
618
|
+
action: 'allowed' | 'denied';
|
|
619
|
+
reason: string;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
export interface AuditQueryFilter {
|
|
623
|
+
requesterId?: string;
|
|
624
|
+
since?: string; // ISO timestamp
|
|
625
|
+
limit?: number;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
export interface AuditRecord extends AuditEntry {
|
|
629
|
+
timestamp: string;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
export class AuditLog {
|
|
633
|
+
private db: InstanceType<typeof Database>;
|
|
634
|
+
|
|
635
|
+
constructor(db: InstanceType<typeof Database>) {
|
|
636
|
+
this.db = db;
|
|
637
|
+
this.db.exec(`
|
|
638
|
+
CREATE TABLE IF NOT EXISTS audit_log (
|
|
639
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
640
|
+
requester_id TEXT NOT NULL,
|
|
641
|
+
knowledge_id TEXT NOT NULL,
|
|
642
|
+
action TEXT NOT NULL,
|
|
643
|
+
reason TEXT NOT NULL,
|
|
644
|
+
timestamp TEXT NOT NULL
|
|
645
|
+
);
|
|
646
|
+
CREATE INDEX IF NOT EXISTS idx_audit_requester ON audit_log(requester_id);
|
|
647
|
+
CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_log(timestamp);
|
|
648
|
+
`);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
log(entry: AuditEntry): void {
|
|
652
|
+
this.db.prepare(`
|
|
653
|
+
INSERT INTO audit_log (requester_id, knowledge_id, action, reason, timestamp)
|
|
654
|
+
VALUES (?, ?, ?, ?, ?)
|
|
655
|
+
`).run(entry.requesterId, entry.knowledgeId, entry.action, entry.reason, new Date().toISOString());
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
query(filter: AuditQueryFilter): AuditRecord[] {
|
|
659
|
+
let sql = 'SELECT * FROM audit_log WHERE 1=1';
|
|
660
|
+
const params: any[] = [];
|
|
661
|
+
|
|
662
|
+
if (filter.requesterId) {
|
|
663
|
+
sql += ' AND requester_id = ?';
|
|
664
|
+
params.push(filter.requesterId);
|
|
665
|
+
}
|
|
666
|
+
if (filter.since) {
|
|
667
|
+
sql += ' AND timestamp >= ?';
|
|
668
|
+
params.push(filter.since);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
sql += ' ORDER BY timestamp DESC';
|
|
672
|
+
|
|
673
|
+
if (filter.limit) {
|
|
674
|
+
sql += ' LIMIT ?';
|
|
675
|
+
params.push(filter.limit);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
return (this.db.prepare(sql).all(...params) as any[]).map(row => ({
|
|
679
|
+
requesterId: row.requester_id,
|
|
680
|
+
knowledgeId: row.knowledge_id,
|
|
681
|
+
action: row.action,
|
|
682
|
+
reason: row.reason,
|
|
683
|
+
timestamp: row.timestamp,
|
|
684
|
+
}));
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
cleanup(retentionDays: number): void {
|
|
688
|
+
const cutoff = new Date(Date.now() - retentionDays * 86400000).toISOString();
|
|
689
|
+
this.db.prepare('DELETE FROM audit_log WHERE timestamp < ?').run(cutoff);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
- [ ] **Step 3: Run tests, commit**
|
|
695
|
+
|
|
696
|
+
```bash
|
|
697
|
+
git commit -m "feat(cortex): add audit trail for access decisions"
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
---
|
|
701
|
+
|
|
702
|
+
## Chunk 3: Integration
|
|
703
|
+
|
|
704
|
+
### Task 5: Integrate with ingestion pipeline
|
|
705
|
+
|
|
706
|
+
**Files:**
|
|
707
|
+
- Modify: `src/lib/cortex/ingestion/pipeline.ts`
|
|
708
|
+
|
|
709
|
+
- [ ] **Step 1: Read pipeline.ts**
|
|
710
|
+
|
|
711
|
+
- [ ] **Step 2: Add auto-classification during ingestion**
|
|
712
|
+
|
|
713
|
+
Import the classifier:
|
|
714
|
+
```typescript
|
|
715
|
+
import { classifySensitivity } from '../boundary/classifier';
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
In the KnowledgeUnit construction (where v2 fields are set), replace the hardcoded `sensitivity: 'internal'` with:
|
|
719
|
+
```typescript
|
|
720
|
+
sensitivity: classifySensitivity(chunk.text),
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
This ensures every ingested knowledge unit gets an auto-classified sensitivity level.
|
|
724
|
+
|
|
725
|
+
- [ ] **Step 3: Run pipeline tests to verify no regressions**
|
|
726
|
+
|
|
727
|
+
```bash
|
|
728
|
+
npx vitest run tests/lib/cortex/ingestion/
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
- [ ] **Step 4: Commit**
|
|
732
|
+
|
|
733
|
+
```bash
|
|
734
|
+
git commit -m "feat(cortex): auto-classify sensitivity during ingestion"
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
---
|
|
738
|
+
|
|
739
|
+
### Task 6: Integrate AccessFilter with ContextEngine
|
|
740
|
+
|
|
741
|
+
**Files:**
|
|
742
|
+
- Modify: `src/lib/cortex/retrieval/context-engine.ts`
|
|
743
|
+
|
|
744
|
+
- [ ] **Step 1: Read context-engine.ts**
|
|
745
|
+
|
|
746
|
+
- [ ] **Step 2: Add AccessFilter to the pipeline**
|
|
747
|
+
|
|
748
|
+
Import AccessFilter:
|
|
749
|
+
```typescript
|
|
750
|
+
import { AccessFilter } from '../boundary/access';
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
Add to `ContextEngineDeps`:
|
|
754
|
+
```typescript
|
|
755
|
+
accessFilter?: AccessFilter;
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
In the `assemble()` method, after fusion/ranking (Stage 5) but before conflict detection (Stage 6), apply the access filter:
|
|
759
|
+
|
|
760
|
+
```typescript
|
|
761
|
+
// Stage 5.5: Access control filtering
|
|
762
|
+
let accessible = fused;
|
|
763
|
+
if (this.deps.accessFilter) {
|
|
764
|
+
accessible = this.deps.accessFilter.filterResults(fused);
|
|
765
|
+
}
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
Then pass `accessible` (not `fused`) to conflict detection and formatting.
|
|
769
|
+
|
|
770
|
+
- [ ] **Step 3: Run context-engine tests to verify no regressions**
|
|
771
|
+
|
|
772
|
+
```bash
|
|
773
|
+
npx vitest run tests/lib/cortex/retrieval/context-engine.test.ts
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
- [ ] **Step 4: Commit**
|
|
777
|
+
|
|
778
|
+
```bash
|
|
779
|
+
git commit -m "feat(cortex): integrate access filter into context assembly pipeline"
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
---
|
|
783
|
+
|
|
784
|
+
### Task 7: Barrel export and config integration
|
|
785
|
+
|
|
786
|
+
**Files:**
|
|
787
|
+
- Create: `src/lib/cortex/boundary/index.ts`
|
|
788
|
+
- Modify: `src/lib/cortex/config.ts` (add policies array to config)
|
|
789
|
+
|
|
790
|
+
- [ ] **Step 1: Create barrel export**
|
|
791
|
+
|
|
792
|
+
```typescript
|
|
793
|
+
// src/lib/cortex/boundary/index.ts
|
|
794
|
+
export { classifySensitivity } from './classifier';
|
|
795
|
+
export { PolicyEngine } from './policy';
|
|
796
|
+
export type { Policy, PolicyAction, PropagationTarget } from './policy';
|
|
797
|
+
export { AccessFilter } from './access';
|
|
798
|
+
export type { AccessFilterConfig } from './access';
|
|
799
|
+
export { AuditLog } from './audit';
|
|
800
|
+
export type { AuditEntry, AuditRecord, AuditQueryFilter } from './audit';
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
- [ ] **Step 2: Add policies to CortexConfig**
|
|
804
|
+
|
|
805
|
+
In `src/lib/cortex/config.ts`, add to the `CortexConfig` interface:
|
|
806
|
+
```typescript
|
|
807
|
+
policies?: Policy[]; // Organizational boundary policies
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
And in `DEFAULT_CORTEX_CONFIG`:
|
|
811
|
+
```typescript
|
|
812
|
+
policies: [],
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
Import the Policy type:
|
|
816
|
+
```typescript
|
|
817
|
+
import type { Policy } from './boundary/policy';
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
- [ ] **Step 3: Run full test suite**
|
|
821
|
+
|
|
822
|
+
```bash
|
|
823
|
+
npx vitest run tests/lib/cortex/
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
- [ ] **Step 4: Commit**
|
|
827
|
+
|
|
828
|
+
```bash
|
|
829
|
+
git commit -m "feat(cortex): add boundary module barrel export and config integration"
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
---
|
|
833
|
+
|
|
834
|
+
## Summary
|
|
835
|
+
|
|
836
|
+
| Task | Component | Tests | Status |
|
|
837
|
+
|------|-----------|-------|--------|
|
|
838
|
+
| 1 | Sensitivity classifier | 7 | |
|
|
839
|
+
| 2 | Policy engine | 6 | |
|
|
840
|
+
| 3 | Access filter | 8 | |
|
|
841
|
+
| 4 | Audit trail | 4 | |
|
|
842
|
+
| 5 | Pipeline integration | regression | |
|
|
843
|
+
| 6 | ContextEngine integration | regression | |
|
|
844
|
+
| 7 | Barrel export + config | regression | |
|
|
845
|
+
|
|
846
|
+
**Total: 7 tasks, ~25 new tests, 3 chunks**
|
|
847
|
+
|
|
848
|
+
**Key design decisions:**
|
|
849
|
+
- Auto-classification is regex-based (no LLM call) — fast, deterministic, auditable
|
|
850
|
+
- Policies are stored in CortexConfig (admin-editable JSON) — not code
|
|
851
|
+
- AccessFilter is a separate class that can be composed with ContextEngine or used standalone
|
|
852
|
+
- Audit log uses the entity graph's SQLite DB (same file, new table) — no new database
|
|
853
|
+
- Most restrictive classification always wins (confidential > restricted > internal > public)
|