@lincyaw/kernel 1.0.0-rc.0 → 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.
- package/README.md +7 -7
- package/package.json +6 -2
- package/src/access.ts +18 -0
- package/src/anchors.ts +5 -1
- package/src/authoring.ts +56 -0
- package/src/blocks.ts +0 -8
- package/src/component-code.ts +6 -0
- package/src/contracts/agent.ts +5 -82
- package/src/contracts/anchors.ts +46 -0
- package/src/contracts/authoring-actions.ts +60 -0
- package/src/contracts/authoring-clients.ts +213 -0
- package/src/contracts/authoring.ts +180 -0
- package/src/contracts/checks.ts +29 -0
- package/src/contracts/classroom.ts +49 -0
- package/src/contracts/diagnostics.ts +37 -0
- package/src/contracts/document-audio.ts +150 -0
- package/src/contracts/document-comments.ts +31 -0
- package/src/contracts/document-history.ts +63 -0
- package/src/contracts/document-image.ts +32 -0
- package/src/contracts/document-reads.ts +57 -0
- package/src/contracts/document-voice.ts +44 -0
- package/src/contracts/document.ts +240 -0
- package/src/contracts/execution.ts +4 -5
- package/src/contracts/locale.ts +3 -0
- package/src/contracts/offline.ts +2 -0
- package/src/contracts/plugin.ts +183 -0
- package/src/contracts/presenter-panels.ts +17 -0
- package/src/contracts/preview.ts +305 -310
- package/src/contracts/production-assets.ts +61 -0
- package/src/contracts/production.ts +109 -0
- package/src/contracts/projects.ts +59 -0
- package/src/contracts/server.ts +1 -51
- package/src/contracts/speech-settings.ts +16 -0
- package/src/contracts/speech.ts +82 -70
- package/src/contracts/steps.ts +15 -0
- package/src/contracts/transcription-adoption.ts +79 -0
- package/src/contracts/transcription.ts +112 -0
- package/src/contracts/voice.ts +97 -0
- package/src/contracts/voiceover-execution.ts +68 -0
- package/src/contracts/workspace.ts +458 -0
- package/src/contracts.ts +28 -278
- package/src/diagnostics.ts +7 -0
- package/src/document-authoring-actions.ts +47 -0
- package/src/document-caption-timings.ts +64 -0
- package/src/document-colors.ts +25 -0
- package/src/document-comments.ts +118 -0
- package/src/document-components.ts +251 -0
- package/src/document-operations.ts +132 -0
- package/src/document-pages.ts +215 -0
- package/src/document-reads.ts +81 -0
- package/src/document-schema.ts +85 -0
- package/src/document-voices.ts +153 -0
- package/src/document.ts +252 -0
- package/src/durations.ts +7 -0
- package/src/errors.ts +8 -0
- package/src/execution.ts +5 -1
- package/src/fingerprint.ts +1 -1
- package/src/follow.ts +26 -30
- package/src/git.ts +1 -1
- package/src/index.ts +66 -13
- package/src/ir.ts +54 -5
- package/src/jupyter-kernel.ts +9 -0
- package/src/kernel.ts +38 -25
- package/src/keyboard.ts +43 -0
- package/src/locale.ts +13 -0
- package/src/lock.ts +4 -8
- package/src/logging.ts +1 -1
- package/src/{markdown.ts → lower-myst.ts} +2 -1
- package/src/node/assets.ts +23 -0
- package/src/node/authoring-actions.ts +50 -0
- package/src/node/authoring-clients.ts +14 -0
- package/src/node/document-audio.ts +22 -0
- package/src/node/document-history.ts +11 -0
- package/src/node/document-image.ts +8 -0
- package/src/node/document-reads.ts +10 -0
- package/src/node/document-voice.ts +7 -0
- package/src/node/ffmpeg.ts +29 -0
- package/src/node/file-version.ts +6 -0
- package/src/node/fonts.ts +22 -0
- package/src/node/index.ts +29 -133
- package/src/node/installed-packages.ts +11 -0
- package/src/node/lines.ts +24 -0
- package/src/node/locale.ts +7 -0
- package/src/node/network.ts +11 -0
- package/src/node/operator-speech.ts +91 -0
- package/src/node/packages.ts +74 -0
- package/src/node/paths.ts +7 -0
- package/src/node/projects.ts +7 -0
- package/src/node/resources.ts +86 -0
- package/src/node/revision.ts +6 -0
- package/src/node/run-directory.ts +70 -0
- package/src/node/transcription-adoption.ts +9 -0
- package/src/node/transcription.ts +10 -0
- package/src/node/voiceover-routes.ts +73 -0
- package/src/node/voiceover.ts +199 -0
- package/src/page-source.ts +359 -0
- package/src/parameters.ts +81 -0
- package/src/preview-edit.ts +18 -0
- package/src/preview-events.ts +57 -0
- package/src/preview-frame.ts +1220 -0
- package/src/props.ts +17 -0
- package/src/push-stream.ts +67 -0
- package/src/reading.ts +12 -7
- package/src/recording.ts +38 -0
- package/src/renderer-components.ts +30 -0
- package/src/responses.ts +49 -0
- package/src/run-outputs.ts +15 -0
- package/src/runner.py +3 -3
- package/src/services.ts +6 -6
- package/src/slides.ts +36 -0
- package/src/speech-words.ts +76 -0
- package/src/speech.ts +13 -0
- package/src/step-keys.ts +38 -0
- package/src/steps.ts +7 -0
- package/src/transcription-adoption.ts +105 -0
- package/src/uri-decoding.ts +8 -0
- package/src/urls.ts +13 -0
- package/src/views.ts +47 -0
- 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
|