@lincyaw/kernel 0.0.0-stage → 1.0.0-rc.1

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 (128) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +32 -2
  3. package/package.json +41 -3
  4. package/src/access.ts +18 -0
  5. package/src/anchors.ts +58 -0
  6. package/src/authoring.ts +56 -0
  7. package/src/blocks.ts +19 -0
  8. package/src/checks.ts +35 -0
  9. package/src/component-code.ts +6 -0
  10. package/src/contracts/agent.ts +76 -0
  11. package/src/contracts/anchors.ts +46 -0
  12. package/src/contracts/authoring-actions.ts +60 -0
  13. package/src/contracts/authoring-clients.ts +213 -0
  14. package/src/contracts/authoring.ts +180 -0
  15. package/src/contracts/checks.ts +29 -0
  16. package/src/contracts/classroom.ts +49 -0
  17. package/src/contracts/diagnostics.ts +37 -0
  18. package/src/contracts/document-audio.ts +150 -0
  19. package/src/contracts/document-comments.ts +31 -0
  20. package/src/contracts/document-history.ts +63 -0
  21. package/src/contracts/document-image.ts +32 -0
  22. package/src/contracts/document-reads.ts +57 -0
  23. package/src/contracts/document-voice.ts +44 -0
  24. package/src/contracts/document.ts +240 -0
  25. package/src/contracts/execution.ts +118 -0
  26. package/src/contracts/locale.ts +3 -0
  27. package/src/contracts/offline.ts +2 -0
  28. package/src/contracts/operations.ts +8 -0
  29. package/src/contracts/plugin.ts +183 -0
  30. package/src/contracts/presenter-panels.ts +17 -0
  31. package/src/contracts/preview.ts +386 -0
  32. package/src/contracts/production-assets.ts +61 -0
  33. package/src/contracts/production.ts +109 -0
  34. package/src/contracts/projects.ts +59 -0
  35. package/src/contracts/server.ts +108 -0
  36. package/src/contracts/service.ts +8 -0
  37. package/src/contracts/speech-settings.ts +16 -0
  38. package/src/contracts/speech.ts +149 -0
  39. package/src/contracts/steps.ts +15 -0
  40. package/src/contracts/transcription-adoption.ts +79 -0
  41. package/src/contracts/transcription.ts +112 -0
  42. package/src/contracts/voice.ts +97 -0
  43. package/src/contracts/voiceover-execution.ts +68 -0
  44. package/src/contracts/workspace.ts +458 -0
  45. package/src/contracts.ts +35 -0
  46. package/src/diagnostics.ts +7 -0
  47. package/src/document-authoring-actions.ts +47 -0
  48. package/src/document-caption-timings.ts +64 -0
  49. package/src/document-colors.ts +25 -0
  50. package/src/document-comments.ts +118 -0
  51. package/src/document-components.ts +251 -0
  52. package/src/document-operations.ts +132 -0
  53. package/src/document-pages.ts +215 -0
  54. package/src/document-reads.ts +81 -0
  55. package/src/document-schema.ts +85 -0
  56. package/src/document-voices.ts +153 -0
  57. package/src/document.ts +252 -0
  58. package/src/durations.ts +7 -0
  59. package/src/errors.ts +15 -0
  60. package/src/events.ts +32 -0
  61. package/src/execution.ts +25 -0
  62. package/src/fingerprint.ts +31 -0
  63. package/src/follow.ts +33 -0
  64. package/src/git.ts +12 -0
  65. package/src/html.ts +41 -0
  66. package/src/index.ts +82 -0
  67. package/src/ir.ts +367 -0
  68. package/src/jupyter-kernel.ts +9 -0
  69. package/src/kernel.ts +190 -0
  70. package/src/keyboard.ts +43 -0
  71. package/src/locale.ts +13 -0
  72. package/src/lock.ts +81 -0
  73. package/src/logging.ts +11 -0
  74. package/src/lower-myst.ts +312 -0
  75. package/src/node/assets.ts +23 -0
  76. package/src/node/authoring-actions.ts +50 -0
  77. package/src/node/authoring-clients.ts +14 -0
  78. package/src/node/document-audio.ts +22 -0
  79. package/src/node/document-history.ts +11 -0
  80. package/src/node/document-image.ts +8 -0
  81. package/src/node/document-reads.ts +10 -0
  82. package/src/node/document-voice.ts +7 -0
  83. package/src/node/ffmpeg.ts +29 -0
  84. package/src/node/file-version.ts +6 -0
  85. package/src/node/fonts.ts +22 -0
  86. package/src/node/index.ts +29 -0
  87. package/src/node/installed-packages.ts +11 -0
  88. package/src/node/lines.ts +24 -0
  89. package/src/node/locale.ts +7 -0
  90. package/src/node/network.ts +11 -0
  91. package/src/node/operator-speech.ts +91 -0
  92. package/src/node/packages.ts +74 -0
  93. package/src/node/paths.ts +7 -0
  94. package/src/node/projects.ts +7 -0
  95. package/src/node/resources.ts +86 -0
  96. package/src/node/revision.ts +6 -0
  97. package/src/node/run-directory.ts +70 -0
  98. package/src/node/strip-types.mjs +15 -0
  99. package/src/node/transcription-adoption.ts +9 -0
  100. package/src/node/transcription.ts +10 -0
  101. package/src/node/voiceover-routes.ts +73 -0
  102. package/src/node/voiceover.ts +199 -0
  103. package/src/page-source.ts +359 -0
  104. package/src/parameters.ts +81 -0
  105. package/src/preview-edit.ts +18 -0
  106. package/src/preview-events.ts +57 -0
  107. package/src/preview-frame.ts +1220 -0
  108. package/src/props.ts +17 -0
  109. package/src/published.ts +11 -0
  110. package/src/push-stream.ts +67 -0
  111. package/src/reading.ts +96 -0
  112. package/src/recording.ts +38 -0
  113. package/src/renderer-components.ts +30 -0
  114. package/src/requests.ts +28 -0
  115. package/src/responses.ts +49 -0
  116. package/src/run-outputs.ts +15 -0
  117. package/src/runner.py +454 -0
  118. package/src/services.ts +146 -0
  119. package/src/slides.ts +36 -0
  120. package/src/speech-words.ts +76 -0
  121. package/src/speech.ts +13 -0
  122. package/src/step-keys.ts +38 -0
  123. package/src/steps.ts +7 -0
  124. package/src/transcription-adoption.ts +105 -0
  125. package/src/uri-decoding.ts +8 -0
  126. package/src/urls.ts +13 -0
  127. package/src/views.ts +47 -0
  128. package/src/voice.ts +86 -0
@@ -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
@@ -0,0 +1,44 @@
1
+ import type { CanonicalSource } from '../ir.ts'
2
+ import type { Finding } from './checks.ts'
3
+ import type { Member } from './server.ts'
4
+ import type { SpeechEngineInfo } from './speech.ts'
5
+ import type { VoiceCatalogAnswer, VoiceoverCancellation } from './voice.ts'
6
+ import type { DocumentVoiceTasks, VoiceoverJobQuery, VoiceoverStart, VoicePreviewStart } from './voiceover-execution.ts'
7
+
8
+ export interface DocumentVoiceCapabilities {
9
+ document: string
10
+ source: CanonicalSource
11
+ /** Configuration discovery does not prove provider authentication or synthesis success. */
12
+ verification: 'configuration-only'
13
+ status: 'configured' | 'unavailable' | 'unknown'
14
+ reason?: 'not-configured' | 'services-unavailable'
15
+ voiceover: boolean
16
+ preview: boolean
17
+ engines: SpeechEngineInfo[]
18
+ defaultEngine?: string
19
+ actions: { status: boolean, list: true, get: true, start: boolean, cancel: true, quotePreview: boolean, preview: boolean, readPreview: true }
20
+ tasks: { idempotency: 'process', limits: { intents: number, recentJobs: number, concurrent: number, quotes: number, quoteLifetimeMs: number, previewBytes: number, previewResources: number, retainedPreviewBytes: number } }
21
+ findings?: Finding[]
22
+ }
23
+ export interface DocumentVoiceCatalogQuery { source?: CanonicalSource, engine?: string, lang?: string }
24
+ export interface DocumentVoiceCatalogAnswer extends VoiceCatalogAnswer { document: string, source: CanonicalSource, findings?: Finding[] }
25
+
26
+ /** Shared voice discovery and explicitly authorized tasks; discovery never compiles or synthesizes. */
27
+ export interface DocumentVoiceAuthoring extends DocumentVoiceTasks {
28
+ capabilities(document: string, member: Member, signal?: AbortSignal): Promise<DocumentVoiceCapabilities>
29
+ catalog(document: string, query: DocumentVoiceCatalogQuery, member: Member, signal?: AbortSignal): Promise<DocumentVoiceCatalogAnswer>
30
+ /** Checks a start or preview with the same path as the action, without synthesizing, so an agent asks only for one that would run. */
31
+ checkStart(document: string, request: VoiceoverStart, member: Member, signal?: AbortSignal): Promise<void>
32
+ checkPreview(document: string, request: VoicePreviewStart, member: Member, signal?: AbortSignal): Promise<void>
33
+ /** Undefined when there is something to cancel; otherwise the unchanged state that cancel would report. */
34
+ checkCancel(document: string, query: VoiceoverJobQuery, member: Member): Promise<VoiceoverCancellation | undefined>
35
+ }
36
+
37
+ export const DOCUMENT_VOICE_CATALOG_SCHEMA = {
38
+ type: 'object', additionalProperties: false,
39
+ properties: {
40
+ 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'] },
41
+ engine: { type: 'string', minLength: 1, maxLength: 200 },
42
+ lang: { type: 'string', minLength: 1, maxLength: 100 },
43
+ },
44
+ } as const
@@ -0,0 +1,240 @@
1
+ import type { ExecuteConfig } from './execution.ts'
2
+ import type { SpeechVoice } from './speech.ts'
3
+
4
+ export const DOCUMENT_SCHEMA_VERSION = 1 as const
5
+ export const DOCUMENT_ENTRY = 'lecture.json'
6
+ export const ASSET_TITLE_MAX_LENGTH = 200
7
+
8
+ export type JsonValue = null | boolean | number | string | JsonValue[] | JsonObject
9
+ export interface JsonObject { [key: string]: JsonValue }
10
+ export type JsonSchema = JsonObject | boolean
11
+ export type DocumentId = string
12
+ export type SectionId = string
13
+ export type BlockId = string
14
+ export type StepId = string
15
+ export type NarrationId = string
16
+ export type AssetId = string
17
+ export type DefinitionId = string
18
+ export type ClipId = string
19
+
20
+ export interface DocumentMetadata {
21
+ title: string
22
+ lang: string
23
+ description?: string
24
+ tags?: string[]
25
+ plugins?: string[]
26
+ presmium?: Record<string, JsonObject>
27
+ renderers?: Record<string, JsonObject>
28
+ execute?: ExecuteConfig
29
+ checks?: JsonObject
30
+ translations?: Record<string, string>
31
+ translationOf?: { entry: string, digest: string }
32
+ }
33
+
34
+ /** Canonical authoring data. Runtime positions, resolved assets and playback plans are derived. */
35
+ export interface TeachingDocument {
36
+ schemaVersion: typeof DOCUMENT_SCHEMA_VERSION
37
+ id: DocumentId
38
+ metadata: DocumentMetadata
39
+ sectionOrder: SectionId[]
40
+ sections: Record<SectionId, Section>
41
+ blocks: Record<BlockId, DocumentBlock>
42
+ steps: Record<StepId, Step>
43
+ narrations: Record<NarrationId, Narration>
44
+ assets: Record<AssetId, Asset>
45
+ definitions: Record<DefinitionId, Definition>
46
+ production: Production
47
+ comments?: Record<string, DocumentThread>
48
+ }
49
+
50
+ export interface Layout {
51
+ type: 'stack' | 'columns' | 'canvas' | 'scrolly'
52
+ properties?: JsonObject
53
+ renderers?: Record<string, JsonObject>
54
+ }
55
+ export interface Section {
56
+ id: SectionId
57
+ title: string
58
+ level?: 1 | 2 | 3 | 4 | 5 | 6
59
+ children: BlockId[]
60
+ stepOrder: StepId[]
61
+ layout: Layout
62
+ audience?: string[]
63
+ config?: Record<string, JsonObject>
64
+ }
65
+
66
+ export type TextAlignment = 'left' | 'center' | 'right' | 'justify'
67
+ export interface TextStyle { alignment?: TextAlignment, color?: string, background?: string }
68
+ export type TextMark =
69
+ | { type: 'bold' | 'italic' | 'underline' | 'strike' | 'code' }
70
+ | { type: 'color' | 'background', value: string }
71
+ export type InlineNode =
72
+ | { type: 'text', text: string, marks?: TextMark[] }
73
+ | { type: 'link', href: string, children: InlineNode[] }
74
+ | { type: 'math', value: string }
75
+ | { type: 'break' }
76
+ | InlineExtensionNode
77
+
78
+ /** A registered extension retains its own versioned semantics and an explicit readable fallback. */
79
+ export interface InlineExtensionNode { type: 'extension', kind: string, schemaVersion: number, data: JsonObject, fallback: string }
80
+ export interface RichTextExtensionNode { id: string, type: 'extension', kind: string, schemaVersion: number, data: JsonObject, fallback: RichTextNode[] }
81
+
82
+ export interface ParagraphNode { id: string, type: 'paragraph', children: InlineNode[], style?: TextStyle }
83
+ export interface HeadingNode { id: string, type: 'heading', level: 1 | 2 | 3 | 4 | 5 | 6, children: InlineNode[], style?: TextStyle }
84
+ export interface ListItemNode { id: string, type: 'listItem', children: RichTextNode[], checked?: boolean }
85
+ export interface ListNode { id: string, type: 'list', ordered: boolean, start?: number, children: ListItemNode[] }
86
+ export interface QuoteNode { id: string, type: 'blockquote', children: RichTextNode[] }
87
+ export interface CodeTextNode { id: string, type: 'code', language: string, value: string }
88
+ export interface MathTextNode { id: string, type: 'math', value: string }
89
+ export interface TableCellNode { id: string, type: 'tableCell', children: InlineNode[], style?: TextStyle, colspan?: number, rowspan?: number }
90
+ export interface TableRowNode { id: string, type: 'tableRow', children: TableCellNode[] }
91
+ export interface TableNode { id: string, type: 'table', children: TableRowNode[], headerRows?: number, headerColumns?: number, columnWidths?: number[] }
92
+ export type RichTextNode = ParagraphNode | HeadingNode | ListNode | QuoteNode | CodeTextNode | MathTextNode | TableNode | RichTextExtensionNode
93
+
94
+ export interface BlockScope { only?: string[], audience?: string[] }
95
+ export interface TextBlock extends BlockScope { id: BlockId, type: 'text', content: RichTextNode[], reveal?: { type: 'items', order: string[] } }
96
+ export interface ContainerProperties { title?: string, columnWidths?: number[], tabTitles?: string[] }
97
+ export interface ContainerBlock extends BlockScope { id: BlockId, type: 'container', layout: 'stack' | 'columns' | 'tabs', children: BlockId[], properties: ContainerProperties }
98
+ /** An image without an asset is an authored placeholder, not an asset with an empty location. */
99
+ export interface ImageBlock extends BlockScope { id: BlockId, type: 'image', assetId?: AssetId, alt: string, caption?: InlineNode[], width?: number, alignment?: TextAlignment }
100
+ export interface CodeReference { anchor: string, pin?: string, repo?: string, highlightRegions?: Record<StepId, string> }
101
+ export interface CodeExecution { args?: Record<string, string>, stdin?: string, timeoutMs?: number, executor?: string, when: 'compile' | 'live' | 'both', session?: string }
102
+ export interface CodeVariantSource { id: string, title: string, value?: string, origin?: Omit<CodeReference, 'highlightRegions'> }
103
+ export interface CodeBlock extends BlockScope {
104
+ id: BlockId
105
+ type: 'code'
106
+ language: string
107
+ value?: string
108
+ origin?: CodeReference
109
+ title?: string
110
+ execution?: CodeExecution
111
+ highlight?: Record<StepId, number[]>
112
+ variants?: CodeVariantSource[]
113
+ echo?: boolean
114
+ }
115
+ export interface ComponentBlock extends BlockScope { id: BlockId, type: 'component', definitionId: DefinitionId, parameters: JsonObject, children?: BlockId[] }
116
+ export type DocumentBlock = TextBlock | ContainerBlock | ImageBlock | CodeBlock | ComponentBlock
117
+
118
+ /** State is independent from ordering and production timing. Omitted blocks are invisible. */
119
+ export interface Step {
120
+ id: StepId
121
+ sectionId: SectionId
122
+ title?: string
123
+ visuals: VisualState[]
124
+ narrationIds: NarrationId[]
125
+ inherits?: StepId
126
+ }
127
+ export interface VisualState { blockId: BlockId, visible: boolean, state?: string, parameters?: JsonObject }
128
+ export type NarrationNode =
129
+ | { type: 'text', text: string }
130
+ | { type: 'pronunciation', display: string, spoken: string }
131
+ | { id: string, type: 'cue', action: string, target?: { blockId: BlockId, element?: string }, parameters?: JsonObject }
132
+ export interface Narration {
133
+ id: NarrationId
134
+ content: NarrationNode[]
135
+ /** Attribution of an explicit adoption; never a second caption or spoken-text source. */
136
+ provenance?: { type: 'transcription', transcriptionId: string, revision: string, assetId: AssetId, audioSha256: string, segmentIds: string[] }
137
+ }
138
+
139
+ export interface Asset { id: AssetId, kind: 'image' | 'audio' | 'video' | 'file', location: string, title?: string, fingerprint?: string, mediaType?: string }
140
+ export type ImplementationRef =
141
+ | { type: 'file', path: string, exportName?: string, fingerprint?: string }
142
+ | { type: 'module', specifier: string, exportName?: string }
143
+ | { type: 'plugin', plugin: string, name: string }
144
+ export interface StateDescriptor {
145
+ id: string
146
+ title?: string
147
+ parameterSchema?: JsonSchema
148
+ /** A migration must retain numerical implementation bindings explicitly, never invent semantic names. */
149
+ legacyIndex?: number
150
+ }
151
+ export interface Definition {
152
+ id: DefinitionId
153
+ kind: string
154
+ schemaVersion: number
155
+ implementation: ImplementationRef
156
+ parameterSchema: JsonSchema
157
+ states: StateDescriptor[]
158
+ data?: JsonObject
159
+ }
160
+
161
+ export type VoiceSource = { type: 'none' } | { type: 'tts', voice: SpeechVoice } | { type: 'recording', assetId: AssetId, sourceRange?: TimeRange }
162
+ export interface StepProduction {
163
+ narrationStart?: 'with-animation' | 'after-animation'
164
+ minimumHoldMs?: number
165
+ trailingPauseMs?: number
166
+ voiceSource?: VoiceSource
167
+ }
168
+ export interface ProductionDefaults extends StepProduction {}
169
+ export interface TimeRange { startMs: number, endMs: number }
170
+ /** Explicit author times; offsets partition the original ordered step narration display in UTF16. */
171
+ export interface CaptionTiming {
172
+ narrationIds: NarrationId[]
173
+ sourceFingerprint: string
174
+ playbackFingerprint: string
175
+ durationMs: number
176
+ segments: { id: string, from: number, to: number, startMs: number, endMs: number }[]
177
+ }
178
+ export interface MediaClip {
179
+ id: ClipId
180
+ assetId: AssetId
181
+ sourceRange: TimeRange
182
+ attachment: { stepId: StepId, offsetMs: number }
183
+ /** Output gain, from zero to one; muting does not replace this value. */
184
+ volume: number
185
+ muted?: boolean
186
+ }
187
+ export interface Production {
188
+ defaults: ProductionDefaults
189
+ sectionOverrides?: Record<SectionId, StepProduction>
190
+ stepOverrides: Record<StepId, StepProduction>
191
+ /** Migrated narration defaults: after page settings, before an explicit step override; shared narrations retain this source. */
192
+ narrationOverrides?: Record<NarrationId, { narrationStart: 'with-animation' | 'after-animation' }>
193
+ /** Cue placement is authored in narration; its delay is owned only here. */
194
+ cueTimings?: Record<string, { pauseMs: number }>
195
+ /** Stale mappings remain inspectable but never retime audio or replace changed narration. */
196
+ captionTimings?: Record<StepId, CaptionTiming>
197
+ mediaClips: MediaClip[]
198
+ }
199
+
200
+ /** A stable rich-text/code ID, or `page:${sectionId}` reserved for an existing section title. */
201
+ export interface DocumentTextPoint { blockId: string, offset: number }
202
+ export interface DocumentComment {
203
+ id: string
204
+ type: 'comment'
205
+ userId: string
206
+ createdAt: string
207
+ updatedAt: string
208
+ deletedAt?: string
209
+ content?: RichTextNode[]
210
+ metadata?: JsonObject
211
+ reactions: { emoji: string, createdAt: string, userIds: string[] }[]
212
+ }
213
+ export interface DocumentThread {
214
+ id: string
215
+ type: 'thread'
216
+ createdAt: string
217
+ updatedAt: string
218
+ resolved: boolean
219
+ resolvedUpdatedAt?: string
220
+ resolvedBy?: string
221
+ metadata?: JsonObject
222
+ deletedAt?: string
223
+ anchor?: { from: DocumentTextPoint, to: DocumentTextPoint }
224
+ comments: DocumentComment[]
225
+ }
226
+
227
+ export interface DocumentIssue { path: string, code: string, message: string, severity: 'error' | 'warning' }
228
+ export interface DocumentValidation { document?: TeachingDocument, issues: DocumentIssue[] }
229
+
230
+ /** A source-document transaction. Its CAS version is unrelated to a compiled snapshot ID. */
231
+ export type DocumentEditOperation =
232
+ | { type: 'insertBlock', sectionId: SectionId, parentId?: BlockId, index: number, block: DocumentBlock }
233
+ | { type: 'moveBlock', blockId: BlockId, sectionId: SectionId, parentId?: BlockId, index: number }
234
+ | { type: 'deleteBlock', blockId: BlockId }
235
+ | { type: 'updateText', blockId: BlockId, content: RichTextNode[] }
236
+ | { type: 'updateSection', sectionId: SectionId, title?: string, layout?: Layout }
237
+ | { type: 'setStepState', stepId: StepId, visuals: VisualState[], inherits?: StepId }
238
+ | { type: 'setNarration', narration: Narration, stepId?: StepId, index?: number }
239
+ | { type: 'setTiming', stepId?: StepId, sectionId?: SectionId, value: StepProduction }
240
+ | { type: 'replaceAsset', asset: Asset }