wiki-viewer 1.14.1 → 1.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- 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.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- 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 +1 -1
- 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 +1 -1
- package/.next/standalone/.next/server/app/api/agent/activity/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/events/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/files/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/fs/move/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/fs/search/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/internal/span/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/settings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/agent/sidecar/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/assets/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/share/[token]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/share/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/reveal/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/set-root/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/refresh/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/system/workspaces/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/upload/[...path]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/app/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/backlinks/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/content/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/download/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/file/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/folder/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/git-branches/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/git-checkout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/git-diff/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/git-file-info/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/git-history/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/git-pull/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/move/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/new-file/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/outlinks/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/page/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/presence/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/search/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/slugs/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/upload/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/wiki/watch/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware-manifest.json +1 -1
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/package.json +1 -1
- package/bin/postbuild.js +21 -0
- package/package.json +1 -1
- package/.next/standalone/.github/workflows/publish.yml +0 -37
- package/.next/standalone/.handoffs/archive/handoff-20260612-113332.md +0 -98
- package/.next/standalone/.handoffs/archive/handoff-20260612-141032.md +0 -73
- package/.next/standalone/.pi/ownership.json +0 -3
- package/.next/standalone/AGENTS.md +0 -114
- package/.next/standalone/DESIGN.md +0 -521
- package/.next/standalone/README.md +0 -821
- package/.next/standalone/TODO.md +0 -20
- package/.next/standalone/agents/bootstrap-prompt.md +0 -14
- package/.next/standalone/agents/wiki-viewer-skill/SKILL.md +0 -456
- package/.next/standalone/bin/postbuild.js +0 -78
- package/.next/standalone/bin/wiki-viewer.js +0 -922
- package/.next/standalone/certificates/localhost-key.pem +0 -28
- package/.next/standalone/certificates/localhost.pem +0 -24
- package/.next/standalone/docs/agent-collab-plan.md +0 -1615
- package/.next/standalone/docs/agent-collab-v2-plan.md +0 -771
- package/.next/standalone/docs/agent-fs-plan.md +0 -232
- package/.next/standalone/docs/editorial-skin-spec.md +0 -242
- package/.next/standalone/docs/file-vs-collab-authority.md +0 -124
- package/.next/standalone/docs/mobile-readiness-spec.md +0 -229
- package/.next/standalone/docs/workspacing-plan.md +0 -364
- package/.next/standalone/packages/wiki-viewer-mcp/README.md +0 -195
- package/.next/standalone/packages/wiki-viewer-mcp/bench-e2e.mjs +0 -166
- package/.next/standalone/packages/wiki-viewer-mcp/bench-latency.mjs +0 -87
- package/.next/standalone/packages/wiki-viewer-mcp/bench-payload.mjs +0 -45
- package/.next/standalone/packages/wiki-viewer-mcp/dist/http-client.js +0 -331
- package/.next/standalone/packages/wiki-viewer-mcp/dist/index.js +0 -531
- package/.next/standalone/packages/wiki-viewer-mcp/dist/register.js +0 -94
- package/.next/standalone/packages/wiki-viewer-mcp/dist/state-cache.js +0 -28
- package/.next/standalone/packages/wiki-viewer-mcp/package-lock.json +0 -1716
- package/.next/standalone/packages/wiki-viewer-mcp/package.json +0 -41
- package/.next/standalone/packages/wiki-viewer-mcp/src/http-client.ts +0 -459
- package/.next/standalone/packages/wiki-viewer-mcp/src/index.ts +0 -638
- package/.next/standalone/packages/wiki-viewer-mcp/src/register.ts +0 -144
- package/.next/standalone/packages/wiki-viewer-mcp/src/state-cache.ts +0 -55
- package/.next/standalone/packages/wiki-viewer-mcp/tsconfig.json +0 -19
- package/.next/standalone/pnpm-lock.yaml +0 -6697
- package/.next/standalone/pnpm-workspace.yaml +0 -8
- package/.next/standalone/postcss.config.mjs +0 -2
- package/.next/standalone/src/app/api/agent/activity/route.ts +0 -59
- package/.next/standalone/src/app/api/agent/admin/agents/[agentId]/revoke/route.ts +0 -40
- package/.next/standalone/src/app/api/agent/admin/agents/route.ts +0 -33
- package/.next/standalone/src/app/api/agent/admin/registrations/[regId]/approve/route.ts +0 -83
- package/.next/standalone/src/app/api/agent/admin/registrations/[regId]/deny/route.ts +0 -36
- package/.next/standalone/src/app/api/agent/admin/registrations/route.ts +0 -27
- package/.next/standalone/src/app/api/agent/events/[...path]/route.ts +0 -148
- package/.next/standalone/src/app/api/agent/files/[...path]/route.ts +0 -219
- package/.next/standalone/src/app/api/agent/fs/file/[...path]/route.ts +0 -446
- package/.next/standalone/src/app/api/agent/fs/ls/[[...path]]/route.ts +0 -152
- package/.next/standalone/src/app/api/agent/fs/move/route.ts +0 -130
- package/.next/standalone/src/app/api/agent/fs/search/route.ts +0 -272
- package/.next/standalone/src/app/api/agent/internal/span/route.ts +0 -121
- package/.next/standalone/src/app/api/agent/register/[regId]/route.ts +0 -50
- package/.next/standalone/src/app/api/agent/register/route.ts +0 -118
- package/.next/standalone/src/app/api/agent/settings/route.ts +0 -34
- package/.next/standalone/src/app/api/agent/settings/token/regenerate/route.ts +0 -20
- package/.next/standalone/src/app/api/agent/sidecar/[...path]/route.ts +0 -53
- package/.next/standalone/src/app/api/agents/install/route.ts +0 -143
- package/.next/standalone/src/app/api/agents/skill/route.ts +0 -20
- package/.next/standalone/src/app/api/agents/skill.tar.gz/route.ts +0 -87
- package/.next/standalone/src/app/api/app-proxy/[...path]/route.ts +0 -247
- package/.next/standalone/src/app/api/assets/[...path]/route.ts +0 -75
- package/.next/standalone/src/app/api/auth/[...all]/route.ts +0 -7
- package/.next/standalone/src/app/api/owner/init/route.ts +0 -14
- package/.next/standalone/src/app/api/share/[token]/route.ts +0 -269
- package/.next/standalone/src/app/api/share/route.ts +0 -127
- package/.next/standalone/src/app/api/system/admins/route.ts +0 -103
- package/.next/standalone/src/app/api/system/auth-config/route.ts +0 -22
- package/.next/standalone/src/app/api/system/auth-settings/route.ts +0 -86
- package/.next/standalone/src/app/api/system/browse/route.ts +0 -82
- package/.next/standalone/src/app/api/system/clear-root/route.ts +0 -16
- package/.next/standalone/src/app/api/system/config/route.ts +0 -14
- package/.next/standalone/src/app/api/system/pins/route.ts +0 -38
- package/.next/standalone/src/app/api/system/reveal/route.ts +0 -36
- package/.next/standalone/src/app/api/system/root-status/route.ts +0 -13
- package/.next/standalone/src/app/api/system/set-root/route.ts +0 -53
- package/.next/standalone/src/app/api/system/users/route.ts +0 -120
- package/.next/standalone/src/app/api/system/workspaces/[id]/open/route.ts +0 -34
- package/.next/standalone/src/app/api/system/workspaces/[id]/refresh/route.ts +0 -32
- package/.next/standalone/src/app/api/system/workspaces/[id]/route.ts +0 -78
- package/.next/standalone/src/app/api/system/workspaces/route.ts +0 -128
- package/.next/standalone/src/app/api/upload/[...path]/route.ts +0 -85
- package/.next/standalone/src/app/api/wiki/app/route.ts +0 -60
- package/.next/standalone/src/app/api/wiki/backlinks/route.ts +0 -29
- package/.next/standalone/src/app/api/wiki/content/route.ts +0 -159
- package/.next/standalone/src/app/api/wiki/download/route.ts +0 -100
- package/.next/standalone/src/app/api/wiki/file/route.ts +0 -57
- package/.next/standalone/src/app/api/wiki/folder/route.ts +0 -34
- package/.next/standalone/src/app/api/wiki/git-branches/route.ts +0 -32
- package/.next/standalone/src/app/api/wiki/git-checkout/route.ts +0 -45
- package/.next/standalone/src/app/api/wiki/git-diff/route.ts +0 -33
- package/.next/standalone/src/app/api/wiki/git-file-info/route.ts +0 -27
- package/.next/standalone/src/app/api/wiki/git-history/route.ts +0 -27
- package/.next/standalone/src/app/api/wiki/git-pull/route.ts +0 -48
- package/.next/standalone/src/app/api/wiki/move/route.ts +0 -61
- package/.next/standalone/src/app/api/wiki/new-file/route.ts +0 -56
- package/.next/standalone/src/app/api/wiki/outlinks/route.ts +0 -76
- package/.next/standalone/src/app/api/wiki/page/route.ts +0 -92
- package/.next/standalone/src/app/api/wiki/presence/route.ts +0 -56
- package/.next/standalone/src/app/api/wiki/route.ts +0 -116
- package/.next/standalone/src/app/api/wiki/search/route.ts +0 -44
- package/.next/standalone/src/app/api/wiki/slugs/route.ts +0 -54
- package/.next/standalone/src/app/api/wiki/upload/route.ts +0 -95
- package/.next/standalone/src/app/api/wiki/watch/route.ts +0 -67
- package/.next/standalone/src/app/globals.css +0 -1249
- package/.next/standalone/src/app/icon.svg +0 -11
- package/.next/standalone/src/app/layout.tsx +0 -78
- package/.next/standalone/src/app/manifest.ts +0 -30
- package/.next/standalone/src/app/page.tsx +0 -3260
- package/.next/standalone/src/app/s/[token]/page.tsx +0 -357
- package/.next/standalone/src/app/signin/page.tsx +0 -16
- package/.next/standalone/src/app/signin/signin-form.tsx +0 -238
- package/.next/standalone/src/components/ai-panel/activity-row.tsx +0 -284
- package/.next/standalone/src/components/ai-panel/ai-panel.tsx +0 -311
- package/.next/standalone/src/components/ai-panel/token-section.tsx +0 -313
- package/.next/standalone/src/components/auth-settings-sheet.tsx +0 -474
- package/.next/standalone/src/components/confirm-dialog.tsx +0 -56
- package/.next/standalone/src/components/dir-picker.tsx +0 -606
- package/.next/standalone/src/components/editor/backlinks-panel.tsx +0 -139
- package/.next/standalone/src/components/editor/bubble-menu.tsx +0 -346
- package/.next/standalone/src/components/editor/callout-extension.ts +0 -36
- package/.next/standalone/src/components/editor/comment-pip.tsx +0 -56
- package/.next/standalone/src/components/editor/comment-thread.tsx +0 -254
- package/.next/standalone/src/components/editor/csv-viewer.tsx +0 -379
- package/.next/standalone/src/components/editor/document-outline.tsx +0 -230
- package/.next/standalone/src/components/editor/editor-toolbar.tsx +0 -564
- package/.next/standalone/src/components/editor/editor.tsx +0 -1327
- package/.next/standalone/src/components/editor/extensions/drag-handle.ts +0 -325
- package/.next/standalone/src/components/editor/extensions/heading-anchors.ts +0 -55
- package/.next/standalone/src/components/editor/extensions/proof-span.ts +0 -60
- package/.next/standalone/src/components/editor/extensions/resizable-image.tsx +0 -159
- package/.next/standalone/src/components/editor/extensions.ts +0 -150
- package/.next/standalone/src/components/editor/file-fallback-viewer.tsx +0 -75
- package/.next/standalone/src/components/editor/folder-index.tsx +0 -236
- package/.next/standalone/src/components/editor/google-doc-viewer.tsx +0 -73
- package/.next/standalone/src/components/editor/image-viewer.tsx +0 -57
- package/.next/standalone/src/components/editor/large-file-gate.tsx +0 -108
- package/.next/standalone/src/components/editor/link-popover.tsx +0 -111
- package/.next/standalone/src/components/editor/media-popover.tsx +0 -212
- package/.next/standalone/src/components/editor/media-viewer.tsx +0 -70
- package/.next/standalone/src/components/editor/mermaid-viewer.tsx +0 -286
- package/.next/standalone/src/components/editor/node-app-viewer.tsx +0 -328
- package/.next/standalone/src/components/editor/notebook-viewer.tsx +0 -371
- package/.next/standalone/src/components/editor/office/docx-viewer.tsx +0 -93
- package/.next/standalone/src/components/editor/office/office-chrome.tsx +0 -83
- package/.next/standalone/src/components/editor/office/pptx-viewer.tsx +0 -104
- package/.next/standalone/src/components/editor/office/xlsx-viewer.tsx +0 -192
- package/.next/standalone/src/components/editor/pdf-viewer.tsx +0 -32
- package/.next/standalone/src/components/editor/proof-span-popover.tsx +0 -162
- package/.next/standalone/src/components/editor/slash-commands.tsx +0 -629
- package/.next/standalone/src/components/editor/source-viewer.tsx +0 -289
- package/.next/standalone/src/components/editor/suggest-edit-popover.tsx +0 -229
- package/.next/standalone/src/components/editor/suggestion-card.tsx +0 -206
- package/.next/standalone/src/components/editor/table-menu.tsx +0 -232
- package/.next/standalone/src/components/editor/view-mode-comment-button.tsx +0 -77
- package/.next/standalone/src/components/editor/website-viewer.tsx +0 -75
- package/.next/standalone/src/components/editor/wiki-link-create-dialog.tsx +0 -189
- package/.next/standalone/src/components/editor/wiki-link-decorator.ts +0 -79
- package/.next/standalone/src/components/editor/wiki-link-extension.ts +0 -120
- package/.next/standalone/src/components/editor/wiki-link-picker.tsx +0 -250
- package/.next/standalone/src/components/layout/viewer-toolbar.tsx +0 -54
- package/.next/standalone/src/components/search/search-command-dialog.tsx +0 -246
- package/.next/standalone/src/components/search/sidebar-search-box.tsx +0 -121
- package/.next/standalone/src/components/search/snippet-text.tsx +0 -48
- package/.next/standalone/src/components/share-dialog.tsx +0 -307
- package/.next/standalone/src/components/theme-provider.tsx +0 -16
- package/.next/standalone/src/components/theme-toggle.tsx +0 -24
- package/.next/standalone/src/components/ui/badge.tsx +0 -35
- package/.next/standalone/src/components/ui/button.tsx +0 -56
- package/.next/standalone/src/components/ui/card.tsx +0 -63
- package/.next/standalone/src/components/ui/collapsible.tsx +0 -9
- package/.next/standalone/src/components/ui/command.tsx +0 -151
- package/.next/standalone/src/components/ui/context-menu.tsx +0 -191
- package/.next/standalone/src/components/ui/dialog.tsx +0 -119
- package/.next/standalone/src/components/ui/dropdown-menu.tsx +0 -194
- package/.next/standalone/src/components/ui/input.tsx +0 -22
- package/.next/standalone/src/components/ui/label.tsx +0 -25
- package/.next/standalone/src/components/ui/popover.tsx +0 -33
- package/.next/standalone/src/components/ui/scroll-area.tsx +0 -47
- package/.next/standalone/src/components/ui/select.tsx +0 -156
- package/.next/standalone/src/components/ui/separator.tsx +0 -30
- package/.next/standalone/src/components/ui/sheet.tsx +0 -140
- package/.next/standalone/src/components/ui/skeleton.tsx +0 -15
- package/.next/standalone/src/components/ui/switch.tsx +0 -28
- package/.next/standalone/src/components/ui/tabs.tsx +0 -54
- package/.next/standalone/src/components/ui/textarea.tsx +0 -21
- package/.next/standalone/src/components/ui/tip.tsx +0 -23
- package/.next/standalone/src/components/ui/toggle-group.tsx +0 -41
- package/.next/standalone/src/components/ui/tooltip.tsx +0 -27
- package/.next/standalone/src/components/view-width-toggle.tsx +0 -70
- package/.next/standalone/src/components/wiki/frontmatter-header.tsx +0 -164
- package/.next/standalone/src/hooks/use-is-mobile.ts +0 -14
- package/.next/standalone/src/lib/app-runner.ts +0 -272
- package/.next/standalone/src/lib/auth/admin.ts +0 -120
- package/.next/standalone/src/lib/auth/allowlist.ts +0 -50
- package/.next/standalone/src/lib/auth/client.ts +0 -4
- package/.next/standalone/src/lib/auth/csrf.ts +0 -68
- package/.next/standalone/src/lib/auth/server.ts +0 -215
- package/.next/standalone/src/lib/cabinets/tree.ts +0 -75
- package/.next/standalone/src/lib/config.ts +0 -114
- package/.next/standalone/src/lib/embeds/detect.ts +0 -142
- package/.next/standalone/src/lib/git-secrets.ts +0 -65
- package/.next/standalone/src/lib/git.ts +0 -503
- package/.next/standalone/src/lib/google/detect.ts +0 -107
- package/.next/standalone/src/lib/markdown/parse-frontmatter.ts +0 -104
- package/.next/standalone/src/lib/markdown/remark-wikilinks.ts +0 -100
- package/.next/standalone/src/lib/markdown/sanitize-schema.ts +0 -106
- package/.next/standalone/src/lib/markdown/to-html.ts +0 -282
- package/.next/standalone/src/lib/markdown/to-markdown.ts +0 -252
- package/.next/standalone/src/lib/proof/activity-shared.ts +0 -31
- package/.next/standalone/src/lib/proof/activity.ts +0 -74
- package/.next/standalone/src/lib/proof/audit.ts +0 -88
- package/.next/standalone/src/lib/proof/auth.ts +0 -143
- package/.next/standalone/src/lib/proof/block-refs.ts +0 -133
- package/.next/standalone/src/lib/proof/blocks.ts +0 -73
- package/.next/standalone/src/lib/proof/client-auth.ts +0 -23
- package/.next/standalone/src/lib/proof/collab-state.ts +0 -81
- package/.next/standalone/src/lib/proof/event-bus.ts +0 -47
- package/.next/standalone/src/lib/proof/file-lock.ts +0 -43
- package/.next/standalone/src/lib/proof/glob.ts +0 -51
- package/.next/standalone/src/lib/proof/idempotency.ts +0 -37
- package/.next/standalone/src/lib/proof/lease.ts +0 -117
- package/.next/standalone/src/lib/proof/mutex.ts +0 -36
- package/.next/standalone/src/lib/proof/ops-applier.ts +0 -727
- package/.next/standalone/src/lib/proof/owner-auth.ts +0 -2
- package/.next/standalone/src/lib/proof/pending.ts +0 -97
- package/.next/standalone/src/lib/proof/proof-span.ts +0 -141
- package/.next/standalone/src/lib/proof/rate-limit.ts +0 -53
- package/.next/standalone/src/lib/proof/raw-fs.ts +0 -197
- package/.next/standalone/src/lib/proof/register-rate-limit.ts +0 -53
- package/.next/standalone/src/lib/proof/registry.ts +0 -195
- package/.next/standalone/src/lib/proof/sidecar.ts +0 -103
- package/.next/standalone/src/lib/proof/suggest-capture.ts +0 -69
- package/.next/standalone/src/lib/proof/types.ts +0 -182
- package/.next/standalone/src/lib/proof-config.ts +0 -14
- package/.next/standalone/src/lib/root-dir.ts +0 -35
- package/.next/standalone/src/lib/search/indexable-exts.ts +0 -25
- package/.next/standalone/src/lib/search/indexer.ts +0 -634
- package/.next/standalone/src/lib/search/sanitize.ts +0 -35
- package/.next/standalone/src/lib/search/search-db.ts +0 -57
- package/.next/standalone/src/lib/search/watcher-pool.ts +0 -79
- package/.next/standalone/src/lib/shared-docs/db.ts +0 -177
- package/.next/standalone/src/lib/toast.ts +0 -9
- package/.next/standalone/src/lib/utils.ts +0 -6
- package/.next/standalone/src/lib/wiki-helpers.ts +0 -36
- package/.next/standalone/src/lib/workspace-client.ts +0 -82
- package/.next/standalone/src/lib/workspace-context.ts +0 -173
- package/.next/standalone/src/lib/workspaces.ts +0 -400
- package/.next/standalone/src/middleware.ts +0 -44
- package/.next/standalone/src/stores/ai-panel-store.ts +0 -79
- package/.next/standalone/src/stores/editor-store.ts +0 -439
- package/.next/standalone/src/stores/pin-store.ts +0 -56
- package/.next/standalone/src/stores/proof-store.ts +0 -124
- package/.next/standalone/src/stores/recent-store.ts +0 -51
- package/.next/standalone/src/stores/search-store.ts +0 -104
- package/.next/standalone/src/stores/show-hidden-store.ts +0 -26
- package/.next/standalone/src/stores/sidebar-width-store.ts +0 -39
- package/.next/standalone/src/stores/skin-store.ts +0 -42
- package/.next/standalone/src/stores/tree-store.ts +0 -52
- package/.next/standalone/src/stores/view-width-store.ts +0 -101
- package/.next/standalone/src/stores/wiki-slugs-store.ts +0 -75
- package/.next/standalone/src/tests/proof/activity-aggregator.test.ts +0 -146
- package/.next/standalone/src/tests/proof/admin-create-user.test.ts +0 -43
- package/.next/standalone/src/tests/proof/agent-fs.test.ts +0 -973
- package/.next/standalone/src/tests/proof/agent-ownership.test.ts +0 -140
- package/.next/standalone/src/tests/proof/agent-workspace-scope.test.ts +0 -134
- package/.next/standalone/src/tests/proof/agents-install.test.ts +0 -110
- package/.next/standalone/src/tests/proof/bench-write.ts +0 -179
- package/.next/standalone/src/tests/proof/better-auth.test.ts +0 -68
- package/.next/standalone/src/tests/proof/block-refs.test.ts +0 -108
- package/.next/standalone/src/tests/proof/blocks.test.ts +0 -92
- package/.next/standalone/src/tests/proof/collab-state.test.ts +0 -501
- package/.next/standalone/src/tests/proof/comments-ops.test.ts +0 -177
- package/.next/standalone/src/tests/proof/editor-roundtrip.test.ts +0 -85
- package/.next/standalone/src/tests/proof/external-edit.test.ts +0 -58
- package/.next/standalone/src/tests/proof/file-lock.test.ts +0 -80
- package/.next/standalone/src/tests/proof/git-branches-checkout.test.ts +0 -136
- package/.next/standalone/src/tests/proof/git-read-features.test.ts +0 -473
- package/.next/standalone/src/tests/proof/git-secrets.test.ts +0 -138
- package/.next/standalone/src/tests/proof/git-sparse.test.ts +0 -257
- package/.next/standalone/src/tests/proof/git-workspace.test.ts +0 -135
- package/.next/standalone/src/tests/proof/git.test.ts +0 -243
- package/.next/standalone/src/tests/proof/glob.test.ts +0 -82
- package/.next/standalone/src/tests/proof/helpers/auth-session.ts +0 -27
- package/.next/standalone/src/tests/proof/markdown-to-html.test.ts +0 -46
- package/.next/standalone/src/tests/proof/ops-applier.test.ts +0 -368
- package/.next/standalone/src/tests/proof/owner-init.test.ts +0 -2
- package/.next/standalone/src/tests/proof/parse-frontmatter.test.ts +0 -60
- package/.next/standalone/src/tests/proof/preload.ts +0 -21
- package/.next/standalone/src/tests/proof/proof-span.test.ts +0 -98
- package/.next/standalone/src/tests/proof/rate-limit.test.ts +0 -54
- package/.next/standalone/src/tests/proof/readonly-enforcement.test.ts +0 -161
- package/.next/standalone/src/tests/proof/reconcile-sidecar.test.ts +0 -195
- package/.next/standalone/src/tests/proof/registration-flow.test.ts +0 -330
- package/.next/standalone/src/tests/proof/registry.test.ts +0 -169
- package/.next/standalone/src/tests/proof/routes.test.ts +0 -516
- package/.next/standalone/src/tests/proof/search-index.test.ts +0 -376
- package/.next/standalone/src/tests/proof/sidecar-lifecycle.test.ts +0 -177
- package/.next/standalone/src/tests/proof/sidecar-trim.test.ts +0 -58
- package/.next/standalone/src/tests/proof/suggestion-ops.test.ts +0 -280
- package/.next/standalone/src/tests/proof/wiki-content-put.test.ts +0 -140
- package/.next/standalone/src/tests/proof/wiki-routes-auth.test.ts +0 -208
- package/.next/standalone/src/tests/proof/workspace-isolation.test.ts +0 -87
- package/.next/standalone/src/tests/proof/workspaces-api.test.ts +0 -227
- package/.next/standalone/src/types/index.ts +0 -47
- package/.next/standalone/tailwind.config.ts +0 -191
- package/.next/standalone/tsconfig.json +0 -41
- package/.next/standalone/tsconfig.tsbuildinfo +0 -1
- /package/.next/standalone/.next/static/{WMOTgfV82apmdovxzgbMx → rRyvFORvJoc5OWbCR2A_P}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{WMOTgfV82apmdovxzgbMx → rRyvFORvJoc5OWbCR2A_P}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{WMOTgfV82apmdovxzgbMx → rRyvFORvJoc5OWbCR2A_P}/_ssgManifest.js +0 -0
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
# Agent Filesystem Plan — "Two tiers, one spine"
|
|
2
|
-
|
|
3
|
-
**Status:** Proposed (co-designed; ready for sign-off).
|
|
4
|
-
**Goal:** Let a remote AI agent work with files in a wiki-viewer instance "barely worse than local filesystem", for **all file types**, without disturbing the existing markdown collaboration feature.
|
|
5
|
-
**Principle:** Elegant, minimalistic, boring on purpose. The bytes API is strict and observable; the collab tier stays special. No CRDT pivot. No exec. No PATCH. No JSON-RPC.
|
|
6
|
-
|
|
7
|
-
> Supersedes the unbuilt `docs/agent-collab-v2-plan.md` Yjs/Hocuspocus/Postgres direction, which is treated as discarded. App today = `better-auth` + `better-sqlite3`, file-on-disk-is-truth.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## 1. The model: two provenance tiers, one shared spine
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
┌────────────────────────────────────────────┐
|
|
15
|
-
│ SHARED SPINE │
|
|
16
|
-
│ discovery (/api/agents/install) │
|
|
17
|
-
│ TOFU register → approve → one-shot token │
|
|
18
|
-
│ Authorization: Bearer + X-Agent-Id │
|
|
19
|
-
│ enforceScope(path-glob, op) │
|
|
20
|
-
│ withFileMutex (in-proc + proper-lockfile) │
|
|
21
|
-
│ safeRootPath (traversal/symlink guard) │
|
|
22
|
-
│ event log + audit │
|
|
23
|
-
└───────────────┬───────────────┬────────────┘
|
|
24
|
-
│ │
|
|
25
|
-
┌──────────────────▼──┐ ┌─────▼───────────────────────┐
|
|
26
|
-
│ TIER 1 — RAW FS │ │ TIER 2 — COLLAB (existing) │
|
|
27
|
-
│ all file types │ │ markdown only │
|
|
28
|
-
│ read/write/ls/ │ │ block-ops + proof-spans │
|
|
29
|
-
│ move/delete/search │ │ comments / suggestions │
|
|
30
|
-
│ boring bytes │ │ reviewable prose, accept/ │
|
|
31
|
-
│ light audit │ │ revert provenance │
|
|
32
|
-
└─────────────────────┘ └─────────────────────────────┘
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
- **Tier 1 (Raw FS, NEW):** bytes on disk, every file type, fast agent tooling. Light audit (event + sha). The "do filework" tier.
|
|
36
|
-
- **Tier 2 (Collab, EXISTING, UNCHANGED):** markdown review layer — block-ops, `<proof-span>` marks, comments, suggestions. The "reviewable prose" tier.
|
|
37
|
-
- **Do NOT** fold block-ops into the fs endpoint as a content-type. Collab is a review/provenance _workflow_, not file IO. Different invariants. Keep them separate; share the spine only.
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## 2. Tier 1 — Raw FS API (v1, minimal)
|
|
42
|
-
|
|
43
|
-
All routes reuse `checkAuth` + `enforceScope` + `withFileMutex` + `safeRootPath`. All reject paths under `.proof/`, the lock dir, and the app db/config. Scope is enforced on **every** surface (reads, listings, search results, move endpoints).
|
|
44
|
-
|
|
45
|
-
| Method | Route | Purpose |
|
|
46
|
-
| -------- | ----------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
47
|
-
| `GET` | `/api/agent/fs/file/<path>` | Read file. Supports `Range`. Returns raw bytes. |
|
|
48
|
-
| `PUT` | `/api/agent/fs/file/<path>` | Atomic whole-file write. `If-Match: <sha256>` optional (REQUIRED for `.md`). |
|
|
49
|
-
| `DELETE` | `/api/agent/fs/file/<path>` | Delete file (+ sidecar if `.md`). |
|
|
50
|
-
| `GET` | `/api/agent/fs/ls/<path>?recursive&limit&depth` | Directory listing. Scope-filtered. |
|
|
51
|
-
| `POST` | `/api/agent/fs/move` | `{from, to, ifMatch?}`. Moves sidecar for `.md`. |
|
|
52
|
-
| `POST` | `/api/agent/fs/search` | `{kind:"grep"\|"glob", query, path?, glob?, limit?}`. Server-side. |
|
|
53
|
-
|
|
54
|
-
**No exec. No batch. No separate grep/glob.** Add later only if profiling proves need.
|
|
55
|
-
|
|
56
|
-
> **Update (shipped):** A server-side `PATCH /api/agent/fs/file/<path>` str-replace endpoint was added after profiling proved the whole-file-PUT payload dominates latency for large docs on slow uplinks (500KB @ 0.5Mbps ≈ 7.8s/edit transfer). It is strict: exact substring (no regex), text/UTF-8 only, `If-Match` required, `expectedOccurrences` must match exactly (default 1, else 422 `MATCH_COUNT_MISMATCH`). It shares one mutation code path (`applyMutation`) with PUT, so lock / R6 `COLLAB_ACTIVE` / reconcile / audit behave identically. The MCP `edit_file` tool uses PATCH first and falls back to read+PUT on older servers.
|
|
57
|
-
|
|
58
|
-
### 2.1 Read (`GET .../file/<path>`)
|
|
59
|
-
|
|
60
|
-
- Returns **raw bytes**, not a JSON wrapper.
|
|
61
|
-
- Metadata in headers: `ETag: "<sha256>"`, `X-File-Size`, `X-File-Mtime`, `Content-Type`.
|
|
62
|
-
- Supports HTTP `Range` for big/binary files.
|
|
63
|
-
|
|
64
|
-
### 2.2 Write (`PUT .../file/<path>`)
|
|
65
|
-
|
|
66
|
-
- Atomic: write temp in **same dir** → `fsync` → `rename`. (`fsync` dir optional but documented.)
|
|
67
|
-
- Preserve mode of existing file. Do **not** create parent dirs unless `?mkdirs=true`.
|
|
68
|
-
- **Create = implicit.** `PUT` to a non-existent path creates the file (write-with-no-prior). Omit `If-Match` for a create; send `If-Match: <sha256>` only when overwriting. A create where the file already exists (no `If-Match`) → 412, so creates can't silently clobber.
|
|
69
|
-
- `If-Match: <sha256>` → 412 on mismatch. **Required by default for overwrites** (see R4); explicit `?force=true` bypasses and is audited.
|
|
70
|
-
- Response: `{path, sha256, size, mtime, created: bool}`.
|
|
71
|
-
|
|
72
|
-
### 2.3 Listing (`GET .../ls`)
|
|
73
|
-
|
|
74
|
-
- Hard limits: `max entries`, `max depth`, cursor or hard cap. Excludes `.proof/`; ignores `.git/` (configurable).
|
|
75
|
-
- Each entry: `{name, type, size, mtime}`. Unauthorized paths omitted (scope-filtered).
|
|
76
|
-
|
|
77
|
-
### 2.4 Search (`POST .../search`)
|
|
78
|
-
|
|
79
|
-
- `kind: "grep" | "glob"`. No shell interpolation (spawn ripgrep with arg array, or in-process).
|
|
80
|
-
- Limits: timeout, max matches, max bytes scanned, skip binary. Each matched path re-checked against scope.
|
|
81
|
-
- This is the **round-trip-explosion mitigation** — one call replaces dozens of `ls`+`read`.
|
|
82
|
-
|
|
83
|
-
### 2.5 Move (`POST .../move`)
|
|
84
|
-
|
|
85
|
-
- Checks: source `read`+`mutate`, dest `mutate`.
|
|
86
|
-
- For `.md`: moves the `.proof/<path>.json` sidecar too. Lock ordering: single operation lock, or lock source+dest in **sorted key order** to avoid deadlock.
|
|
87
|
-
|
|
88
|
-
### 2.6 Delete (`DELETE .../file/<path>`)
|
|
89
|
-
|
|
90
|
-
- Requires the `delete` scope op (see §3.5) and `If-Match: <sha256>` — the agent must have read the current file first, so it cannot blind-delete. This `If-Match` requirement **is** the confirmation for v1.
|
|
91
|
-
- `.md`: also deletes the sidecar (R3).
|
|
92
|
-
- Directory delete is opt-in and explicit: `?recursive=true`; without it, deleting a non-empty dir is refused. (No human approval queue in v1 — deferred.)
|
|
93
|
-
|
|
94
|
-
---
|
|
95
|
-
|
|
96
|
-
## 3. Tier 2 — Collab (unchanged)
|
|
97
|
-
|
|
98
|
-
Markdown block-ops (`block.replace/insertAfter/insertBefore/delete/append/prepend`), `comment.*`, `suggestion.*`, `<proof-span>` marks, `baseRevision` optimistic concurrency, mandatory `Idempotency-Key`, sidecar JSON in `.proof/`. **No changes.** This remains the reviewable-prose path.
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
## 3.5 Working mode vs Collaborating mode (THE central distinction)
|
|
103
|
-
|
|
104
|
-
The whole design hinges on the agent knowing **when a file is being actively co-edited by a human** (use the reviewable Tier-2 path) versus **when it's just a file to work on** (use fast raw Tier-1). Today nothing tells the agent this; a wrong guess clobbers a human's pending suggestions. We make the mode **discoverable, documented, and enforced** — three layers so the agent cannot miss it.
|
|
105
|
-
|
|
106
|
-
### The signal: `X-Collab-State` header (on every read, both tiers)
|
|
107
|
-
|
|
108
|
-
Every `GET fs/file/<path>` and every Tier-2 snapshot read returns:
|
|
109
|
-
|
|
110
|
-
```
|
|
111
|
-
X-Collab-State: active | tracked | untracked | not-markdown
|
|
112
|
-
X-Collab-Snapshot: /api/agent/files/<path>.md # present when state != not-markdown
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
| State | Meaning | Agent should… |
|
|
116
|
-
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
117
|
-
| `active` | `.md` that is **live**: either has review artifacts (pending suggestions / unresolved comments / unreverted proof-spans) **OR has a current human edit lease** (someone has the doc open in the editor). A human is collaborating right now. | **Use Tier-2 block-ops.** Your edits become reviewable suggestions. A raw `PUT` is rejected unless `If-Collab-Match` matches (see R6). |
|
|
118
|
-
| `tracked` | `.md` with a sidecar, no artifacts, no lease. | **Prefer Tier-2 for any semantic/prose edit** so provenance accrues. Raw only for mechanical/whole-file ops (e.g. reformat, regenerate). Not a free pass. |
|
|
119
|
-
| `untracked` | `.md`, no sidecar, no lease — nobody has collaborated. | Raw ok. A reviewable (Tier-2) edit creates a sidecar so future edits are tracked. |
|
|
120
|
-
| `not-markdown` | Any non-`.md` file. | **Tier-1 raw only** (Tier-2 doesn't apply). |
|
|
121
|
-
|
|
122
|
-
**Definition of `active` (two sources, OR'd):**
|
|
123
|
-
|
|
124
|
-
1. **Artifacts:** `pendingSuggestions > 0` OR `unresolvedComments > 0` OR `proofSpanCount > 0` (from sidecar — cheap, already in memory on read).
|
|
125
|
-
2. **Human edit lease:** a short-TTL presence marker (default 90s, heartbeat ~30s) set when a human opens the doc in the editor and refreshed while it stays open. **This closes the false-negative GPT-5.5 caught:** a human who opens a doc but hasn't typed a suggestion yet still reads `active`, so the agent won't blind-write into a live session. Lease lives in-memory (or the existing SQLite), keyed by `(path)`, set by a lightweight `POST /api/wiki/presence` ping the editor already can issue alongside its chokidar SSE connection. No lease + no artifacts → not active.
|
|
126
|
-
|
|
127
|
-
### Layer 2 — documented (manifest + skill)
|
|
128
|
-
|
|
129
|
-
The `/api/agents/install` manifest and the skill state the rule in one sentence:
|
|
130
|
-
|
|
131
|
-
> _"Before editing a `.md`, read it and check `X-Collab-State`. If `active`, use block-ops (Tier 2) so a human can review. Otherwise use raw fs (Tier 1). For non-markdown, always use raw fs."_
|
|
132
|
-
|
|
133
|
-
### Layer 3 — enforced (R4 + R6 backstop)
|
|
134
|
-
|
|
135
|
-
Even a confused or racing agent cannot silently clobber a live session: a raw `PUT` to an `active` `.md` is **rejected with 409 `COLLAB_ACTIVE`** unless it carries a matching `If-Collab-Match` (R6) — and the state is re-checked atomically inside the write mutex, so a session that goes active mid-flight still wins. Any genuinely orphaned suggestion/comment is marked `stale` by reconciliation (R2), never dropped. The 409 returns the Tier-2 snapshot URL, re-teaching the mode.
|
|
136
|
-
|
|
137
|
-
### What the human sees
|
|
138
|
-
|
|
139
|
-
- Agent works in Tier-2 on an `active` doc → normal reviewable suggestions (existing UX).
|
|
140
|
-
- Agent raw-writes an `active` doc anyway → `file.rawWritten by ai:<id>` in the AI Panel feed + any unbindable anchors flagged `stale` (not vanished).
|
|
141
|
-
- Agent works on `untracked`/non-md → silent, fast, audited. No collab noise.
|
|
142
|
-
|
|
143
|
-
---
|
|
144
|
-
|
|
145
|
-
## 4. Integration rules (load-bearing)
|
|
146
|
-
|
|
147
|
-
- **R1 — Shared lock.** A raw write/move/delete on a `.md` acquires the **same** `withFileMutex` key the ops-applier uses. No interleaving with block-ops mid-proof-span.
|
|
148
|
-
- **R2 — No fake provenance.** Raw writes never author proof-spans. After a raw write to a tracked `.md`, emit `file.rawWritten` (writer known) and **reconcile the sidecar synchronously, inside the same mutex** (rebuild `refMap`, set fingerprint to `newSha`, mark affected proof refs stale). Raw edits are unmarked by definition; human sees _who_ via the event, not as an accept/revert suggestion.
|
|
149
|
-
- **GOTCHA (do not regress):** do NOT just "bump fingerprint to newSha and let existing reconciliation fire later." The existing reconciliation triggers on `sidecar.fingerprint != file sha256`; if the raw write sets fingerprint current, the mismatch is gone and rebuild never runs. Because the writer is known, reconcile **eagerly within the write**, not lazily on next read.
|
|
150
|
-
- **R3 — Sidecar lifecycle.** Raw `mv`/`rm` of a `.md` moves/deletes its sidecar. **NOTE:** the human `wiki/move` route currently does a bare `rename` and does NOT move the sidecar — a latent bug that orphans `.proof/` JSON today. Build a shared `moveSidecar`/`deleteSidecar` helper in `sidecar.ts` and wire **both** the raw-fs path and `wiki/move` to it (fixes the existing bug for free).
|
|
151
|
-
- **R4 — Write guard (If-Match by default).** **All** mutating raw ops (`PUT`/`DELETE`/`move`) REQUIRE `If-Match: <sha256>` by default; 412 on mismatch. An explicit `?force=true` bypasses it and is recorded in the audit row. Rationale: agents edit code/config just as much as `.md`; optional concurrency = silent lost updates, and a format-based safety boundary is arbitrary. Cheap because the agent already has the sha from `GET`.
|
|
152
|
-
- **R5 — Scope unification + `delete` op.** Registration `scope.paths` glob already covers **directories natively** (e.g. `notes/**` grants the whole subtree — no file lists). Raw-fs honors the same glob, on every surface (read/ls/search/move/delete results all re-checked). **One addition:** split `delete` out of `mutate`, so `scope.ops` becomes `[read, mutate, delete]`. This lets a human grant "edit but never delete." `mutate` = create/overwrite/move; `delete` = remove. Back-compat: existing `[read, mutate]` agents simply can't delete until re-scoped. Glob dialect (v1): `**`, `*`, `?` only — no braces/negation; advertise this in the manifest.
|
|
153
|
-
- **R6 — Collab-state precondition on raw `.md` writes (closes the TOCTOU race).** `If-Match` protects _bytes_, not _collaboration intent_: a human could create a sidecar/lease after the agent's read but before its raw `PUT`, leaving bytes unchanged so `If-Match` still passes. To prevent silently writing into a session that went `active` mid-flight:
|
|
154
|
-
- Every `.md` read also returns `X-Collab-Revision: <n>` (bumped on any sidecar/lease state change).
|
|
155
|
-
- A raw `PUT` to a `.md` must, **inside the same mutex, re-read collab state immediately before writing**: if the doc is now `active` and the request did not supply a matching `If-Collab-Match: <n>`, **reject with 409 `COLLAB_ACTIVE`** + the Tier-2 snapshot URL. The agent then switches to block-ops. `?force=true` still bypasses (audited) for deliberate overrides.
|
|
156
|
-
- This makes the mode check atomic with the write, not advisory.
|
|
157
|
-
|
|
158
|
-
### Event taxonomy (don't overload)
|
|
159
|
-
|
|
160
|
-
- `file.rawWritten` — writer **known** (`by: ai:<id>`, `oldSha`, `newSha`, `path`, `at`). Emitted by raw-fs mutations.
|
|
161
|
-
- `file.externallyEdited` — writer **unknown** (chokidar watcher, vim, git). Existing behavior, untouched.
|
|
162
|
-
|
|
163
|
-
### Audit
|
|
164
|
-
|
|
165
|
-
- `.md`: append event to sidecar (already have the machinery).
|
|
166
|
-
- All other files: durable audit row (reuse SQLite). Non-negotiable — without it an agent could silently rewrite a PDF.
|
|
167
|
-
|
|
168
|
-
### Collab-anchor safety (raw `.md` overwrite)
|
|
169
|
-
|
|
170
|
-
A full-file `PUT` to a `.md` can strip `<proof-span>` marks and shift block boundaries, orphaning pending comments/suggestions. **Invariant:** after any raw write to a `.md`, the synchronous reconciliation (R2) MUST re-bind or mark-stale every affected proof ref **before** the human UI reads the snapshot. A suggestion/comment whose anchor no longer resolves is flagged `stale`, not silently dropped. This is the explicit guard that keeps Tier 1 from corrupting Tier 2.
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
## 5. Discovery
|
|
175
|
-
|
|
176
|
-
Extend `/api/agents/install` manifest:
|
|
177
|
-
|
|
178
|
-
- Advertise the new `fs/*` routes.
|
|
179
|
-
- Add a `capabilities` block: `{ maxFileBytes, supportsRange, ifMatchRequired:true, forceBypass:true, search:["grep","glob"], globDialect:"**,*,?", scopeOps:["read","mutate","delete"], collabStates:["active","tracked","untracked","not-markdown"], collabPrecondition:"If-Collab-Match" }`.
|
|
180
|
-
- State the **working-vs-collaborating rule** (§3.5) in prose so the agent self-configures its tier choice.
|
|
181
|
-
- Advertise the optional MCP adapter package + version.
|
|
182
|
-
One manifest; agent self-configures. HTTP routes are the canonical contract.
|
|
183
|
-
|
|
184
|
-
---
|
|
185
|
-
|
|
186
|
-
## 6. Client story
|
|
187
|
-
|
|
188
|
-
- **Canonical = the HTTP API.** Easy to curl, test, version. Independent of MCP transport churn.
|
|
189
|
-
- **`npx wiki-viewer-mcp`** = thin MCP adapter mapping standard MCP filesystem tools onto these endpoints:
|
|
190
|
-
- `read_file` → `GET fs/file`
|
|
191
|
-
- `write_file` → `PUT fs/file` (with `If-Match` from a prior read)
|
|
192
|
-
- `edit_file` (str-replace) → **client-side**: read → transform → `PUT If-Match`. (Server stays dumb; edit footguns live in the adapter.)
|
|
193
|
-
- `list_directory` → `GET fs/ls`
|
|
194
|
-
- `search` → `POST fs/search`
|
|
195
|
-
- `delete_file` → `DELETE fs/file` (requires `delete` scope + `If-Match`)
|
|
196
|
-
- The shim reads `X-Collab-State` and, when `active`, **warns or routes the edit through the Tier-2 block-op tools** instead of a blind raw write — so even off-the-shelf MCP clients respect collaborating mode.
|
|
197
|
-
- Do **not** make the core app MCP-native first. The app already has HTTP auth/discovery; the shim adapts to Claude Code / Cursor / Codex without poisoning the core.
|
|
198
|
-
|
|
199
|
-
---
|
|
200
|
-
|
|
201
|
-
## 7. Path identity (must specify before build)
|
|
202
|
-
|
|
203
|
-
- Stable **relative** paths only; reject traversal and symlinks escaping root.
|
|
204
|
-
- Decide + document symlink listing behavior.
|
|
205
|
-
- Hard-deny: `.proof/`, `~/.wiki-viewer/.locks/`, the app SQLite db + config.
|
|
206
|
-
- Unicode normalization policy stated explicitly.
|
|
207
|
-
|
|
208
|
-
---
|
|
209
|
-
|
|
210
|
-
## 8. Non-goals (explicit)
|
|
211
|
-
|
|
212
|
-
- **No remote compute / exec.** wiki-viewer is a remote **filesystem + review surface**, not a sandbox. Real exec needs quotas, secrets/env/cwd policy, process lifecycle, cancellation, logs — a different product. Server-side `search` covers the ~80% case (find code) without it. Future: a separate optional "workspace daemon" with its own scope + manifest capability.
|
|
213
|
-
- **No CRDT / Yjs / Hocuspocus.** File-on-disk stays truth.
|
|
214
|
-
- **No PATCH, no JSON-RPC, no batch endpoint** in v1.
|
|
215
|
-
|
|
216
|
-
---
|
|
217
|
-
|
|
218
|
-
## 9. Build order
|
|
219
|
-
|
|
220
|
-
1. Spine confirm: nothing new — reuse auth/scope/lock/safePath.
|
|
221
|
-
2. `GET/PUT/DELETE fs/file` + atomic write + `If-Match` + headers/ETag/Range.
|
|
222
|
-
3. Audit row + `file.rawWritten` event; wire R1/R2 for `.md`.
|
|
223
|
-
4. `fs/ls` (limits, scope filter) + `fs/move` (sidecar move, R3).
|
|
224
|
-
5. `fs/search` (grep/glob, limits).
|
|
225
|
-
6. Extend `/api/agents/install` manifest + capabilities.
|
|
226
|
-
7. `wiki-viewer-mcp` adapter (read/write/edit/list/search).
|
|
227
|
-
8. **Mode plumbing:** human edit-lease (`POST /api/wiki/presence` + TTL, editor heartbeat), `X-Collab-State` + `X-Collab-Revision` headers on both read paths, R6 atomic re-check + 409 `COLLAB_ACTIVE` on raw `.md` writes. Plus `delete` scope op + create/`?mkdirs`/`?recursive` semantics.
|
|
228
|
-
9. Tests: traversal, symlink escape, If-Match 412, create-collision 412, sidecar move (incl. `wiki/move` bug fix), scope filtering on ls/search, big-file Range, binary skip in grep, **`X-Collab-State` matrix incl. lease-only `active`**, **R6 TOCTOU: doc goes active between read and raw PUT → 409**, stale-anchor after raw `.md` overwrite, `delete` op gating, lease expiry flips active→tracked.
|
|
229
|
-
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
```
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
# Editorial Skin Spec — "Literary Review"
|
|
2
|
-
|
|
3
|
-
A selectable skin for wiki-viewer. Refined printed-journal aesthetic (LRB / vintage
|
|
4
|
-
Penguin / n+1), not a loud brutalist newspaper. Calm authority for a reading-and-writing tool.
|
|
5
|
-
|
|
6
|
-
It is a NEW axis, independent of the existing light/dark theme. Composes 4 ways:
|
|
7
|
-
default-light, default-dark, editorial-light, editorial-dark.
|
|
8
|
-
|
|
9
|
-
## Architecture
|
|
10
|
-
|
|
11
|
-
- Existing theme axis: `next-themes`, `attribute="class"`, toggles `.dark` on `<html>`.
|
|
12
|
-
- New skin axis: `data-skin="editorial"` attribute on `<html>`. Absence = default skin.
|
|
13
|
-
- Persistence: `localStorage` key `wiki-skin`. No-flash inline script in `layout.tsx` sets
|
|
14
|
-
the attribute before paint (same pattern next-themes uses).
|
|
15
|
-
- Selectable from settings sheet Appearance section.
|
|
16
|
-
|
|
17
|
-
CSS layering in globals.css (append AFTER existing :root and .dark blocks so it overrides):
|
|
18
|
-
|
|
19
|
-
```css
|
|
20
|
-
[data-skin="editorial"] {
|
|
21
|
-
/* editorial light token overrides */
|
|
22
|
-
}
|
|
23
|
-
[data-skin="editorial"].dark {
|
|
24
|
-
/* editorial dark token overrides */
|
|
25
|
-
}
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Editorial only OVERRIDES tokens (fonts, colors, radius). Inherits everything else.
|
|
29
|
-
|
|
30
|
-
## Fonts (next/font/google, real, loadable)
|
|
31
|
-
|
|
32
|
-
In layout.tsx, load alongside existing Inter (keep Inter for default skin):
|
|
33
|
-
|
|
34
|
-
- `Fraunces` — display: headings, drop caps, document title. variable, opsz axis.
|
|
35
|
-
CSS var `--font-fraunces`.
|
|
36
|
-
- `Newsreader` — body prose + editorial UI labels. variable. CSS var `--font-newsreader`.
|
|
37
|
-
- `IBM Plex Mono` — file tree, code, metadata, outline panel. weights 400,500,600.
|
|
38
|
-
CSS var `--font-plex-mono`.
|
|
39
|
-
|
|
40
|
-
Expose all three variables on `<body>` className alongside `--font-inter`.
|
|
41
|
-
|
|
42
|
-
Tailwind: add fontFamily entries `display` (Fraunces), `reading` (Newsreader), `mono`
|
|
43
|
-
(IBM Plex Mono) referencing the vars with serif/mono fallbacks. Do NOT change the existing
|
|
44
|
-
`sans`/`serif` defaults (default skin must stay Inter).
|
|
45
|
-
|
|
46
|
-
Under editorial skin, body font becomes Newsreader, headings/.tiptap headings become Fraunces,
|
|
47
|
-
code/tree/outline become IBM Plex Mono — via the [data-skin] token block setting font on the
|
|
48
|
-
relevant containers (use a `--skin-font-body`, `--skin-font-display`, `--skin-font-mono`
|
|
49
|
-
indirection OR target containers directly in the editorial CSS block).
|
|
50
|
-
|
|
51
|
-
## Color tokens — map to EXISTING var names
|
|
52
|
-
|
|
53
|
-
### Editorial Light `[data-skin="editorial"]`
|
|
54
|
-
|
|
55
|
-
```
|
|
56
|
-
--background: #faf6f0; /* warm newsprint */
|
|
57
|
-
--foreground: #1a1714; /* warm ink */
|
|
58
|
-
--card: #fffdfa;
|
|
59
|
-
--card-foreground: #1a1714;
|
|
60
|
-
--popover: #fffdfa;
|
|
61
|
-
--popover-foreground: #1a1714;
|
|
62
|
-
--primary: #b54a1f; /* terracotta accent (NOT vermilion — avoids destructive clash) */
|
|
63
|
-
--primary-foreground: #fffdfa;
|
|
64
|
-
--primary-soft: #f3e2d8;
|
|
65
|
-
--secondary: #f1ebe1;
|
|
66
|
-
--secondary-foreground: #1a1714;
|
|
67
|
-
--muted: #f1ebe1;
|
|
68
|
-
--muted-foreground: #756c5f;
|
|
69
|
-
--accent: #f1ebe1;
|
|
70
|
-
--accent-foreground: #b54a1f;
|
|
71
|
-
--accent-soft: #f3e2d8;
|
|
72
|
-
--destructive: #b3261e; /* keep distinct true-red for danger */
|
|
73
|
-
--destructive-foreground: #fffdfa;
|
|
74
|
-
--destructive-soft: #f7dcda;
|
|
75
|
-
--success: #4a7c59;
|
|
76
|
-
--success-soft: #dceadf;
|
|
77
|
-
--warning: #b8860b;
|
|
78
|
-
--warning-soft: #f5ecd2;
|
|
79
|
-
--warning-ink: #8a6508;
|
|
80
|
-
--info: #1a1714;
|
|
81
|
-
--border: #ddd2c2; /* parchment hairline */
|
|
82
|
-
--input: #ccc0ad;
|
|
83
|
-
--switch-checked: #b54a1f;
|
|
84
|
-
--ring: #b54a1f;
|
|
85
|
-
--overlay: rgb(26 23 20 / 0.4);
|
|
86
|
-
--radius: 0px; /* square print corners */
|
|
87
|
-
--sidebar-background: #f4eee4;
|
|
88
|
-
--sidebar-foreground: #1a1714;
|
|
89
|
-
--sidebar-primary: #b54a1f;
|
|
90
|
-
--sidebar-primary-foreground: #fffdfa;
|
|
91
|
-
--sidebar-accent: #faf6f0;
|
|
92
|
-
--sidebar-accent-foreground: #b54a1f;
|
|
93
|
-
--sidebar-border: #ddd2c2;
|
|
94
|
-
--sidebar-ring: #b54a1f;
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### Editorial Dark `[data-skin="editorial"].dark`
|
|
98
|
-
|
|
99
|
-
```
|
|
100
|
-
--background: #1a1714; /* warm charcoal */
|
|
101
|
-
--foreground: #ece4d8; /* warm paper-white */
|
|
102
|
-
--card: #221e1a;
|
|
103
|
-
--card-foreground: #ece4d8;
|
|
104
|
-
--popover: #221e1a;
|
|
105
|
-
--popover-foreground: #ece4d8;
|
|
106
|
-
--primary: #e8a07d; /* warm terracotta, lifted for dark */
|
|
107
|
-
--primary-foreground: #1a1714;
|
|
108
|
-
--primary-soft: #3a2c22;
|
|
109
|
-
--secondary: #262019;
|
|
110
|
-
--secondary-foreground: #ece4d8;
|
|
111
|
-
--muted: #262019;
|
|
112
|
-
--muted-foreground: #a89e8e;
|
|
113
|
-
--accent: #2c2620;
|
|
114
|
-
--accent-foreground: #e8a07d;
|
|
115
|
-
--accent-soft: #3a2c22;
|
|
116
|
-
--destructive: #e5544b;
|
|
117
|
-
--destructive-foreground: #1a1714;
|
|
118
|
-
--destructive-soft: #4a211d;
|
|
119
|
-
--success: #7fae8c;
|
|
120
|
-
--success-soft: #24341f;
|
|
121
|
-
--warning: #d9a441;
|
|
122
|
-
--warning-soft: #3a2e18;
|
|
123
|
-
--warning-ink: #d9a441;
|
|
124
|
-
--info: #ece4d8;
|
|
125
|
-
--border: #382f26;
|
|
126
|
-
--input: #4a3f33;
|
|
127
|
-
--switch-checked: #e8a07d;
|
|
128
|
-
--ring: #e8a07d;
|
|
129
|
-
--overlay: rgb(0 0 0 / 0.6);
|
|
130
|
-
--sidebar-background: #161310;
|
|
131
|
-
--sidebar-foreground: #ece4d8;
|
|
132
|
-
--sidebar-primary: #e8a07d;
|
|
133
|
-
--sidebar-primary-foreground: #1a1714;
|
|
134
|
-
--sidebar-accent: #221e1a;
|
|
135
|
-
--sidebar-accent-foreground: #e8a07d;
|
|
136
|
-
--sidebar-border: #382f26;
|
|
137
|
-
--sidebar-ring: #e8a07d;
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
Accent appears on: links, active file in tree (left-border + text), drop cap, focus ring,
|
|
141
|
-
section-label underline, blockquote rule, active toolbar buttons.
|
|
142
|
-
|
|
143
|
-
## Prose redesign — `.tiptap` under `[data-skin="editorial"]`
|
|
144
|
-
|
|
145
|
-
Scope ALL of this under `[data-skin="editorial"] .tiptap` so default skin untouched.
|
|
146
|
-
|
|
147
|
-
- Container: `max-width: 66ch; margin: 0 auto;` body font Newsreader,
|
|
148
|
-
`font-size: 1.0625rem; line-height: 1.65; letter-spacing: 0;`
|
|
149
|
-
`text-rendering: optimizeLegibility; font-feature-settings: "liga","kern";`
|
|
150
|
-
- Headings: Fraunces. h1 2.25rem/600, h2 1.6rem/600, h3 1.25rem/600, h4 1.05rem/600 small-caps.
|
|
151
|
-
Generous top margin (h1 0, h2 2em, h3 1.6em).
|
|
152
|
-
- Drop cap: `[data-skin="editorial"] .tiptap > p:first-of-type::first-letter` —
|
|
153
|
-
`float:left; font-family:Fraunces; font-size:3.4em; line-height:0.72; padding:0.05em 0.12em 0 0;
|
|
154
|
-
color:var(--primary); font-weight:600;`
|
|
155
|
-
- Scotch rule under document title: handled in chrome (content header), see below.
|
|
156
|
-
- Blockquote: no gray fill. `border-left: 3px solid var(--primary); padding-left: 1.1rem;
|
|
157
|
-
font-style: italic; font-size: 1.15rem; color: var(--foreground);`
|
|
158
|
-
- Inline code: IBM Plex Mono, `0.85em`, `background: var(--muted); border: 1px solid var(--border);
|
|
159
|
-
border-radius: 0; padding: 0.05rem 0.3rem;`
|
|
160
|
-
- Code block (pre): IBM Plex Mono, `border-radius: 0; border-top: 1px solid var(--border);
|
|
161
|
-
border-bottom: 1px solid var(--border); background: var(--muted);` keep existing hljs token colors
|
|
162
|
-
(they read fine on warm muted).
|
|
163
|
-
- Links: `color: var(--primary); text-decoration: underline; text-underline-offset: 2px;
|
|
164
|
-
text-decoration-thickness: 1px;`
|
|
165
|
-
- Lists: `ul { list-style-type: square; }` keep ol decimal.
|
|
166
|
-
- hr (asterism): hide the line, inject centered `✳ ✳ ✳` (Fraunces, muted, letter-spacing 0.4em)
|
|
167
|
-
via `::before` on a zero-border hr; `margin: 2.5rem 0;`
|
|
168
|
-
- Tables: square corners, hairline borders var(--border), header row small-caps Fraunces.
|
|
169
|
-
|
|
170
|
-
## Chrome redesign — under `[data-skin="editorial"]`
|
|
171
|
-
|
|
172
|
-
All square corners (radius:0 via token). Targets are utility classes on existing markup in
|
|
173
|
-
src/app/page.tsx; prefer adding skin-scoped CSS in globals.css over rewriting JSX where possible.
|
|
174
|
-
Where a class hook is missing, add a stable className to the JSX (do not restructure layout).
|
|
175
|
-
|
|
176
|
-
- Sidebar / file tree: IBM Plex Mono for file names (`--font-plex-mono`), `font-size: 0.8125rem`.
|
|
177
|
-
Active file: left border `2px solid var(--primary)`, text color var(--primary), no rounded
|
|
178
|
-
background pill. Hover: subtle `var(--muted)` fill, no radius. Folder/file lucide icons may
|
|
179
|
-
stay but de-emphasize (muted-foreground).
|
|
180
|
-
- Top toolbars (`bg-muted border-b`): make background `var(--background)` (transparent feel),
|
|
181
|
-
keep single hairline bottom `1px solid var(--border)`.
|
|
182
|
-
- Content pane header (the `flex items-center justify-between px-4 py-2 border-b` at page.tsx
|
|
183
|
-
~1990 / ~2100): under this header add a SCOTCH RULE as the bottom border — implement via a
|
|
184
|
-
skin-scoped `::after` or replace border with layered box-shadow:
|
|
185
|
-
`box-shadow: 0 1px 0 0 var(--foreground), 0 3px 0 0 var(--foreground)` is wrong;
|
|
186
|
-
use: bottom border 2px solid var(--foreground) + a 1px var(--foreground) hairline 2px below via
|
|
187
|
-
`::after` absolute. Keep it ONLY on the active-document header, not every bar.
|
|
188
|
-
Simpler acceptable version: `border-bottom: 3px double var(--foreground)` (CSS double rule).
|
|
189
|
-
Use the `double` rule approach for reliability.
|
|
190
|
-
- Document title in header: Fraunces, slightly larger, tracking -0.01em.
|
|
191
|
-
- Outline panel (right): IBM Plex Mono, uppercase tracked `caption` style for the "Outline" label
|
|
192
|
-
(small-caps, letter-spacing 0.12em, muted). Active outline item: var(--primary).
|
|
193
|
-
- Buttons: square. Primary = solid var(--primary). Ghost/secondary = transparent, hairline border
|
|
194
|
-
appears on hover. Inputs: square, hairline border, focus ring var(--ring).
|
|
195
|
-
|
|
196
|
-
## Section labels (signature)
|
|
197
|
-
|
|
198
|
-
Any existing uppercase tracked `<h3>` labels (settings sections, sidebar headers): under editorial
|
|
199
|
-
render as small-caps Fraunces with a short terracotta underline rule. Scope via skin selector.
|
|
200
|
-
|
|
201
|
-
## Motion
|
|
202
|
-
|
|
203
|
-
Restrained. Page/content load: content fades opacity 0→1 over 400ms ease-out (no slide).
|
|
204
|
-
Hover: color swaps instant, background fades 120ms. No bounce. Respect existing motion tokens.
|
|
205
|
-
|
|
206
|
-
## Settings UI — Appearance section
|
|
207
|
-
|
|
208
|
-
Add a new `<section className="space-y-2">` in auth-settings-sheet.tsx (after Account, before
|
|
209
|
-
Signup allowlist) titled "Appearance". Contains a skin selector: two choices "Default" and
|
|
210
|
-
"Editorial" (segmented buttons or radio row matching existing button styling). Wire to the new
|
|
211
|
-
skin store. Keep the existing ThemeToggle behavior intact (theme axis is separate; it lives in
|
|
212
|
-
the top bar today — leave it there).
|
|
213
|
-
|
|
214
|
-
## Skin store
|
|
215
|
-
|
|
216
|
-
Create `src/stores/skin-store.ts` (zustand, matching existing store conventions). State:
|
|
217
|
-
`skin: "default" | "editorial"`, `setSkin(skin)`. On set: write `localStorage["wiki-skin"]`
|
|
218
|
-
and set/remove `document.documentElement.dataset.skin`. Initialize from the attribute the
|
|
219
|
-
no-flash script already set (read on mount), do NOT re-read localStorage in a way that causes
|
|
220
|
-
hydration mismatch.
|
|
221
|
-
|
|
222
|
-
## No-flash script (layout.tsx)
|
|
223
|
-
|
|
224
|
-
Add an inline `<script>` in `<head>` (before body) that reads `localStorage["wiki-skin"]` and,
|
|
225
|
-
if `=== "editorial"`, sets `document.documentElement.setAttribute("data-skin","editorial")`.
|
|
226
|
-
Mirror next-themes' approach. Use `suppressHydrationWarning` already present on `<html>`.
|
|
227
|
-
|
|
228
|
-
## Highest-impact, ranked
|
|
229
|
-
|
|
230
|
-
1. Fonts wired + editorial token block (the single biggest visible jump — serif prose + warm paper)
|
|
231
|
-
2. .tiptap prose redesign (drop cap, measure, blockquote, links)
|
|
232
|
-
3. File tree mono + active terracotta border
|
|
233
|
-
4. Scotch rule under document title + square corners
|
|
234
|
-
5. Outline panel + toolbars + settings selector
|
|
235
|
-
|
|
236
|
-
## Out of scope / do not touch
|
|
237
|
-
|
|
238
|
-
- Do not change default-skin appearance at all. Every editorial rule MUST be scoped under
|
|
239
|
-
`[data-skin="editorial"]`.
|
|
240
|
-
- Do not restructure page.tsx layout/DOM hierarchy. Add classNames only where a hook is missing.
|
|
241
|
-
- Do not alter auth, agent API, editor logic, stores other than the new skin-store.
|
|
242
|
-
- Do not bump deps. next/font/google fonts need no install.
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
# File vs Collab Authority Model
|
|
2
|
-
|
|
3
|
-
**TL;DR:** File on disk is truth. Tier-1 raw edits are fast + audited but unmarked. Tier-2 block-ops are slower but reviewable. The `X-Collab-State` header tells an agent which tier to use; the server enforces it atomically.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Source of truth
|
|
8
|
-
|
|
9
|
-
The `.md` file on disk is the canonical document. The `.proof/<path>.json` sidecar holds provenance metadata (comments, suggestions, proof-span refs, fingerprint) but is never the source of truth for content. If the sidecar is lost the document is intact; if the document is lost the sidecar is orphaned.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Two tiers
|
|
14
|
-
|
|
15
|
-
### Tier 1 — Raw FS
|
|
16
|
-
|
|
17
|
-
- Routes: `GET/PUT/DELETE /api/agent/fs/file/<path>`, `GET /api/agent/fs/ls/...`, `POST /api/agent/fs/move`, `POST /api/agent/fs/search`.
|
|
18
|
-
- All file types. Raw bytes in, raw bytes out.
|
|
19
|
-
- Writes are **atomic** (temp-file + rename in same directory).
|
|
20
|
-
- **Audit:** every mutation records a row in the `agent_fs_audit` SQLite table (`path`, `op`, `agentId`, `oldSha`, `newSha`, `forced`, `at`).
|
|
21
|
-
- **Event:** `file.rawWritten` emitted with `{ by: "ai:<id>", path, oldSha, newSha }`. Distinct from `file.externallyEdited` (chokidar, writer unknown).
|
|
22
|
-
- No `<proof-span>` marks. No accept/revert UI. Audit log is the paper trail.
|
|
23
|
-
|
|
24
|
-
### Tier 2 — Collab
|
|
25
|
-
|
|
26
|
-
- Routes: `GET/POST /api/agent/files/<path>.md`, `GET/POST /api/agent/events/<path>.md`.
|
|
27
|
-
- Markdown only.
|
|
28
|
-
- Edits are wrapped in `<proof-span>` marks inline in the `.md` file.
|
|
29
|
-
- Provenance (suggestions, comments, block refs) stored in `.proof/<path>.json` sidecar.
|
|
30
|
-
- Human can accept or revert individual spans in the editor UI.
|
|
31
|
-
- Mandatory `baseRevision` optimistic concurrency + `Idempotency-Key` per request.
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
## The `X-Collab-State` header
|
|
36
|
-
|
|
37
|
-
Every `GET /api/agent/fs/file/<path>` (Tier-1 read) and every Tier-2 snapshot read returns:
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
X-Collab-State: active | tracked | untracked | not-markdown
|
|
41
|
-
X-Collab-Revision: <n>
|
|
42
|
-
X-Collab-Snapshot: /api/agent/files/<path>.md # present when not not-markdown
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### State machine
|
|
46
|
-
|
|
47
|
-
```
|
|
48
|
-
[file created]
|
|
49
|
-
│
|
|
50
|
-
▼
|
|
51
|
-
untracked ──── first Tier-2 op ────► tracked
|
|
52
|
-
│ │
|
|
53
|
-
│ (Tier-1 writes stay untracked) │ human opens editor
|
|
54
|
-
│ ▼
|
|
55
|
-
│ active
|
|
56
|
-
│ │
|
|
57
|
-
│ editor closes +
|
|
58
|
-
│ no artifacts
|
|
59
|
-
│ │
|
|
60
|
-
│ ▼
|
|
61
|
-
└────────────────────────────────── tracked
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
**`active`** is set by two independent sources (OR'd):
|
|
65
|
-
|
|
66
|
-
1. **Artifacts** — sidecar has `pendingSuggestions > 0` OR `unresolvedComments > 0` OR `proofSpanCount > 0`. Cheap: always in memory when sidecar is loaded.
|
|
67
|
-
2. **Human edit lease** — a short-TTL presence marker (default 90 s, heartbeat ~30 s) set via `POST /api/wiki/presence { path, action: "open" | "heartbeat" | "close" }`. Closes the false-negative: a human who opens a doc but hasn't typed a suggestion yet still reads `active`.
|
|
68
|
-
|
|
69
|
-
`X-Collab-Revision` bumps on any sidecar write OR lease open/close. Used by R6.
|
|
70
|
-
|
|
71
|
-
---
|
|
72
|
-
|
|
73
|
-
## R6 — atomic TOCTOU race fix
|
|
74
|
-
|
|
75
|
-
`If-Match` (sha256) protects _bytes_. But a human could open the doc between the agent's read and its raw `PUT`, making it `active` while bytes are unchanged — so `If-Match` passes but the write clobbers a live session.
|
|
76
|
-
|
|
77
|
-
**Fix:** a raw `PUT` to any `.md` re-checks `collabState` **inside the same `withFileMutex` call, immediately before writing**. If the state is now `active` and the request did not supply a matching `If-Collab-Match: <revision>`, the write is **rejected 409 `COLLAB_ACTIVE`** with the Tier-2 URL. This check is atomic with the write — not advisory.
|
|
78
|
-
|
|
79
|
-
`?force=true` bypasses both `If-Match` and `If-Collab-Match` and is recorded in the audit row as `forced: true`.
|
|
80
|
-
|
|
81
|
-
---
|
|
82
|
-
|
|
83
|
-
## R2 — eager sidecar reconciliation
|
|
84
|
-
|
|
85
|
-
After a raw `PUT` to a tracked/active `.md`, the sidecar must be reconciled **synchronously, inside the same mutex**, before the lock is released:
|
|
86
|
-
|
|
87
|
-
1. Re-scan the new file bytes for surviving `<proof-span>` marks.
|
|
88
|
-
2. Re-bind each proof ref to its new block offset; mark unresolvable refs `stale: true`.
|
|
89
|
-
3. Set `sidecar.fingerprint = newSha` (so the lazy mismatch-trigger in `readSnapshot` does NOT re-fire — the reconcile already ran).
|
|
90
|
-
4. Emit `file.rawWritten` event.
|
|
91
|
-
|
|
92
|
-
**Do not** just bump the fingerprint and let reconciliation fire lazily on the next read. The lazy trigger fires only when `sidecar.fingerprint != file sha256`; once the raw write sets fingerprint current, the mismatch is gone and rebuild never runs. Eager reconciliation is the correct invariant.
|
|
93
|
-
|
|
94
|
-
---
|
|
95
|
-
|
|
96
|
-
## Known v1 limitations
|
|
97
|
-
|
|
98
|
-
### (a) Editor does not call `/api/wiki/presence` yet
|
|
99
|
-
|
|
100
|
-
The front-end editor does **not** currently issue `POST /api/wiki/presence` on open/heartbeat/close. Consequence: lease-based `active` never fires. Only artifact-based `active` works (pending suggestions, unresolved comments, proof-spans in sidecar).
|
|
101
|
-
|
|
102
|
-
**Impact:** a human who opens a doc but has no pending review artifacts will see `X-Collab-State: tracked` (or `untracked`), not `active`. An agent raw-writing that doc won't be blocked. This is a safety gap — not a correctness bug (no data loss), but the intended "lock out while human is live-editing" experience is incomplete until the editor heartbeat is wired.
|
|
103
|
-
|
|
104
|
-
**Fix:** call `POST /api/wiki/presence { path, action: "open" }` when the editor mounts the doc and `{ action: "close" }` on unmount. Heartbeat every ~30 s. Tracked as a follow-up task.
|
|
105
|
-
|
|
106
|
-
### (b) Lease store is in-memory (single-process only)
|
|
107
|
-
|
|
108
|
-
The presence lease map lives in the Node.js process memory. In a multi-process deployment (PM2 cluster mode, multiple Next.js instances behind a load balancer) each process has its own lease state and processes do not share it.
|
|
109
|
-
|
|
110
|
-
**Impact:** a human's open lease set by process A is invisible to process B. A raw write handled by process B may not see the `active` state and proceed when it should block.
|
|
111
|
-
|
|
112
|
-
**Fix:** move the lease store to the shared SQLite (`~/.wiki-viewer/auth.db`) with a TTL column and a periodic cleanup job. The table is already shared across the process boundary. Tracked as a follow-up task.
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
## Sidecar lifecycle rules
|
|
117
|
-
|
|
118
|
-
| Raw-fs op | On non-`.md` | On `.md` |
|
|
119
|
-
| ------------------------ | ------------ | ------------------------------------------------------------------ |
|
|
120
|
-
| `PUT` (create/overwrite) | plain write | acquire mutex → write → reconcile sidecar → emit `file.rawWritten` |
|
|
121
|
-
| `DELETE` | plain delete | delete file + delete `.proof/<path>.json` sidecar |
|
|
122
|
-
| `POST /fs/move` | plain rename | rename file + rename `.proof/<old>.json` → `.proof/<new>.json` |
|
|
123
|
-
|
|
124
|
-
The human `wiki/move` route was previously a bare `rename` that orphaned sidecars. It now uses the same shared `moveSidecar` helper (fixed in Phase 1).
|