@lincyaw/kernel 1.0.0-rc.0 → 1.0.0-rc.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.
Files changed (119) hide show
  1. package/README.md +7 -7
  2. package/package.json +6 -2
  3. package/src/access.ts +18 -0
  4. package/src/anchors.ts +5 -1
  5. package/src/authoring.ts +56 -0
  6. package/src/blocks.ts +0 -8
  7. package/src/component-code.ts +6 -0
  8. package/src/contracts/agent.ts +5 -82
  9. package/src/contracts/anchors.ts +46 -0
  10. package/src/contracts/authoring-actions.ts +60 -0
  11. package/src/contracts/authoring-clients.ts +213 -0
  12. package/src/contracts/authoring.ts +180 -0
  13. package/src/contracts/checks.ts +29 -0
  14. package/src/contracts/classroom.ts +49 -0
  15. package/src/contracts/diagnostics.ts +37 -0
  16. package/src/contracts/document-audio.ts +150 -0
  17. package/src/contracts/document-comments.ts +31 -0
  18. package/src/contracts/document-history.ts +63 -0
  19. package/src/contracts/document-image.ts +32 -0
  20. package/src/contracts/document-reads.ts +57 -0
  21. package/src/contracts/document-voice.ts +44 -0
  22. package/src/contracts/document.ts +240 -0
  23. package/src/contracts/execution.ts +4 -5
  24. package/src/contracts/locale.ts +3 -0
  25. package/src/contracts/offline.ts +2 -0
  26. package/src/contracts/plugin.ts +183 -0
  27. package/src/contracts/presenter-panels.ts +17 -0
  28. package/src/contracts/preview.ts +305 -310
  29. package/src/contracts/production-assets.ts +61 -0
  30. package/src/contracts/production.ts +109 -0
  31. package/src/contracts/projects.ts +59 -0
  32. package/src/contracts/server.ts +1 -51
  33. package/src/contracts/speech-settings.ts +16 -0
  34. package/src/contracts/speech.ts +82 -70
  35. package/src/contracts/steps.ts +15 -0
  36. package/src/contracts/transcription-adoption.ts +79 -0
  37. package/src/contracts/transcription.ts +112 -0
  38. package/src/contracts/voice.ts +97 -0
  39. package/src/contracts/voiceover-execution.ts +68 -0
  40. package/src/contracts/workspace.ts +458 -0
  41. package/src/contracts.ts +28 -278
  42. package/src/diagnostics.ts +7 -0
  43. package/src/document-authoring-actions.ts +47 -0
  44. package/src/document-caption-timings.ts +64 -0
  45. package/src/document-colors.ts +25 -0
  46. package/src/document-comments.ts +118 -0
  47. package/src/document-components.ts +251 -0
  48. package/src/document-operations.ts +132 -0
  49. package/src/document-pages.ts +215 -0
  50. package/src/document-reads.ts +81 -0
  51. package/src/document-schema.ts +85 -0
  52. package/src/document-voices.ts +153 -0
  53. package/src/document.ts +252 -0
  54. package/src/durations.ts +7 -0
  55. package/src/errors.ts +8 -0
  56. package/src/execution.ts +5 -1
  57. package/src/fingerprint.ts +1 -1
  58. package/src/follow.ts +26 -30
  59. package/src/git.ts +1 -1
  60. package/src/index.ts +66 -13
  61. package/src/ir.ts +54 -5
  62. package/src/jupyter-kernel.ts +9 -0
  63. package/src/kernel.ts +38 -25
  64. package/src/keyboard.ts +43 -0
  65. package/src/locale.ts +13 -0
  66. package/src/lock.ts +4 -8
  67. package/src/logging.ts +1 -1
  68. package/src/{markdown.ts → lower-myst.ts} +2 -1
  69. package/src/node/assets.ts +23 -0
  70. package/src/node/authoring-actions.ts +50 -0
  71. package/src/node/authoring-clients.ts +14 -0
  72. package/src/node/document-audio.ts +22 -0
  73. package/src/node/document-history.ts +11 -0
  74. package/src/node/document-image.ts +8 -0
  75. package/src/node/document-reads.ts +10 -0
  76. package/src/node/document-voice.ts +7 -0
  77. package/src/node/ffmpeg.ts +29 -0
  78. package/src/node/file-version.ts +6 -0
  79. package/src/node/fonts.ts +22 -0
  80. package/src/node/index.ts +29 -133
  81. package/src/node/installed-packages.ts +11 -0
  82. package/src/node/lines.ts +24 -0
  83. package/src/node/locale.ts +7 -0
  84. package/src/node/network.ts +11 -0
  85. package/src/node/operator-speech.ts +91 -0
  86. package/src/node/packages.ts +74 -0
  87. package/src/node/paths.ts +7 -0
  88. package/src/node/projects.ts +7 -0
  89. package/src/node/resources.ts +86 -0
  90. package/src/node/revision.ts +6 -0
  91. package/src/node/run-directory.ts +70 -0
  92. package/src/node/transcription-adoption.ts +9 -0
  93. package/src/node/transcription.ts +10 -0
  94. package/src/node/voiceover-routes.ts +73 -0
  95. package/src/node/voiceover.ts +199 -0
  96. package/src/page-source.ts +359 -0
  97. package/src/parameters.ts +81 -0
  98. package/src/preview-edit.ts +18 -0
  99. package/src/preview-events.ts +57 -0
  100. package/src/preview-frame.ts +1220 -0
  101. package/src/props.ts +17 -0
  102. package/src/push-stream.ts +67 -0
  103. package/src/reading.ts +12 -7
  104. package/src/recording.ts +38 -0
  105. package/src/renderer-components.ts +30 -0
  106. package/src/responses.ts +49 -0
  107. package/src/run-outputs.ts +15 -0
  108. package/src/runner.py +3 -3
  109. package/src/services.ts +6 -6
  110. package/src/slides.ts +36 -0
  111. package/src/speech-words.ts +76 -0
  112. package/src/speech.ts +13 -0
  113. package/src/step-keys.ts +38 -0
  114. package/src/steps.ts +7 -0
  115. package/src/transcription-adoption.ts +105 -0
  116. package/src/uri-decoding.ts +8 -0
  117. package/src/urls.ts +13 -0
  118. package/src/views.ts +47 -0
  119. package/src/voice.ts +86 -0
@@ -0,0 +1,180 @@
1
+ import type { CanonicalSource } from '../ir.ts'
2
+ import type { DocumentEditOperation, DocumentIssue, TeachingDocument } from './document.ts'
3
+ import type { Member } from './server.ts'
4
+ import type { DocumentAnswer, DocumentChange, DocumentChangeAnswer, RevisionOrigin } from './workspace.ts'
5
+ import type { DocumentOutlineAnswer, DocumentOutlineQuery, DocumentPartAnswer, DocumentPartQuery } from './document-reads.ts'
6
+ import type { AppliedDocumentAuthoringAction, DocumentAuthoringActionInspection, DocumentAuthoringActionPlan, DocumentAuthoringActionRead, DocumentAuthoringActionReference, DocumentAuthoringActionRequest, DocumentAuthoringAction, DocumentComponentImpactAnswer, DocumentComponentImpactQuery } from './authoring-actions.ts'
7
+
8
+ export const AUTHORING_API_VERSION = 1 as const
9
+ export const DOCUMENT_OPERATION_TYPES = ['insertBlock', 'moveBlock', 'deleteBlock', 'updateText', 'updateSection', 'setStepState', 'setNarration', 'setTiming', 'replaceAsset'] as const satisfies readonly DocumentEditOperation['type'][]
10
+
11
+ export interface AuthoringCapabilities {
12
+ apiVersion: typeof AUTHORING_API_VERSION
13
+ documentSchemaVersion: number
14
+ available: boolean
15
+ reason?: 'host-unavailable' | 'permission-denied'
16
+ actions: { read: boolean, outline: boolean, readPart: boolean, plan: boolean, apply: boolean, planPageDuplication: boolean, applyPageDuplication: boolean, planPageChange: boolean, applyPageChange: boolean, planAction: boolean, applyAction: boolean }
17
+ applyUnavailableReason?: 'review-required' | 'operation-disabled'
18
+ readLimits?: { maxRequestChars: number, maxResponseChars: number, responseSerialization: 'JSON.stringify(value,null,2)', offsetUnit: 'utf16' }
19
+ pageLimits?: { maxRequestChars: number, maxResponseChars: number, responseSerialization: 'JSON.stringify(value,null,2)' }
20
+ actionLimits?: { maxRequestChars: number, maxResponseChars: number, responseSerialization: 'JSON.stringify(value,null,2)' }
21
+ semanticActions?: readonly DocumentAuthoringAction['type'][]
22
+ components?: { parameters: true, stepParameters: true, restoreStepParameters: true, useExistingDefinition: true, impactRead: true, maxChanges: number, maxPathDepth: number, independentCopy: { available: false, reason: 'implementation-closure-unavailable' } }
23
+ operations: readonly DocumentEditOperation['type'][]
24
+ replacement: boolean
25
+ fileChanges: boolean
26
+ limits?: { maxFiles: number, maxTextBytes: number, maxOperations: number, planLifetimeMs: number, maxPlans: number, maxReceipts: number }
27
+ idempotency: 'process-current-source' | 'unavailable'
28
+ review: 'required-unsupported' | 'locks-and-history' | 'locks-only' | 'unavailable'
29
+ }
30
+
31
+ /** A pure local preflight. Files include lecture.json and their predicted post-save versions. */
32
+ export interface DocumentChangePlan {
33
+ apiVersion: typeof AUTHORING_API_VERSION
34
+ planId: string
35
+ changeId: string
36
+ document: string
37
+ source: CanonicalSource
38
+ digest: string
39
+ expiresAt: string
40
+ content: TeachingDocument
41
+ issues: DocumentIssue[]
42
+ files: { path: string, base: string | null, version: string }[]
43
+ impact: { sections: string[], blocks: string[], steps: string[], narrations: string[], assets: string[], definitions: string[], sectionOrder: boolean, production: boolean, comments: boolean, metadata: boolean }
44
+ }
45
+
46
+ export interface ApplyDocumentChange { change: DocumentChange, planId: string }
47
+ export interface DocumentPageDuplicationRequest { id: string, source: CanonicalSource, sectionId: string, title: string }
48
+ export type DocumentPageAction =
49
+ | { type: 'create', title: string, after?: string | null }
50
+ | { type: 'rename', sectionId: string, title: string }
51
+ | { type: 'move', sectionId: string, before: string | null }
52
+ | { type: 'delete', sectionId: string }
53
+ export interface DocumentPageIds { sectionId: string, blockId: string, nodeId: string, stepId: string }
54
+ export interface DocumentPageDeletionImpact {
55
+ sectionId: string
56
+ retainedEmpty: boolean
57
+ blocks: string[]
58
+ steps: string[]
59
+ narrations: string[]
60
+ retainedNarrations: string[]
61
+ cues: string[]
62
+ clips: string[]
63
+ detachedComments: string[]
64
+ references: ({ type: 'cue', narrationId: string, cueId: string, blockId: string, stepIds: string[] } | { type: 'inheritance', stepId: string, inheritedStepId: string })[]
65
+ }
66
+ export interface DocumentPageChangeRequest { id: string, source: CanonicalSource, action: DocumentPageAction }
67
+ export interface DocumentPageChangePlan extends DocumentChangePlan {
68
+ action: DocumentPageAction
69
+ sectionId: string
70
+ created?: DocumentPageIds
71
+ deletion?: DocumentPageDeletionImpact
72
+ change: DocumentChange
73
+ }
74
+ export interface DocumentPageChangeReference extends DocumentPageDuplicationReference { confirmDeletion?: true }
75
+ export interface DocumentPageChangeInspection { plan: DocumentPageChangePlan, currentSource: CanonicalSource, state: 'current' | 'applied' | 'old' }
76
+ export interface AppliedDocumentPageChange extends Omit<AppliedDocumentChange, 'content' | 'issues' | 'verification' | 'commit'> {
77
+ commit?: { id: string, origin: RevisionOrigin['kind'] }
78
+ planId: string
79
+ digest: string
80
+ action: DocumentPageAction['type']
81
+ sectionId: string
82
+ retainedEmpty: boolean
83
+ changed: boolean
84
+ impact: Record<keyof DocumentChangePlan['impact'], number | boolean>
85
+ verification: AppliedDocumentChange['verification'] & { plannedVersion: string, pageStateVerified: true }
86
+ }
87
+ export interface DocumentPageDuplicationPlan extends DocumentChangePlan {
88
+ action: 'duplicatePage'
89
+ section: { from: string, to: string }
90
+ /** Only copied declared identities; assets, definitions, opaque payloads and comments retain their references. */
91
+ mapping: Record<string, string>
92
+ /** Pass this exact candidate with planId to the existing apply operation. */
93
+ change: DocumentChange
94
+ }
95
+ export interface DocumentPageDuplicationReference { planId: string, source: CanonicalSource, digest: string }
96
+ export interface DocumentPageDuplicationRead { planId: string, digest: string }
97
+ export interface DocumentPageDuplicationInspection {
98
+ plan: DocumentPageDuplicationPlan
99
+ currentSource: CanonicalSource
100
+ state: 'current' | 'applied' | 'old'
101
+ }
102
+ export interface AppliedDocumentPageDuplication extends Omit<AppliedDocumentChange, 'content' | 'issues' | 'verification'> {
103
+ planId: string
104
+ digest: string
105
+ section: DocumentPageDuplicationPlan['section']
106
+ copied: { identities: number, sections: number, blocks: number, steps: number, narrations: number }
107
+ verification: AppliedDocumentChange['verification'] & { plannedVersion: string, pagePresent: true }
108
+ }
109
+ export const DOCUMENT_PAGE_DUPLICATION_SCHEMA = {
110
+ type: 'object', additionalProperties: false, required: ['id', 'source', 'sectionId', 'title'],
111
+ properties: {
112
+ id: { type: 'string', minLength: 1, maxLength: 200 },
113
+ source: { type: 'object', additionalProperties: false, required: ['documentId', 'version', 'fingerprint'], properties: {
114
+ documentId: { type: 'string', minLength: 1, maxLength: 200 }, version: { type: 'string', minLength: 1, maxLength: 128 }, fingerprint: { type: 'string', minLength: 1, maxLength: 128 },
115
+ } },
116
+ sectionId: { type: 'string', minLength: 1, maxLength: 200 }, title: { type: 'string', minLength: 1, maxLength: 1000 },
117
+ },
118
+ } as const
119
+ export const DOCUMENT_PAGE_DUPLICATION_APPLY_SCHEMA = {
120
+ type: 'object', additionalProperties: false, required: ['planId', 'source', 'digest'], properties: {
121
+ planId: { type: 'string', minLength: 1, maxLength: 200 },
122
+ source: DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.source,
123
+ digest: { type: 'string', pattern: '^[a-f0-9]{64}$' },
124
+ },
125
+ } as const
126
+ export const DOCUMENT_PAGE_CHANGE_SCHEMA = {
127
+ type: 'object', additionalProperties: false, required: ['id', 'source', 'action'], properties: {
128
+ id: DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.id,
129
+ source: DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.source,
130
+ action: { oneOf: [
131
+ { type: 'object', additionalProperties: false, required: ['type', 'title'], properties: { type: { const: 'create' }, title: DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.title, after: { type: ['string', 'null'], minLength: 1, maxLength: 200 } } },
132
+ { type: 'object', additionalProperties: false, required: ['type', 'sectionId', 'title'], properties: { type: { const: 'rename' }, sectionId: DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.sectionId, title: DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.title } },
133
+ { type: 'object', additionalProperties: false, required: ['type', 'sectionId', 'before'], properties: { type: { const: 'move' }, sectionId: DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.sectionId, before: { type: ['string', 'null'], minLength: 1, maxLength: 200 } } },
134
+ { type: 'object', additionalProperties: false, required: ['type', 'sectionId'], properties: { type: { const: 'delete' }, sectionId: DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.sectionId } },
135
+ ] },
136
+ },
137
+ } as const
138
+ export const DOCUMENT_PAGE_CHANGE_APPLY_SCHEMA = {
139
+ ...DOCUMENT_PAGE_DUPLICATION_APPLY_SCHEMA,
140
+ properties: { ...DOCUMENT_PAGE_DUPLICATION_APPLY_SCHEMA.properties, confirmDeletion: { const: true } },
141
+ } as const
142
+ export interface AppliedDocumentChange extends DocumentChangeAnswer {
143
+ verification: { saved: true, source: CanonicalSource, compiled: 'not-requested', runtime: 'not-requested' }
144
+ }
145
+
146
+ /**
147
+ * Injected by an authoring host. Neither MCP nor compilation owns file-writing semantics.
148
+ * Each `check*` runs its apply's checks (plan reference, review, current source or a replayable receipt, the dry-run transaction) without writing,
149
+ * so a doomed request is refused before the author is asked; the apply checks everything again.
150
+ */
151
+ export interface DocumentAuthoring {
152
+ capabilities(document: string, member: Member): AuthoringCapabilities
153
+ read(document: string, member: Member): Promise<DocumentAnswer>
154
+ outline(document: string, query: DocumentOutlineQuery, member: Member): Promise<DocumentOutlineAnswer>
155
+ readPart(document: string, query: DocumentPartQuery, member: Member): Promise<DocumentPartAnswer>
156
+ plan(document: string, change: DocumentChange, member: Member): Promise<DocumentChangePlan>
157
+ planPageDuplication(document: string, request: DocumentPageDuplicationRequest, member: Member): Promise<DocumentPageDuplicationPlan>
158
+ readPageDuplicationPlan(document: string, reference: DocumentPageDuplicationRead, member: Member): Promise<DocumentPageDuplicationInspection>
159
+ checkPageDuplication(document: string, reference: DocumentPageDuplicationReference, member: Member): Promise<void>
160
+ applyPageDuplication(document: string, reference: DocumentPageDuplicationReference, member: Member): Promise<AppliedDocumentPageDuplication>
161
+ planPageChange(document: string, request: DocumentPageChangeRequest, member: Member): Promise<DocumentPageChangePlan>
162
+ readPageChangePlan(document: string, reference: DocumentPageDuplicationRead, member: Member): Promise<DocumentPageChangeInspection>
163
+ checkPageChange(document: string, reference: DocumentPageChangeReference, member: Member): Promise<void>
164
+ applyPageChange(document: string, reference: DocumentPageChangeReference, member: Member): Promise<AppliedDocumentPageChange>
165
+ planAction(document: string, request: DocumentAuthoringActionRequest, member: Member): Promise<DocumentAuthoringActionPlan>
166
+ readActionPlan(document: string, reference: DocumentAuthoringActionRead, member: Member): Promise<DocumentAuthoringActionInspection>
167
+ readActionImpact(document: string, query: DocumentComponentImpactQuery, member: Member): Promise<DocumentComponentImpactAnswer>
168
+ checkAction(document: string, reference: DocumentAuthoringActionReference, member: Member): Promise<void>
169
+ applyAction(document: string, reference: DocumentAuthoringActionReference, member: Member): Promise<AppliedDocumentAuthoringAction>
170
+ checkApply(document: string, request: ApplyDocumentChange, member: Member): Promise<void>
171
+ apply(document: string, request: ApplyDocumentChange, member: Member): Promise<AppliedDocumentChange>
172
+ }
173
+
174
+ export interface AuthoringFailure {
175
+ code: string
176
+ message: string
177
+ status: number
178
+ retryable: boolean
179
+ detail?: Record<string, unknown>
180
+ }
@@ -0,0 +1,29 @@
1
+ import type { IRDocument } from '../ir.ts'
2
+ import type { AnchorLockFile, AnchorResolverRegistry, SourceReader } from './anchors.ts'
3
+ import type { ServiceToken } from './service.ts'
4
+
5
+ export type Severity = 'error' | 'warning' | 'info'
6
+
7
+ export interface Finding {
8
+ checkId: string
9
+ severity: Severity
10
+ message: string
11
+ source?: { file: string; line?: number }
12
+ hint?: string
13
+ }
14
+
15
+ export interface CheckContext {
16
+ deckDir: string
17
+ workspaceRoot: string
18
+ headmatter: Record<string, unknown>
19
+ src: SourceReader
20
+ resolvers: AnchorResolverRegistry
21
+ lock: AnchorLockFile
22
+ ir: IRDocument
23
+ service?<T>(token: ServiceToken<T>): T | undefined
24
+ }
25
+
26
+ export interface Check {
27
+ id: string
28
+ run(ctx: CheckContext): Promise<Finding[]>
29
+ }
@@ -0,0 +1,49 @@
1
+ import type { Unsubscribe } from './plugin.ts'
2
+ import { defineService } from './service.ts'
3
+ import type { Cursor } from './steps.ts'
4
+
5
+ export type ParticipantRole = 'presenter' | 'learner'
6
+
7
+ export interface Participant {
8
+ id: string
9
+ name: string
10
+ role: ParticipantRole
11
+ }
12
+
13
+ export type SessionStatus = 'idle' | 'active' | 'ended'
14
+
15
+ export interface SessionSnapshot {
16
+ sessionId?: string
17
+ status: SessionStatus
18
+ me?: Participant
19
+ participants: Participant[]
20
+ presenterCursor?: Cursor
21
+ }
22
+
23
+ export interface SessionService {
24
+ snapshot(): SessionSnapshot
25
+ subscribe(listener: (snapshot: SessionSnapshot) => void): Unsubscribe
26
+ create(name: string): Promise<string>
27
+ join(sessionId: string, name: string): Promise<void>
28
+ end(): Promise<void>
29
+ leave(): void
30
+ archive?(): Promise<string>
31
+ readonly personal?: ClassroomStore
32
+ }
33
+
34
+ export const SESSION = defineService<SessionService>('presmium.session')
35
+
36
+ export interface StoredItem<T> {
37
+ id: string
38
+ value: T
39
+ }
40
+
41
+ export interface ClassroomStore {
42
+ list<T>(collection: string): StoredItem<T>[]
43
+ get<T>(collection: string, id: string): T | undefined
44
+ put<T>(collection: string, id: string, value: T): void
45
+ remove(collection: string, id: string): void
46
+ subscribe(collection: string, listener: () => void): Unsubscribe
47
+ }
48
+
49
+ export const CLASSROOM_STORE = defineService<ClassroomStore>('presmium.classroomStore')
@@ -0,0 +1,37 @@
1
+ import type { Finding } from './checks.ts'
2
+ import type { CanonicalSource } from '../ir.ts'
3
+
4
+ export type DiagnosticOrigin = 'compile' | 'check' | 'preview' | 'runtime' | 'server' | 'authoring'
5
+
6
+ /** A current problem. Source paths are relative to the workspace root. */
7
+ export interface Diagnostic extends Finding {
8
+ id: string
9
+ document: string
10
+ origin: DiagnosticOrigin
11
+ renderer?: 'web' | 'slides'
12
+ revision?: string
13
+ /** Saved authoring baseline when this issue was observed, never a compiled IR revision. */
14
+ authoringSource?: CanonicalSource
15
+ }
16
+
17
+ /** Replaces the previous snapshot; it is not a history of diagnostic events. */
18
+ export interface DiagnosticSnapshot {
19
+ version: number
20
+ diagnostics: Diagnostic[]
21
+ }
22
+
23
+ /** The current problems reported by one embedded preview. */
24
+ export interface PreviewDiagnostics {
25
+ document: string
26
+ renderer: 'web' | 'slides'
27
+ instance: string
28
+ revision: string
29
+ findings: Finding[]
30
+ }
31
+
32
+ /** A selected author's window can report a failure before any runtime has compiled. */
33
+ export interface AuthoringDiagnostics {
34
+ document: string
35
+ instance: string
36
+ findings: { checkId: string, severity: 'error', message: string, authoringSource?: CanonicalSource }[]
37
+ }
@@ -0,0 +1,150 @@
1
+ import type { CanonicalSource } from '../ir.ts'
2
+ import type { Finding } from './checks.ts'
3
+ import type { DocumentIssue, MediaClip, TimeRange } from './document.ts'
4
+ import { DOCUMENT_AUDIO_MAX_BYTES } from './production-assets.ts'
5
+ import type { DocumentAudioImportAnswer, DocumentAudioInspectionAnswer, ProductionAssetInspection, ProductionAudioPlanQuery } from './production-assets.ts'
6
+ import type { ProductionBinding, ProductionPlan } from './production.ts'
7
+ import type { Member } from './server.ts'
8
+ import type { DocumentChangeAnswer } from './workspace.ts'
9
+
10
+ export const DOCUMENT_AUDIO_REQUEST_MAX_BYTES = 16 << 10
11
+
12
+ export interface DocumentAudioCapabilities {
13
+ source: CanonicalSource
14
+ available: boolean
15
+ status: 'available' | 'unavailable' | 'unknown'
16
+ reason?: 'audio-unavailable' | 'services-unavailable'
17
+ verification: 'configuration-only'
18
+ findings?: Finding[]
19
+ actions: { inspect: boolean, inspectWorkspace: boolean, importWorkspace: boolean, readProductionPlan: boolean, validateClipChange: boolean, changeClip: boolean, adoptRecording: boolean }
20
+ importUnavailableReason?: 'review-required' | 'operation-disabled'
21
+ clipChangeUnavailableReason?: 'review-required' | 'operation-disabled'
22
+ clips: { upsert: boolean, remove: true, upsertRequires: 'current-production-binding' }
23
+ limits: { audioBytes: number, requestBytes: number, receipts: number, concurrent: number }
24
+ workspaceFileScope: 'same-document'
25
+ idempotency: 'process-current-source'
26
+ review: 'required-unsupported' | 'locks-and-history' | 'locks-only'
27
+ }
28
+ export interface DocumentAudioInspect { source: CanonicalSource, assetId: string }
29
+ /** Missing range adopts the whole file. A range edits only an already adopted same-asset step recording. */
30
+ export interface DocumentRecordingAdoption { id: string, source: CanonicalSource, stepId: string, assetId: string, sha256: string, sourceRange?: TimeRange }
31
+ export interface VerifiedDocumentRecordingAdoption extends DocumentChangeAnswer {
32
+ verification: { saved: true, source: CanonicalSource, stepId: string, assetId: string, sha256: string, durationMs: number, sourceRange?: TimeRange, usedDurationMs?: number, compiled: 'not-requested', runtime: 'not-requested' }
33
+ }
34
+ /** Inspect an existing real snapshot; an expected revision is optional during discovery. */
35
+ export type DocProductionPlanQuery = ProductionAudioPlanQuery
36
+ export interface DocumentWorkspaceAudioInspect { source: CanonicalSource, path: string }
37
+ export interface WorkspaceAudioInspectionAnswer {
38
+ document: string
39
+ source: CanonicalSource
40
+ file: { path: string, sha256: string, byteLength: number }
41
+ inspection: Omit<ProductionAssetInspection, 'assetId' | 'mediaId'>
42
+ }
43
+ export interface DocumentWorkspaceAudioImport {
44
+ id: string
45
+ source: CanonicalSource
46
+ /** Workspace-relative regular file permitted by the selected document's existing source scope. */
47
+ file: { path: string, sha256: string, byteLength: number }
48
+ }
49
+ export interface VerifiedDocumentAudioImport extends DocumentAudioImportAnswer {
50
+ verification: {
51
+ saved: true
52
+ source: CanonicalSource
53
+ asset: { assetId: string, path: string, sha256: string, byteLength: number }
54
+ compiled: 'not-requested'
55
+ runtime: 'not-requested'
56
+ }
57
+ }
58
+ /** A repeatable check against saved content; no plan or write intent is retained. */
59
+ export type DocumentMediaClipValidation =
60
+ | { source: CanonicalSource, binding: ProductionBinding, clip: MediaClip }
61
+ | { source: CanonicalSource, removeClipId: string }
62
+ export type DocumentMediaClipEdit = DocumentMediaClipValidation & { id: string }
63
+ export interface DocumentMediaClipValidationAnswer {
64
+ document: string
65
+ source: CanonicalSource
66
+ operation: 'add' | 'update' | 'remove'
67
+ clipId: string
68
+ before?: MediaClip
69
+ after?: MediaClip
70
+ impact: { stepIds: string[], assetIds: string[] }
71
+ issues: DocumentIssue[]
72
+ checked: { canonical: true, production: 'validated' | 'not-required', binding?: ProductionBinding }
73
+ }
74
+ export interface VerifiedDocumentMediaClipChange extends DocumentChangeAnswer {
75
+ verification: {
76
+ saved: true
77
+ source: CanonicalSource
78
+ production: 'validated-before-save' | 'not-required'
79
+ binding?: ProductionBinding
80
+ compiled: 'not-requested'
81
+ runtime: 'not-requested'
82
+ }
83
+ }
84
+ /** Workspace authoring boundary, distinct from the plugin's low-level ProductionAudioAuthoring. */
85
+ export interface DocumentAudioAuthoring {
86
+ capabilities(document: string, member: Member): Promise<DocumentAudioCapabilities>
87
+ inspect(document: string, request: DocumentAudioInspect, member: Member, signal?: AbortSignal): Promise<DocumentAudioInspectionAnswer>
88
+ inspectWorkspace(document: string, request: DocumentWorkspaceAudioInspect, member: Member, signal?: AbortSignal): Promise<WorkspaceAudioInspectionAnswer>
89
+ readProductionPlan(document: string, request: DocProductionPlanQuery, member: Member, signal?: AbortSignal): Promise<ProductionPlan>
90
+ /** Agent entry. Origin and required review come from the host, never from this request. */
91
+ /** The import's checks without its transaction (scope, file bytes and SHA-256/length, audio format, current source or a replayable receipt); `importWorkspace` checks everything again. */
92
+ checkWorkspaceImport(document: string, request: DocumentWorkspaceAudioImport, member: Member, signal?: AbortSignal): Promise<void>
93
+ importWorkspace(document: string, request: DocumentWorkspaceAudioImport, member: Member, signal?: AbortSignal): Promise<VerifiedDocumentAudioImport>
94
+ validateClipChange(document: string, request: DocumentMediaClipValidation, member: Member, signal?: AbortSignal): Promise<DocumentMediaClipValidationAnswer>
95
+ /** Agent entry; the transaction rechecks source, real media and current production timing. */
96
+ changeClip(document: string, request: DocumentMediaClipEdit, member: Member, signal?: AbortSignal): Promise<VerifiedDocumentMediaClipChange>
97
+ /** The change's checks without writing (as validateClipChange, plus review and a replayable receipt). */
98
+ checkClipChange(document: string, request: DocumentMediaClipEdit, member: Member, signal?: AbortSignal): Promise<void>
99
+ /** The adoption's checks without writing: step, asset bytes and SHA-256, range, current source or a replayable receipt. */
100
+ checkRecordingAdoption(document: string, request: DocumentRecordingAdoption, member: Member, signal?: AbortSignal): Promise<void>
101
+ adoptRecording(document: string, request: DocumentRecordingAdoption, member: Member, signal?: AbortSignal): Promise<VerifiedDocumentRecordingAdoption>
102
+ }
103
+
104
+ const source = { type: 'object', additionalProperties: false, properties: { documentId: { type: 'string', minLength: 1, maxLength: 200 }, version: { type: 'string', pattern: '^[a-f\\d]{20}$' }, fingerprint: { type: 'string', pattern: '^[a-f\\d]{16}$' } }, required: ['documentId', 'version', 'fingerprint'] } as const
105
+ export const DOCUMENT_AUDIO_INSPECTION_SCHEMA = {
106
+ type: 'object', additionalProperties: false,
107
+ properties: { source, assetId: { type: 'string', minLength: 1, maxLength: 200 } },
108
+ required: ['source', 'assetId'],
109
+ } as const
110
+ export const DOCUMENT_RECORDING_ADOPTION_SCHEMA = {
111
+ type: 'object', additionalProperties: false,
112
+ properties: { id: { type: 'string', minLength: 1, maxLength: 200 }, source, stepId: { type: 'string', minLength: 1, maxLength: 200 }, assetId: { type: 'string', minLength: 1, maxLength: 200 }, sha256: { type: 'string', pattern: '^[a-f\\d]{64}$' }, sourceRange: { type: 'object', additionalProperties: false, properties: { startMs: { type: 'number', minimum: 0 }, endMs: { type: 'number', minimum: 0 } }, required: ['startMs', 'endMs'] } },
113
+ required: ['id', 'source', 'stepId', 'assetId', 'sha256'],
114
+ } as const
115
+ export const DOCUMENT_AUDIO_WORKSPACE_IMPORT_SCHEMA = {
116
+ type: 'object', additionalProperties: false,
117
+ properties: {
118
+ id: { type: 'string', minLength: 1, maxLength: 200 }, source,
119
+ file: { type: 'object', additionalProperties: false, properties: { path: { type: 'string', minLength: 1, maxLength: 1024 }, sha256: { type: 'string', pattern: '^[a-f\\d]{64}$' }, byteLength: { type: 'integer', minimum: 1, maximum: DOCUMENT_AUDIO_MAX_BYTES } }, required: ['path', 'sha256', 'byteLength'] },
120
+ },
121
+ required: ['id', 'source', 'file'],
122
+ } as const
123
+
124
+ export const DOCUMENT_WORKSPACE_AUDIO_INSPECTION_SCHEMA = {
125
+ type: 'object', additionalProperties: false,
126
+ properties: { source, path: { type: 'string', minLength: 1, maxLength: 1024 } },
127
+ required: ['source', 'path'],
128
+ } as const
129
+
130
+ const clipId = { type: 'string', minLength: 1, maxLength: 200, pattern: '^[^\\u0000-\\u001f]+$', not: { enum: ['__proto__', 'constructor', 'prototype'] } } as const
131
+ const clip = {
132
+ type: 'object', additionalProperties: false,
133
+ properties: {
134
+ id: clipId, assetId: clipId,
135
+ sourceRange: { type: 'object', additionalProperties: false, properties: { startMs: { type: 'number', minimum: 0 }, endMs: { type: 'number', minimum: 0 } }, required: ['startMs', 'endMs'] },
136
+ attachment: { type: 'object', additionalProperties: false, properties: { stepId: clipId, offsetMs: { type: 'number', minimum: 0 } }, required: ['stepId', 'offsetMs'] },
137
+ volume: { type: 'number', minimum: 0, maximum: 1 }, muted: { type: 'boolean' },
138
+ },
139
+ required: ['id', 'assetId', 'sourceRange', 'attachment', 'volume'],
140
+ } as const
141
+ const productionBinding = { type: 'object', additionalProperties: false, properties: { source, irRevision: { type: 'string', pattern: '^[a-f\\d]{16}$' }, planId: { type: 'string', pattern: '^[a-f\\d]{32}$' } }, required: ['source', 'irRevision', 'planId'] } as const
142
+ export const DOCUMENT_PRODUCTION_PLAN_SCHEMA = { type: 'object', additionalProperties: false, properties: { source, irRevision: productionBinding.properties.irRevision }, required: ['source'] } as const
143
+ const clipRequestSchema = (edit: boolean) => ({
144
+ oneOf: [
145
+ { type: 'object', additionalProperties: false, properties: { ...edit ? { id: clipId } : {}, source, binding: productionBinding, clip }, required: [...edit ? ['id'] : [], 'source', 'binding', 'clip'] },
146
+ { type: 'object', additionalProperties: false, properties: { ...edit ? { id: clipId } : {}, source, removeClipId: clipId }, required: [...edit ? ['id'] : [], 'source', 'removeClipId'] },
147
+ ],
148
+ })
149
+ export const DOCUMENT_MEDIA_CLIP_VALIDATION_SCHEMA = /* @__PURE__ */ clipRequestSchema(false)
150
+ export const DOCUMENT_MEDIA_CLIP_CHANGE_SCHEMA = /* @__PURE__ */ clipRequestSchema(true)
@@ -0,0 +1,31 @@
1
+ import type { DocumentThread, JsonObject, RichTextNode } from './document.ts'
2
+ import type { Member } from './server.ts'
3
+
4
+ /** Canonical comment intent; author identity, dates and new entity IDs are supplied by the adapter. */
5
+ export type DocumentCommentAction =
6
+ | { type: 'comment.create', content: RichTextNode[], anchor?: DocumentThread['anchor'] | null, metadata?: JsonObject, commentMetadata?: JsonObject }
7
+ | { type: 'comment.reply', threadId: string, content: RichTextNode[], metadata?: JsonObject }
8
+ | { type: 'comment.update', threadId: string, commentId: string, content: RichTextNode[], metadata?: JsonObject }
9
+ | { type: 'comment.react', threadId: string, commentId: string, emoji: string, active: boolean }
10
+ | { type: 'comment.resolve' | 'comment.reopen', threadId: string }
11
+ | { type: 'comment.delete', threadId: string, commentId?: string }
12
+
13
+ export interface DocumentCommentContext {
14
+ member?: Member
15
+ now: string
16
+ allocate(): string
17
+ /** MCP content IDs are only input placeholders; native projections retain their own stable rich IDs. */
18
+ allocateContentIds?: boolean
19
+ }
20
+ export interface DocumentCommentEffect {
21
+ threadId: string
22
+ commentId?: string
23
+ changed: boolean
24
+ anchored: boolean
25
+ /** The target thread’s prior anchor was invalid; the discussion remains explicitly unanchored. */
26
+ anchorDetached?: boolean
27
+ threadRemoved: boolean
28
+ removedComments: number
29
+ comments: number
30
+ reactions: number
31
+ }
@@ -0,0 +1,63 @@
1
+ import type { Member } from './server.ts'
2
+ import type { HistoryRevision, RevisionsAnswer } from './workspace.ts'
3
+
4
+ export const DOCUMENT_HISTORY_MAX_REQUEST_CHARS = 2048
5
+ export const DOCUMENT_HISTORY_MAX_RESPONSE_CHARS = 7168
6
+ export const DOCUMENT_HISTORY_MAX_FILE_BYTES = 2 << 20
7
+ export const DOCUMENT_HISTORY_PAGE_SIZE = 20
8
+
9
+ /** Actual saved bytes; a malformed current JSON can still be inspected and undone. */
10
+ export interface DocumentHistorySource { version: string, fingerprint: string }
11
+ export interface DocumentHistoryBinding { source: DocumentHistorySource, snapshot: string }
12
+ export interface DocumentHistoryQuery { offset?: number, snapshot?: string }
13
+ export interface DocumentHistoryAnswer extends RevisionsAnswer {
14
+ document: string
15
+ available: boolean
16
+ unborn?: true
17
+ historyBinding?: DocumentHistoryBinding
18
+ nextOffset?: number
19
+ /** Compact summaries and file lists may be abbreviated, never commit IDs. */
20
+ truncated?: true
21
+ }
22
+ export interface DocumentHistoryRead { revision: string, path?: string, offset?: number }
23
+ export interface DocumentHistoryFile {
24
+ document: string
25
+ revision: string
26
+ path: string
27
+ encoding: 'utf8-text-fragment'
28
+ text: string
29
+ offset: number
30
+ nextOffset?: number
31
+ totalChars: number
32
+ complete: boolean
33
+ sha256: string
34
+ verification: 'historical-file-only'
35
+ }
36
+ export interface DocumentHistoryChange extends DocumentHistoryBinding { id: string, expect: string }
37
+ export interface DocumentHistoryChanged extends DocumentHistoryBinding {
38
+ document: string
39
+ ok: true
40
+ revision: Pick<HistoryRevision, 'id' | 'origin'>
41
+ verification: { saved: true, compiled: 'not-requested', runtime: 'not-requested' }
42
+ }
43
+ /** One workspace owner; HTTP and MCP do not implement reversion or write files. */
44
+ export interface DocumentHistory {
45
+ list(document: string, query: DocumentHistoryQuery, member: Member): Promise<DocumentHistoryAnswer>
46
+ read(document: string, query: DocumentHistoryRead, member: Member): Promise<DocumentHistoryFile>
47
+ /** The change's checks without writing: binding, target, document scope, next undo/redo, conflicts and uncommitted files, or a replayable receipt. */
48
+ checkChange(document: string, which: 'undo' | 'redo' | 'revert', request: DocumentHistoryChange, member: Member, origin?: 'editor' | 'agent'): Promise<void>
49
+ change(document: string, which: 'undo' | 'redo' | 'revert', request: DocumentHistoryChange, member: Member, origin?: 'editor' | 'agent'): Promise<DocumentHistoryChanged>
50
+ }
51
+ const hash = { type: 'string', pattern: '^(?:[a-f0-9]{40}|[a-f0-9]{64})$' } as const
52
+ export const DOCUMENT_HISTORY_QUERY_SCHEMA = { type: 'object', additionalProperties: false, properties: {
53
+ offset: { type: 'integer', minimum: 0 }, snapshot: hash,
54
+ } } as const
55
+ export const DOCUMENT_HISTORY_READ_SCHEMA = { type: 'object', additionalProperties: false, required: ['revision'], properties: {
56
+ revision: hash, path: { type: 'string', minLength: 1, maxLength: 500 }, offset: { type: 'integer', minimum: 0 },
57
+ } } as const
58
+ export const DOCUMENT_HISTORY_CHANGE_SCHEMA = { type: 'object', additionalProperties: false, required: ['id', 'source', 'snapshot', 'expect'], properties: {
59
+ id: { type: 'string', minLength: 1, maxLength: 200 }, snapshot: hash, expect: hash,
60
+ source: { type: 'object', additionalProperties: false, required: ['version', 'fingerprint'], properties: {
61
+ version: { type: 'string', minLength: 1, maxLength: 128 }, fingerprint: { type: 'string', minLength: 1, maxLength: 128 },
62
+ } },
63
+ } } as const
@@ -0,0 +1,32 @@
1
+ import type { CanonicalSource } from '../ir.ts'
2
+ import type { Member } from './server.ts'
3
+ import type { DocumentImageImportAnswer } from './workspace.ts'
4
+ import { DOCUMENT_IMAGE_MAX_BYTES } from './workspace.ts'
5
+
6
+ export interface DocumentWorkspaceImageInspect { source: CanonicalSource, path: string }
7
+ export interface DocumentWorkspaceImageImport { id: string, source: CanonicalSource, file: { path: string, sha256: string, byteLength: number } }
8
+ export interface WorkspaceImageInspection {
9
+ document: string, source: CanonicalSource
10
+ file: { path: string, sha256: string, byteLength: number }
11
+ image: { mediaType: string, dimensions: { width: number, height: number } }
12
+ }
13
+ export interface VerifiedDocumentImageImport extends DocumentImageImportAnswer {
14
+ verification: { saved: true, source: CanonicalSource, asset: { assetId: string, path: string, sha256: string, byteLength: number }, compiled: 'not-requested', runtime: 'not-requested' }
15
+ }
16
+ export interface DocumentImageCapabilities {
17
+ source: CanonicalSource, available: true, verification: 'configuration-only'
18
+ actions: { inspectWorkspace: true, importWorkspace: boolean }
19
+ importUnavailableReason?: 'review-required' | 'operation-disabled'
20
+ limits: { imageBytes: number, receipts: number, concurrent: number }, workspaceFileScope: 'same-document', idempotency: 'process-current-source'
21
+ review: 'required-unsupported' | 'locks-and-history' | 'locks-only'
22
+ }
23
+ export interface DocumentImageAuthoring {
24
+ capabilities(document: string, member: Member): Promise<DocumentImageCapabilities>
25
+ inspectWorkspace(document: string, request: DocumentWorkspaceImageInspect, member: Member, signal?: AbortSignal): Promise<WorkspaceImageInspection>
26
+ /** The import's checks without its transaction (scope, file bytes and SHA-256/length, current source or a replayable receipt), so a doomed import is refused before the author is asked; `importWorkspace` checks everything again. */
27
+ checkWorkspaceImport(document: string, request: DocumentWorkspaceImageImport, member: Member, signal?: AbortSignal): Promise<void>
28
+ importWorkspace(document: string, request: DocumentWorkspaceImageImport, member: Member, signal?: AbortSignal): Promise<VerifiedDocumentImageImport>
29
+ }
30
+ const source = { type: 'object', additionalProperties: false, properties: { documentId: { type: 'string', minLength: 1, maxLength: 200 }, version: { type: 'string', pattern: '^[a-f\\d]{20}$' }, fingerprint: { type: 'string', pattern: '^[a-f\\d]{16}$' } }, required: ['documentId', 'version', 'fingerprint'] } as const
31
+ export const DOCUMENT_WORKSPACE_IMAGE_INSPECTION_SCHEMA = { type: 'object', additionalProperties: false, properties: { source, path: { type: 'string', minLength: 1, maxLength: 1024 } }, required: ['source', 'path'] } as const
32
+ export const DOCUMENT_WORKSPACE_IMAGE_IMPORT_SCHEMA = { type: 'object', additionalProperties: false, properties: { id: { type: 'string', minLength: 1, maxLength: 200 }, source, file: { type: 'object', additionalProperties: false, properties: { path: { type: 'string', minLength: 1, maxLength: 1024 }, sha256: { type: 'string', pattern: '^[a-f\\d]{64}$' }, byteLength: { type: 'integer', minimum: 1, maximum: DOCUMENT_IMAGE_MAX_BYTES } }, required: ['path', 'sha256', 'byteLength'] } }, required: ['id', 'source', 'file'] } as const
@@ -0,0 +1,57 @@
1
+ import type { CanonicalSource } from '../ir.ts'
2
+ import type { JsonValue } from './document.ts'
3
+
4
+ export const DOCUMENT_READ_MAX_REQUEST_CHARS = 2048
5
+ /** Includes JSON escaping and indentation used by the MCP text result. */
6
+ export const DOCUMENT_READ_MAX_RESPONSE_CHARS = 7168
7
+ export const DOCUMENT_ENTITY_COLLECTIONS = ['sections', 'blocks', 'steps', 'narrations', 'assets', 'definitions', 'comments'] as const
8
+ export const DOCUMENT_ROOT_FIELDS = ['schemaVersion', 'id', 'metadata', 'sectionOrder', 'production'] as const
9
+ export type DocumentEntityCollection = typeof DOCUMENT_ENTITY_COLLECTIONS[number]
10
+ export type DocumentPartTarget = { collection: DocumentEntityCollection, id: string, path?: string[] } | { collection: typeof DOCUMENT_ROOT_FIELDS[number], path?: string[] }
11
+ export interface DocumentOutlineQuery { source?: CanonicalSource, collection?: DocumentEntityCollection, offset?: number }
12
+ export interface DocumentPartQuery { source: CanonicalSource, target: DocumentPartTarget, offset?: number }
13
+ export interface DocumentOutlineEntry {
14
+ id: string
15
+ type?: string
16
+ sectionId?: string
17
+ title?: { preview: string, complete: boolean, totalChars: number }
18
+ counts?: { children?: number, steps?: number, narrations?: number }
19
+ }
20
+ export interface DocumentOutlineAnswer {
21
+ source: CanonicalSource
22
+ documentSchemaVersion: number
23
+ counts: Record<DocumentEntityCollection, number>
24
+ collection: DocumentEntityCollection
25
+ present: boolean
26
+ offset: number
27
+ nextOffset?: number
28
+ total: number
29
+ complete: boolean
30
+ entries: DocumentOutlineEntry[]
31
+ }
32
+ interface DocumentPartAnswerBase {
33
+ source: CanonicalSource
34
+ target: DocumentPartTarget
35
+ /** FNV-1a 64 of JSON.stringify(selected canonical value), with no indentation. */
36
+ valueDigest: { algorithm: 'fnv1a64', serialization: 'JSON.stringify', value: string }
37
+ }
38
+ export type DocumentPartAnswer = DocumentPartAnswerBase & (
39
+ { encoding: 'value', complete: true, value: JsonValue } |
40
+ { encoding: 'json-text-fragment', offsetUnit: 'utf16', offset: number, nextOffset?: number, totalChars: number, complete: boolean, text: string }
41
+ )
42
+
43
+ const source = { type: 'object', additionalProperties: false, required: ['documentId', 'version', 'fingerprint'], properties: {
44
+ documentId: { type: 'string', minLength: 1, maxLength: 200 }, version: { type: 'string', minLength: 1, maxLength: 128 }, fingerprint: { type: 'string', minLength: 1, maxLength: 128 },
45
+ } } as const
46
+ const offset = { type: 'integer', minimum: 0, maximum: Number.MAX_SAFE_INTEGER } as const
47
+ const path = { type: 'array', maxItems: 64, items: { type: 'string', maxLength: 1024 } } as const
48
+ export const DOCUMENT_OUTLINE_QUERY_SCHEMA = {
49
+ type: 'object', additionalProperties: false, properties: { source, collection: { enum: DOCUMENT_ENTITY_COLLECTIONS }, offset },
50
+ if: { required: ['offset'], properties: { offset: { minimum: 1 } } }, then: { required: ['source'] },
51
+ } as const
52
+ export const DOCUMENT_PART_QUERY_SCHEMA = {
53
+ type: 'object', additionalProperties: false, required: ['source', 'target'], properties: { source, offset, target: { oneOf: [
54
+ { type: 'object', additionalProperties: false, required: ['collection', 'id'], properties: { collection: { enum: DOCUMENT_ENTITY_COLLECTIONS }, id: { type: 'string', minLength: 1, maxLength: 200 }, path } },
55
+ { type: 'object', additionalProperties: false, required: ['collection'], properties: { collection: { enum: DOCUMENT_ROOT_FIELDS }, path } },
56
+ ] } },
57
+ } as const