@skaile/forge-editor 6.1.1 → 6.2.0
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/CHANGELOG.md +16 -0
- package/dist/code/CodeSurface.vue.d.ts +3 -3
- package/dist/document/DocumentSurface.vue.d.ts +3 -3
- package/dist/features/composites/components/CompositeInspector.js +8 -8
- package/dist/features/composites/vue.js +1 -1
- package/dist/markdown/MarkdownSurface.vue.d.ts +9 -9
- package/dist/server/handlers/ai.js +2 -1
- package/dist/server/handlers/ai.js.map +1 -1
- package/dist/server/tools.d.ts +2 -0
- package/dist/server/tools.js.map +1 -1
- package/dist/tree/DocumentTree.js +1 -1
- package/dist/tree/use-tree-model.js +1 -1
- package/dist/vue/AdvancedEditor.vue.d.ts +6 -6
- package/dist/vue/SimpleEditor.vue.d.ts +6 -6
- package/dist/vue/components/EditorSurface.vue.d.ts +6 -6
- package/dist/vue/compose.js +1 -1
- package/dist/vue/index.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 6.2.0 - 2026-10-28
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `ToolContext.documentId` (`@skaile/forge-editor/server`): the Document open in the chat, as the client sent it (`documentId` of the chat body), so a `ToolProvider` knows which Document the user is working on. Absent for a chat without a Document; existing providers are unaffected.
|
|
8
|
+
|
|
9
|
+
## 6.1.2 - 2026-10-28
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- The optional `@skaile/forge-chat` peer range is `^0.2.3`, the tested version, instead of `^0.2.0`.
|
|
14
|
+
|
|
15
|
+
### Documented
|
|
16
|
+
|
|
17
|
+
- In a bundled host (Vite, Nuxt) a missing `@skaile/forge-chat` peer surfaces as the bundler's own resolve error for `@skaile/forge-chat`; the named missing-peer error of `@skaile/forge-editor/ai/vue/chat` appears when the module loads unbundled (Node, SSR). The entry uses a top-level `await`, so the host's build target must be ES2022 or later.
|
|
18
|
+
|
|
3
19
|
## 6.1.1 - 2026-10-28
|
|
4
20
|
|
|
5
21
|
### Fixed
|
|
@@ -11,17 +11,17 @@ type __VLS_Props = {
|
|
|
11
11
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
|
|
12
12
|
view: import("vue").ShallowRef<EditorView | undefined, EditorView | undefined>;
|
|
13
13
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
|
-
ready: (view: EditorView) => any;
|
|
15
14
|
"update:modelValue": (text: string) => any;
|
|
16
15
|
save: (text: string) => any;
|
|
16
|
+
ready: (view: EditorView) => any;
|
|
17
17
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
18
|
-
onReady?: ((view: EditorView) => any) | undefined;
|
|
19
18
|
"onUpdate:modelValue"?: ((text: string) => any) | undefined;
|
|
20
19
|
onSave?: ((text: string) => any) | undefined;
|
|
20
|
+
onReady?: ((view: EditorView) => any) | undefined;
|
|
21
21
|
}>, {
|
|
22
|
-
modelValue: string;
|
|
23
22
|
editable: boolean;
|
|
24
23
|
wordWrap: boolean;
|
|
24
|
+
modelValue: string;
|
|
25
25
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
26
26
|
declare const _default: typeof __VLS_export;
|
|
27
27
|
//#endregion
|
|
@@ -32,17 +32,17 @@ type __VLS_Slots = {} & { [K in NonNullable<typeof __VLS_13>]?: (props: typeof _
|
|
|
32
32
|
binary?: (props: typeof __VLS_26) => any;
|
|
33
33
|
};
|
|
34
34
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
35
|
-
ready: (target: DocumentSurfaceTarget) => any;
|
|
36
35
|
"update:modelValue": (text: string) => any;
|
|
37
36
|
save: (text: string) => any;
|
|
37
|
+
ready: (target: DocumentSurfaceTarget) => any;
|
|
38
38
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
39
|
-
onReady?: ((target: DocumentSurfaceTarget) => any) | undefined;
|
|
40
39
|
"onUpdate:modelValue"?: ((text: string) => any) | undefined;
|
|
41
40
|
onSave?: ((text: string) => any) | undefined;
|
|
41
|
+
onReady?: ((target: DocumentSurfaceTarget) => any) | undefined;
|
|
42
42
|
}>, {
|
|
43
|
-
modelValue: string;
|
|
44
43
|
editable: boolean;
|
|
45
44
|
wordWrap: boolean;
|
|
45
|
+
modelValue: string;
|
|
46
46
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
47
47
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
48
48
|
declare const _default: typeof __VLS_export;
|
|
@@ -4,10 +4,10 @@ import { propertyHostOf } from "../properties.js";
|
|
|
4
4
|
import { useUiKit } from "../../../vue/kit/use-ui-kit.js";
|
|
5
5
|
import { useEditorI18n } from "../../../vue/use-editor-i18n.js";
|
|
6
6
|
import { compositeFieldText, isStructuredField } from "../inspector-model.js";
|
|
7
|
-
import _sfc_main$1 from "./
|
|
8
|
-
import _sfc_main$2 from "./
|
|
9
|
-
import _sfc_main$3 from "./
|
|
10
|
-
import _sfc_main$4 from "./
|
|
7
|
+
import _sfc_main$1 from "./DerivationBadge.js";
|
|
8
|
+
import _sfc_main$2 from "./CompositeFieldEditor.js";
|
|
9
|
+
import _sfc_main$3 from "./InspectorSections.js";
|
|
10
|
+
import _sfc_main$4 from "./DocumentBadges.js";
|
|
11
11
|
import { useCompositeInspector } from "../use-composite-inspector.js";
|
|
12
12
|
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, renderList, renderSlot, toDisplayString, unref, useId, withCtx } from "vue";
|
|
13
13
|
//#region src/features/composites/components/CompositeInspector.vue
|
|
@@ -109,7 +109,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
109
109
|
class: "text-sm font-semibold text-[var(--sk-fg-highlighted)]"
|
|
110
110
|
}, toDisplayString(unref(t)("composites.inspector.document")), 9, _hoisted_4),
|
|
111
111
|
createCommentVNode(" Chips of the whole document other features add (`EditorFeature.documentBadges`), e.g. the Freigabestatus. "),
|
|
112
|
-
createVNode(_sfc_main$
|
|
112
|
+
createVNode(_sfc_main$4, { editor: __props.editor }, null, 8, ["editor"])
|
|
113
113
|
]), createElementVNode("div", _hoisted_5, [renderSlot(_ctx.$slots, "document"), createElementVNode("dl", _hoisted_6, [
|
|
114
114
|
createElementVNode("dt", _hoisted_7, toDisplayString(unref(t)("composites.inspector.composites")), 1),
|
|
115
115
|
unref(kinds).length ? (openBlock(), createElementBlock("dd", _hoisted_8, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(kinds), (kind) => {
|
|
@@ -267,7 +267,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
267
267
|
label: field.label,
|
|
268
268
|
size: "sm"
|
|
269
269
|
}, {
|
|
270
|
-
default: withCtx(() => [createCommentVNode(" Fields whose value is not text (reference, bibliography, derivation) have their own editors, as in the details card; a text input would overwrite the stored object with a string. "), unref(isStructuredField)(field) ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [__props.editor ? (openBlock(), createBlock(_sfc_main$
|
|
270
|
+
default: withCtx(() => [createCommentVNode(" Fields whose value is not text (reference, bibliography, derivation) have their own editors, as in the details card; a text input would overwrite the stored object with a string. "), unref(isStructuredField)(field) ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [__props.editor ? (openBlock(), createBlock(_sfc_main$2, {
|
|
271
271
|
key: 0,
|
|
272
272
|
editor: __props.editor,
|
|
273
273
|
row: unref(properties),
|
|
@@ -335,14 +335,14 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
335
335
|
label: unref(t)("composites.inspector.derivation"),
|
|
336
336
|
size: "sm"
|
|
337
337
|
}, {
|
|
338
|
-
default: withCtx(() => [createVNode(_sfc_main$
|
|
338
|
+
default: withCtx(() => [createVNode(_sfc_main$1, {
|
|
339
339
|
editor: __props.editor,
|
|
340
340
|
node: unref(derivation).node
|
|
341
341
|
}, null, 8, ["editor", "node"])]),
|
|
342
342
|
_: 1
|
|
343
343
|
}, 8, ["label"])) : createCommentVNode("v-if", true),
|
|
344
344
|
createCommentVNode(" Sections other features add (`EditorFeature.inspectorSections`), e.g. the Prüfstand. "),
|
|
345
|
-
__props.editor ? (openBlock(), createBlock(_sfc_main$
|
|
345
|
+
__props.editor ? (openBlock(), createBlock(_sfc_main$3, {
|
|
346
346
|
key: 3,
|
|
347
347
|
editor: __props.editor,
|
|
348
348
|
node: blockNode.value
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { embedViewOptions } from "./embed-dom.js";
|
|
2
2
|
import { typedBlockViewOptions } from "./node-views.js";
|
|
3
3
|
import { compositeFeatureCore } from "./feature.js";
|
|
4
|
-
import _sfc_main$13 from "./components/DocumentBadges.js";
|
|
5
4
|
import _sfc_main from "./components/BlockEmbedView.js";
|
|
6
5
|
import _sfc_main$1 from "./components/CiteView.js";
|
|
7
6
|
import _sfc_main$11 from "./components/CompositeView.js";
|
|
@@ -21,6 +20,7 @@ import _sfc_main$14 from "./components/EdgesSection.js";
|
|
|
21
20
|
import _sfc_main$16 from "./components/FocusChip.js";
|
|
22
21
|
import _sfc_main$18 from "./components/ReferenceView.js";
|
|
23
22
|
import _sfc_main$19 from "./components/SettingsEditingTab.js";
|
|
23
|
+
import _sfc_main$13 from "./components/DocumentBadges.js";
|
|
24
24
|
import _sfc_main$6 from "./components/CompositeInspector.js";
|
|
25
25
|
import _sfc_main$9 from "./components/CompositeTable.js";
|
|
26
26
|
import { VueNodeViewRenderer } from "@tiptap/vue-3";
|
|
@@ -41,28 +41,28 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
41
41
|
settingsOpen: import("vue").WritableComputedRef<boolean, boolean>;
|
|
42
42
|
settingsTab: import("vue").WritableComputedRef<string | undefined, string | undefined>;
|
|
43
43
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
44
|
+
"update:modelValue": (markdown: string) => any;
|
|
45
|
+
ready: (editor: Editor) => any;
|
|
46
|
+
settingsOpen: (tab?: string | undefined) => any;
|
|
44
47
|
"update:sidebarOpen": (value: boolean) => any;
|
|
45
48
|
"update:commentsOpen": (value: boolean) => any;
|
|
46
|
-
|
|
47
|
-
"update:modelValue": (markdown: string) => any;
|
|
49
|
+
frontmatter: (frontmatter: string | null) => any;
|
|
48
50
|
metadataCreate: (list: string, item: MetadataItem) => any;
|
|
49
51
|
contentError: (error: Error) => any;
|
|
50
|
-
settingsOpen: (tab?: string | undefined) => any;
|
|
51
|
-
frontmatter: (frontmatter: string | null) => any;
|
|
52
52
|
diagnostics: (diagnostics: MarkdownDiagnostic[]) => any;
|
|
53
53
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
54
|
+
"onUpdate:modelValue"?: ((markdown: string) => any) | undefined;
|
|
55
|
+
onReady?: ((editor: Editor) => any) | undefined;
|
|
56
|
+
onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
|
|
54
57
|
"onUpdate:sidebarOpen"?: ((value: boolean) => any) | undefined;
|
|
55
58
|
"onUpdate:commentsOpen"?: ((value: boolean) => any) | undefined;
|
|
56
|
-
|
|
57
|
-
"onUpdate:modelValue"?: ((markdown: string) => any) | undefined;
|
|
59
|
+
onFrontmatter?: ((frontmatter: string | null) => any) | undefined;
|
|
58
60
|
onMetadataCreate?: ((list: string, item: MetadataItem) => any) | undefined;
|
|
59
61
|
onContentError?: ((error: Error) => any) | undefined;
|
|
60
|
-
onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
|
|
61
|
-
onFrontmatter?: ((frontmatter: string | null) => any) | undefined;
|
|
62
62
|
onDiagnostics?: ((diagnostics: MarkdownDiagnostic[]) => any) | undefined;
|
|
63
63
|
}>, {
|
|
64
|
-
modelValue: string;
|
|
65
64
|
editable: boolean;
|
|
65
|
+
modelValue: string;
|
|
66
66
|
settingsUi: "internal" | "external";
|
|
67
67
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
68
68
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -247,7 +247,8 @@ async function chatPost(ctx, request) {
|
|
|
247
247
|
ports: ctx.ports,
|
|
248
248
|
user,
|
|
249
249
|
signal,
|
|
250
|
-
liveContent: (id) => ctx.collab?.liveContent(id) ?? null
|
|
250
|
+
liveContent: (id) => ctx.collab?.liveContent(id) ?? null,
|
|
251
|
+
...openDocumentId && { documentId: openDocumentId }
|
|
251
252
|
};
|
|
252
253
|
const loaded = /* @__PURE__ */ new Map();
|
|
253
254
|
const documentOf = (id) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai.js","names":[],"sources":["../../../src/server/handlers/ai.ts"],"sourcesContent":["import { createHash, randomUUID } from 'node:crypto'\nimport type { JSONContent } from '@tiptap/core'\nimport { jsonSchema, type ModelMessage, tool, type ToolSet } from 'ai'\nimport { z } from 'zod'\nimport {\n AGENT_LIMITS,\n AGENT_OP_OUTCOMES,\n AGENT_OP_TYPES,\n AGENT_TOOL,\n AI_ID_PATTERN,\n AI_TASKS,\n type AgentOp,\n type AgentOpProblem,\n agentOpTarget,\n agentRetryMessage,\n agentUnreadableProblem,\n aiBlockSchema,\n checkAgentOps,\n CHAT_LIMITS,\n type BlockLines,\n type ChatToolCall,\n completeBodySchema,\n documentChatMessages,\n generateBodySchema,\n generateProblem,\n type ModelAgentOp,\n modelRefSchema,\n readBlockLines,\n runCompletion,\n runGenerate,\n} from '../../features/ai/protocol'\nimport { blockExcerptText } from '../../core/block-snippet'\nimport { AGENT_OP_ID, type ChangeSetHunk } from '../../core/change-set'\nimport type { ExtractMethod, ExtractRequest, ExtractResponse } from '../../core/backend-types'\nimport { PROPERTY_TYPES } from '../../core/properties'\nimport { aiBlockTypesOf } from '../../features/composites/schema'\nimport { ASSET_DOCUMENT_ID, ASSET_NAME } from '../../features/media/schema'\nimport {\n runTranscriptCleanup,\n TranscriptCleanupUnreadableError,\n transcriptCleanupRequestSchema,\n} from '../../integrations/transcript-ai/protocol'\nimport { STRUCTURE_MODES } from '../../settings'\nimport { HttpError } from '../errors'\nimport { BIBLIOGRAPHY_PAGES, bibliographyText, describeImage, extractBibliography, isScanned, joinPages, MAX_OCR_PAGES, MAX_TRANSCRIBE_BYTES, ocrPages, pdfPagesText, RasterError, transcribe, type TranscriptionResult } from '../extract'\nimport { DOI_PATTERN } from '../crossref'\nimport { blockExcerptOf, blockLinesOfContent } from '../document-blocks'\nimport { openPdf } from '../pdf'\nimport { changeSetOwner } from '../../adapters/change-sets/owner'\nimport { mayEditDocument } from '../readable-documents'\nimport { loadCurrentContent } from '../stored-content'\nimport { type AiHandlerContext, authenticated, enforceRateLimit, eventStream, type HandlerRequest, type HandlerResponse, json, port, readBody } from './context'\nimport { type ToolContext, toolRegistry, type ToolRegistry } from '../tools'\n\n/** What a context without `tools` offers the model: nothing beside `edit_document`. */\nconst NO_TOOLS = toolRegistry()\n\n/** Turns of the chat that may end in read-only tool calls before the model has to answer, and how many such calls one turn may run. */\nconst MAX_TOOL_ROUNDS = 5\nconst MAX_TOOL_CALLS_PER_ROUND = 8\nconst TOOL_CALLS_LIMIT_OUTPUT = `Nicht ausgeführt: höchstens ${MAX_TOOL_CALLS_PER_ROUND} Werkzeugaufrufe pro Antwort. Wiederhole diesen Aufruf, wenn du ihn noch brauchst.`\nconst EDIT_DEFERRED = `Nicht ausgeführt: In derselben Antwort fehlten noch die Ergebnisse der Lesewerkzeuge. Rufe ${AGENT_TOOL} erneut auf, wenn die Änderung noch passt.`\n\n/** The input of a tool call as the history keeps it: always an object. */\nconst toolInput = (input: unknown): Record<string, unknown> => typeof input === 'object' && input !== null && !Array.isArray(input) ? input as Record<string, unknown> : {}\n\n/** Runs one call of a registered tool and answers the text the model reads, cut to `CHAT_LIMITS.toolOutput`. A failure is that text too, so the model can go on without the tool. */\nasync function runTool(registry: ToolRegistry, context: ToolContext, call: { toolName: string, input: unknown }): Promise<string> {\n try {\n return (await registry.providers.get(call.toolName)!.handle(context, call.input)).slice(0, CHAT_LIMITS.toolOutput)\n }\n catch (error) {\n if (context.signal.aborted) throw error\n const { statusMessage, message } = error as { statusMessage?: string, message?: string }\n return `Fehler: ${statusMessage ?? message ?? 'Unbekannter Fehler'}`.slice(0, CHAT_LIMITS.toolOutput)\n }\n}\n\nconst blockId = z.string().min(1).max(100)\nconst opId = z.string().regex(AGENT_OP_ID)\nconst markdown = z.string().min(1).max(AGENT_LIMITS.markdown)\n/** A `convert` target: a `CONVERT_TARGET_IDS` entry, `typedBlock:<kind>`, or a host block id (`aiContext.blocks` with a conversion provider). */\nconst convertTarget = z.string().min(1).max(100)\n\n/** The Document an operation or a decision belongs to when it is not the open one. */\nconst documentId = z.string().min(1).max(200)\n\nconst opSchema = z.discriminatedUnion('type', [\n z.object({ op: opId, type: z.literal('update'), documentId: documentId.optional(), id: blockId, markdown }),\n z.object({ op: opId, type: z.literal('delete'), documentId: documentId.optional(), id: blockId }),\n z.object({ op: opId, type: z.literal('add'), documentId: documentId.optional(), afterId: blockId.nullable(), markdown }),\n z.object({ op: opId, type: z.literal('convert'), documentId: documentId.optional(), id: blockId, to: convertTarget }),\n])\n\nconst label = z.string().min(1).max(100)\nconst blockTypeSchema = z.object({\n // Same characters as the `::: <Block type>` fence opener accepts.\n id: z.string().regex(AI_ID_PATTERN).max(50),\n label,\n purpose: z.string().min(1).max(500).optional(),\n fields: z.array(z.object({\n key: z.string().min(1).max(50),\n label,\n type: z.enum(PROPERTY_TYPES),\n options: z.array(z.object({ value: z.string().min(1).max(100), label })).max(50).optional(),\n })).max(20),\n})\n\n/** A read-only tool call of an earlier turn (the `tool` event the client kept): replayed to the model with its result. */\nconst toolCallSchema = z.object({\n name: z.string().min(1).max(64),\n input: z.record(z.string(), z.unknown()),\n output: z.string().max(CHAT_LIMITS.toolOutput),\n}) satisfies z.ZodType<ChatToolCall>\n\nconst messageSchema = z.discriminatedUnion('role', [\n z.object({\n role: z.literal('user'),\n content: z.string().min(1).max(CHAT_LIMITS.message),\n feedback: z.array(z.object({ op: opId, type: z.enum(AGENT_OP_TYPES), target: blockId.nullable(), outcome: z.enum(AGENT_OP_OUTCOMES), documentId: documentId.optional() })).max(500).optional(),\n }),\n z.object({\n role: z.literal('assistant'),\n content: z.string().max(CHAT_LIMITS.message),\n ops: z.array(opSchema).max(AGENT_LIMITS.ops).optional(),\n dropped: z.number().int().min(0).max(1000).optional(),\n tools: z.array(toolCallSchema).max(CHAT_LIMITS.tools).optional(),\n }).refine(m => m.content || m.ops?.length || m.dropped || m.tools?.length, 'Leere Antwort im Verlauf'),\n])\n\nexport const chatBodySchema = z.object({\n messages: z.array(messageSchema).min(1).max(CHAT_LIMITS.messages).refine(messages => messages.at(-1)!.role === 'user', 'Die letzte Nachricht muss von der Nutzerin bzw. dem Nutzer sein'),\n document: z.string().max(CHAT_LIMITS.document),\n /** The ID of the open Document: operations naming it (or no Document) are edits of the open Document, any other `documentId` aims at another Document of the workspace. */\n documentId: documentId.optional(),\n /** Block IDs edits are limited to (the selected blocks); null or missing: the whole document. */\n scope: z.array(blockId).min(1).max(AGENT_LIMITS.scope).nullish(),\n /** Number of the first new operation (`op<n>`), so IDs stay unique across the conversation. */\n nextOp: z.number().int().min(1).max(1_000_000).default(1),\n model: modelRefSchema,\n /** The editor's Block types (`blockTypesOf`), for the block model in the system prompt. Ignored when the server resolves them itself (`AiHandlerContext.blockTypes`). */\n blockTypes: z.array(blockTypeSchema).max(50),\n /** The editor's host blocks (`aiContext.blocks`), for the block model in the system prompt. */\n blocks: z.array(aiBlockSchema).max(50).default([]),\n /** Block types `convert` may turn a block into. */\n targets: z.array(convertTarget).max(100),\n /** The user's preferred structure of new content (settings `blocks.structure`). */\n structure: z.enum(STRUCTURE_MODES),\n})\n\n/**\n * The tools of one chat turn: `edit_document` (deliberately loose, so malformed calls reach `checkAgentOps` and get a precise\n * retry message) and, with `reads`, the registry's read-only tools. With `workspace` (the host keeps change sets and a mutator)\n * the operations of `edit_document` may name another Document by `documentId`.\n */\nfunction agentTools(registry: ToolRegistry, { workspace, reads }: { workspace: boolean, reads: boolean }): ToolSet {\n return {\n [AGENT_TOOL]: tool({\n description: workspace\n ? 'Schlägt Änderungen vor. Operationen am geöffneten Dokument zeigt die Nutzerin bzw. der Nutzer im Dokument zur Prüfung an; Operationen mit documentId (anderes Dokument des Arbeitsbereichs) landen in einem Änderungssatz, den die Nutzerin bzw. der Nutzer je Dokument prüft und übernimmt oder ablehnt.'\n : 'Schlägt Änderungen am Dokument vor. Jede Operation wird der Nutzerin bzw. dem Nutzer im Dokument zur Prüfung angezeigt.',\n inputSchema: jsonSchema<{ ops: unknown[] }>({\n type: 'object',\n properties: {\n ops: {\n type: 'array',\n description: 'Die Operationen in Dokumentreihenfolge.',\n items: {\n type: 'object',\n properties: {\n type: { type: 'string', enum: [...AGENT_OP_TYPES], description: 'update: Blockinhalt ersetzen; delete: Block löschen; add: Blöcke einfügen; convert: Blocktyp ändern.' },\n ...(workspace && { documentId: { type: 'string', description: 'Nur für Blöcke eines anderen Dokuments: dessen ID (zusammen mit den Block-IDs aus find_blocks oder read_block). Es muss ein Dokument sein, das die Nutzerin bzw. der Nutzer bearbeiten darf. Ohne Angabe gilt das geöffnete Dokument.' } }),\n id: { type: 'string', description: 'update/delete/convert: ID des Blocks aus dem Dokument.' },\n afterId: { type: ['string', 'null'], description: 'add (Pflicht): ID des Blocks, nach dem eingefügt wird; für den Dokumentanfang ausdrücklich null.' },\n markdown: { type: 'string', description: 'update/add: der neue Inhalt als Markdown, ohne Block-ID.' },\n to: { type: 'string', description: 'convert: der neue Blocktyp, einer der im Systemprompt erlaubten Werte (z. B. paragraph, heading2, composite:task).' },\n },\n required: ['type'],\n },\n },\n },\n required: ['ops'],\n }),\n }),\n ...(reads && registry.tools),\n }\n}\n\n/**\n * Document chat: answers the last message of `messages` about `document`, or proposes edit operations through the\n * `edit_document` tool. Operations pointing to IDs that are unknown or outside `scope`, an `add` without `afterId`, an\n * operation inside a block that another operation replaces or deletes, or converting into a type not in\n * `targets`, make the model retry (at most `AGENT_LIMITS.retries` times); what is still invalid then, or left out by a\n * corrected list, is dropped. The system prompt describes the editor's block model from `blockTypes`, `blocks` and `targets`. SSE like\n * `/api/ai/generate`: `delta` (text), `ops` (`{ ops: AgentOp[], dropped: AgentOpProblem[], retries: number }`, once,\n * before `done`), `change-set` (the stored `ChangeSet`, once, when operations aimed at other Documents), `done`, `error`.\n * Operations of the open Document (`documentId` absent or the body's) are `ops`; operations naming another Document the\n * user may edit (`mayEditDocument`) become one `ChangeSet` (`ChangeSetStore`) for the user to review per Document, when the\n * host has the `changeSets` port and a mutator; else such an operation is a `document` problem the model retries. All\n * operations share one numbering (`op<n>`).\n */\nexport async function chatPost(ctx: AiHandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await enforceRateLimit(ctx, request)\n const { messages, document, documentId: openDocumentId, scope, nextOp, model, blockTypes: clientBlockTypes, blocks, targets, structure } = await readBody(request, chatBodySchema)\n const blockTypes = ctx.blockTypes ? aiBlockTypesOf(ctx.blockTypes) : clientBlockTypes\n // Unknown preset or missing key must fail as a plain JSON error before the SSE response starts.\n const languageModel = await ctx.ai.resolveModel(model, user)\n if (!messages.at(-1)!.content.trim()) throw new HttpError(400, 'Nachricht fehlt')\n\n const lines = readBlockLines(document)\n const allowed = scope ? new Set(scope.filter(id => lines.ids.has(id))) : null\n if (allowed && !allowed.size) throw new HttpError(400, 'Die Auswahl enthält keine Blöcke des Dokuments')\n const context = { lines, allowed, targets: new Set(targets) }\n const registry = ctx.tools ?? NO_TOOLS\n // Operations may aim at other Documents only where accepting them is possible: stored change sets and the mutator that applies them.\n const changeSets = ctx.mutator ? ctx.ports.changeSets : undefined\n const workspace = changeSets !== undefined\n const editTools = agentTools(registry, { workspace, reads: false })\n const allTools = agentTools(registry, { workspace, reads: true })\n\n return eventStream(request, async (emit, signal) => {\n try {\n let conversation: ModelMessage[] = documentChatMessages(messages, document, allowed && [...allowed], { blockTypes, blocks, targets, structure }, registry.infos)\n let answered = false\n let result: { valid: ModelAgentOp[], problems: AgentOpProblem[] } | null = null\n // Problems of attempts that were retried: an operation the corrected list leaves out counts as dropped too.\n const retried: AgentOpProblem[] = []\n let retries = 0\n // Turns that ended in read-only tool calls: the server ran them and the model answers again.\n let rounds = 0\n const toolContext: ToolContext = { ports: ctx.ports, user, signal, liveContent: id => ctx.collab?.liveContent(id) ?? null }\n // The other Documents the operations name, as far as the user may edit them: loaded once per request (the model's read and its edit see the same content).\n const loaded = new Map<string, Promise<{ content: JSONContent, lines: BlockLines } | null>>()\n const documentOf = (id: string) => {\n let entry = loaded.get(id)\n if (!entry) {\n entry = (async () => {\n if (!await mayEditDocument(ctx.ports, user, id)) return null\n const content = toolContext.liveContent(id) ?? await loadCurrentContent(ctx.ports.documents, id)\n return content && { content, lines: blockLinesOfContent(content) }\n })()\n loaded.set(id, entry)\n }\n return entry\n }\n for (;;) {\n // `edit_document` calls are collected for checking; calls of the registered tools are run right after the turn.\n const calls: { toolCallId: string, input: unknown, invalid: boolean }[] = []\n const reads: { toolCallId: string, toolName: string, input: unknown }[] = []\n const retry = retries > 0\n let text = ''\n for await (const part of ctx.ai.streamChatTurn({\n model: languageModel,\n messages: conversation,\n // Retries only correct the operations: no second answer text, nothing to read.\n tools: retry || rounds >= MAX_TOOL_ROUNDS ? editTools : allTools,\n toolChoice: retry ? { type: 'tool', toolName: AGENT_TOOL } : 'auto',\n signal,\n })) {\n if (part.type === 'tool-call') {\n if (part.toolName === AGENT_TOOL) calls.push(part)\n else if (registry.providers.has(part.toolName)) reads.push(part)\n }\n else if (!retry) {\n // The text of a later turn is a new paragraph of the answer.\n if (!text && answered) emit('delta', JSON.stringify({ text: '\\n\\n' }))\n text += part.text\n answered = true\n emit('delta', JSON.stringify({ text: part.text }))\n }\n }\n\n if (reads.length && !retry && rounds < MAX_TOOL_ROUNDS) {\n rounds++\n const outputs = await Promise.all(reads.map((call, i) => i < MAX_TOOL_CALLS_PER_ROUND ? runTool(registry, toolContext, call) : TOOL_CALLS_LIMIT_OUTPUT))\n reads.forEach((call, i) => emit('tool', JSON.stringify({ name: call.toolName, input: toolInput(call.input), output: outputs[i]! } satisfies ChatToolCall)))\n conversation = [\n ...conversation,\n {\n role: 'assistant',\n content: [\n ...(text ? [{ type: 'text' as const, text }] : []),\n ...reads.map(call => ({ type: 'tool-call' as const, toolCallId: call.toolCallId, toolName: call.toolName, input: call.input ?? {} })),\n ...calls.map(call => ({ type: 'tool-call' as const, toolCallId: call.toolCallId, toolName: AGENT_TOOL, input: call.input ?? {} })),\n ],\n },\n {\n role: 'tool',\n content: [\n ...reads.map((call, i) => ({ type: 'tool-result' as const, toolCallId: call.toolCallId, toolName: call.toolName, output: { type: 'text' as const, value: outputs[i]! } })),\n // An edit proposed in the same turn was written before its context was complete: not applied.\n ...calls.map(call => ({ type: 'tool-result' as const, toolCallId: call.toolCallId, toolName: AGENT_TOOL, output: { type: 'text' as const, value: EDIT_DEFERRED } })),\n ],\n },\n ]\n continue\n }\n if (!calls.length) break\n\n const unreadable = calls.some(call => call.invalid || !Array.isArray((call.input as { ops?: unknown } | null)?.ops))\n const raw = calls.flatMap(call => (call.input as { ops?: unknown } | null)?.ops).filter(op => op !== undefined)\n const documents = new Map<string, BlockLines>()\n if (changeSets) {\n const named = new Set(raw.flatMap(op => typeof (op as { documentId?: unknown } | null)?.documentId === 'string' ? [(op as { documentId: string }).documentId.trim()] : []))\n named.delete(openDocumentId ?? '')\n // At most one Document per operation; the limit of operations bounds the loads.\n for (const id of [...named].slice(0, AGENT_LIMITS.ops)) {\n const entry = id ? await documentOf(id) : null\n if (entry) documents.set(id, entry.lines)\n }\n }\n const checked = checkAgentOps(raw, { ...context, ...(openDocumentId && { documentId: openDocumentId }), documents })\n if (unreadable) checked.problems.unshift(agentUnreadableProblem())\n result = checked\n if (!checked.problems.length || retries === AGENT_LIMITS.retries) break\n\n retried.push(...checked.problems)\n retries++\n const correction = agentRetryMessage(checked.problems, !!allowed)\n conversation = [\n ...conversation,\n {\n role: 'assistant',\n content: [\n ...(text ? [{ type: 'text' as const, text }] : []),\n ...calls.map(call => ({ type: 'tool-call' as const, toolCallId: call.toolCallId, toolName: AGENT_TOOL, input: call.input ?? {} })),\n ],\n },\n { role: 'tool', content: calls.map(call => ({ type: 'tool-result' as const, toolCallId: call.toolCallId, toolName: AGENT_TOOL, output: { type: 'text' as const, value: correction } })) },\n ]\n }\n\n if (result) {\n const all: AgentOp[] = result.valid.map((op, i) => ({ ...op, op: `op${nextOp + i}` }))\n // The open Document's operations are the editor's in-document review; the others become a change set.\n const ops = all.filter(op => op.documentId === undefined)\n const foreign = all.filter(op => op.documentId !== undefined)\n const targets = new Set(all.map(op => agentOpTarget(op)))\n const dropped = [...result.problems]\n for (const problem of retried) {\n if (problem.id !== null && !targets.has(problem.id) && !dropped.some(d => d.id === problem.id && d.type === problem.type)) dropped.push(problem)\n }\n if (!answered && !all.length) {\n emit('delta', JSON.stringify({ text: 'Ich konnte keine gültigen Änderungen vorschlagen.' }))\n }\n if (foreign.length) {\n const hunks: ChangeSetHunk[] = await Promise.all(foreign.map(async (op): Promise<ChangeSetHunk> => {\n const documentId = op.documentId!\n const target = agentOpTarget(op)\n const entry = await documentOf(documentId)\n const excerpt = target === null || !entry ? null : blockExcerptOf(entry.content, target)\n return { op: { ...op, documentId }, status: 'pending', target: excerpt && { type: excerpt.type, text: blockExcerptText(excerpt.text) } }\n }))\n emit('change-set', JSON.stringify(await changeSets!.create({ by: changeSetOwner(user), hunks })))\n }\n emit('ops', JSON.stringify({ ops, dropped, retries }))\n }\n emit('done', '{}')\n \n }\n catch (error) {\n if (!signal.aborted) {\n const { statusMessage, message } = error as { statusMessage?: string, message?: string }\n emit('error', JSON.stringify({ message: statusMessage ?? message ?? 'Unbekannter Fehler' }))\n }\n }\n })\n}\n\n// The body schemas live with the protocol (the browser-direct adapter validates with them too); the handlers keep exporting them.\nexport { completeBodySchema, generateBodySchema }\n\n/** Short inline continuation (ghost text) of the text before the caret. Plain JSON: `{ text }`, empty when nothing fits. */\nexport async function completePost(ctx: AiHandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await enforceRateLimit(ctx, request)\n const { before, model } = await readBody(request, completeBodySchema)\n const languageModel = await ctx.ai.resolveModel(model, user)\n\n // Typing on the client aborts its fetch; stop the upstream request with it.\n const abort = new AbortController()\n request.signal?.addEventListener('abort', () => abort.abort(), { once: true })\n return json({ text: await runCompletion(before, (messages, signal) => ctx.ai.streamChat({ model: languageModel, messages, signal }), abort.signal) })\n}\n\n/** Streams one text task (rewrite, translate, generate, …) as SSE: `delta` (`{ text }`), `done`, `error` (`{ message }`). */\nexport async function generatePost(ctx: AiHandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await enforceRateLimit(ctx, request)\n const { task: taskId, model, ...input } = await readBody(request, generateBodySchema)\n // Unknown preset or missing key must fail as a plain JSON error before the SSE response starts.\n const languageModel = await ctx.ai.resolveModel(model, user)\n const task = AI_TASKS[taskId]\n const problem = generateProblem(task, input)\n if (problem) throw new HttpError(400, problem)\n const users = task.needsUsers ? (await port(ctx, 'directory').listUsers()).map(u => u.username) : undefined\n\n return eventStream(request, async (emit, signal) => {\n for await (const { event, data } of runGenerate(task, input, users, (messages, streamSignal) => ctx.ai.streamChat({ model: languageModel, messages, signal: streamSignal }), signal)) {\n emit(event, JSON.stringify(data))\n }\n })\n}\n\n/**\n * The models the client may choose from (without keys) — the `models` port's `listModels` for the user, else the\n * server-defined presets — and the default preset id per model role, for the client's model profiles. Requires an\n * authenticated user through the `identity` port; not rate limited, since it calls no model.\n */\nexport async function modelsGet(ctx: AiHandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await authenticated(ctx, request)\n return json({ presets: await ctx.ai.listModels(user), roles: ctx.ai.roles })\n}\n\nconst sha256Hex = (data: string | Uint8Array): string => createHash('sha256').update(data).digest('hex')\n\n/** PDF pages read by `pdf-text` and `ocr` unless the request caps them lower. */\nconst DEFAULT_MAX_PAGES = 200\n\n/** Body schema of `extractPost`. */\nexport const extractBodySchema = z.object({\n documentId: z.string().regex(ASSET_DOCUMENT_ID),\n name: z.string().regex(ASSET_NAME).optional(),\n method: z.enum(['auto', 'transcription', 'pdf-text', 'ocr', 'description', 'bibliography']),\n model: modelRefSchema,\n bibliography: z.boolean().optional(),\n doi: z.string().regex(DOI_PATTERN).optional(),\n maxPages: z.number().int().min(1).max(500).default(DEFAULT_MAX_PAGES),\n}).refine(body => body.name !== undefined || (body.method === 'bibliography' && body.doi !== undefined), {\n message: 'name fehlt (nur eine Literaturabfrage mit DOI braucht keine Datei)',\n path: ['name'],\n}) satisfies z.ZodType<ExtractRequest>\n\n/** The method an `ExtractRequest` runs for an asset of `mimeType`: `auto` picks by type; 415 when the type does not fit the method. */\nfunction extractMethodOf(requested: ExtractMethod, mimeType: string, hasDoi: boolean): ExtractResponse['method'] {\n const audioVideo = /^(?:audio|video)\\//.test(mimeType)\n const image = mimeType.startsWith('image/')\n const pdf = mimeType === 'application/pdf'\n const fits: Record<ExtractMethod, boolean> = {\n 'auto': audioVideo || image || pdf,\n 'transcription': audioVideo,\n 'description': image,\n 'pdf-text': pdf,\n 'ocr': pdf,\n // A given DOI needs no document text, so any asset can stand for the work.\n 'bibliography': pdf || hasDoi,\n }\n if (!fits[requested]) throw new HttpError(415, `Dateityp ${mimeType} wird für diese Auswertung nicht unterstützt`)\n if (requested !== 'auto') return requested\n if (audioVideo) return 'transcription'\n return image ? 'description' : 'pdf-text'\n}\n\n/**\n * Turns a stored asset into readable content (`ExtractRequest` → `ExtractResponse`): audio/video into a transcription\n * with segment timestamps, a PDF into its text per page (OCR through the model when it has no text layer), an image\n * into a description, bibliographic data from Crossref or the model. The raw Markdown is stored as an asset of the same\n * document (`output`), with the SHA-256 of input and output. Errors: 404 unknown asset, 415 type not supported, 413\n * oversize audio, 422 unreadable PDF, 501 transcription or OCR unavailable, 502 upstream failure.\n */\nexport async function extractPost(ctx: AiHandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await enforceRateLimit(ctx, request)\n const body = await readBody(request, extractBodySchema)\n if (body.name === undefined) {\n // Only a DOI lookup comes without a file (the schema allows nothing else): no asset, no output, empty hashes.\n const { bib, warnings } = await extractBibliography({ doi: body.doi, text: '', resolveModel: () => ctx.ai.resolveModel(body.model, user), signal: request.signal })\n return json({ method: 'bibliography', markdown: '', bib, inputHash: '', outputHash: sha256Hex(''), output: null, warnings } satisfies ExtractResponse)\n }\n const assets = port(ctx, 'assets')\n const asset = await assets.loadAsset(body.documentId, body.name)\n if (!asset) throw new HttpError(404, 'Datei nicht gefunden')\n const inputHash = sha256Hex(asset.data)\n let method = extractMethodOf(body.method, asset.record.mimeType, body.doi !== undefined)\n // Closing the page aborts the client's fetch; the upstream requests stop with it.\n const { signal } = request\n const resolveModel = () => ctx.ai.resolveModel(body.model, user)\n\n const warnings: string[] = []\n let markdown = ''\n let model: string | undefined\n let segments: TranscriptionResult['segments']\n let bib: ExtractResponse['bib']\n\n if (method === 'transcription') {\n const config = ctx.ai.transcription\n if (!config?.endpoint || !config.model) throw new HttpError(501, 'Transkription nicht konfiguriert')\n if (asset.data.byteLength > MAX_TRANSCRIBE_BYTES) throw new HttpError(413, `Datei ist größer als ${MAX_TRANSCRIBE_BYTES / 1024 / 1024} MB`)\n ;({ markdown, segments } = await transcribe(config, asset, signal))\n model = config.model\n }\n else if (method === 'description') {\n markdown = await describeImage(await resolveModel(), asset.data, asset.record.mimeType, signal)\n }\n else if (asset.record.mimeType !== 'application/pdf') {\n // `bibliography` of a non-PDF asset: only reachable with a DOI.\n const found = await extractBibliography({ doi: body.doi, text: '', resolveModel, signal })\n bib = found.bib\n warnings.push(...found.warnings)\n }\n else {\n const pdf = await openPdf(asset.data)\n try {\n if (method === 'bibliography') {\n const pages = await pdfPagesText(pdf, Math.min(pdf.pageCount, BIBLIOGRAPHY_PAGES), signal)\n const found = await extractBibliography({ doi: body.doi, text: bibliographyText(pages), resolveModel, signal })\n bib = found.bib\n warnings.push(...found.warnings)\n }\n else {\n const count = Math.min(pdf.pageCount, body.maxPages)\n if (pdf.pageCount > count) warnings.push('pages-truncated')\n let pages = await pdfPagesText(pdf, count, signal)\n if (body.method === 'auto' && isScanned(pages)) method = 'ocr'\n if (method === 'ocr') {\n // One vision call per page: the OCR reads fewer pages than the text layer would.\n const ocrCount = Math.min(count, MAX_OCR_PAGES)\n try {\n pages = await ocrPages(pdf, ocrCount, resolveModel, signal)\n if (ocrCount < count) warnings.push('ocr-truncated')\n }\n catch (error) {\n if (!(error instanceof RasterError)) throw error\n // Pages cannot be rendered here: keep what the text layer has.\n if (!pages.some(Boolean)) throw new HttpError(501, 'OCR nicht verfügbar')\n warnings.push('ocr-unavailable')\n method = 'pdf-text'\n }\n }\n markdown = joinPages(pages)\n if (body.bibliography) {\n try {\n const found = await extractBibliography({ doi: body.doi, text: bibliographyText(pages), resolveModel, signal })\n bib = found.bib\n warnings.push(...found.warnings)\n }\n catch (error) {\n // The text is the result; missing bibliographic data must not lose it.\n if (signal?.aborted) throw error\n warnings.push('bibliography-failed')\n }\n }\n }\n }\n finally {\n await pdf.close()\n }\n }\n\n const outputHash = sha256Hex(markdown)\n let output: string | null = null\n if (method !== 'bibliography' && markdown) {\n const data = Buffer.from(markdown, 'utf8')\n output = `${randomUUID()}.md`\n await assets.storeAsset(body.documentId, {\n name: output,\n filename: `${asset.record.filename}.${method}.md`.slice(0, 255),\n mimeType: 'text/markdown',\n size: data.byteLength,\n createdAt: new Date().toISOString(),\n sha256: outputHash,\n data,\n })\n }\n return json({ method, markdown, ...(segments && { segments }), ...(bib && { bib }), ...(model && { model }), inputHash, outputHash, output, warnings } satisfies ExtractResponse)\n}\n\n/** Body schema of `transcriptCleanupPost`. */\nexport const transcriptCleanupBodySchema = transcriptCleanupRequestSchema\n\n/**\n * Cleans transcript segments (filler words, punctuation, casing, fragmented sentences) without touching speakers or\n * order: the model only returns a new text per segment ID. Plain JSON: `{ cleaned, failed }`.\n */\nexport async function transcriptCleanupPost(ctx: AiHandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await enforceRateLimit(ctx, request)\n const { segments, model } = await readBody(request, transcriptCleanupBodySchema)\n const languageModel = await ctx.ai.resolveModel(model, user)\n\n // Closing the menu or the page aborts the client's fetch; stop the upstream requests with it.\n const abort = new AbortController()\n request.signal?.addEventListener('abort', () => abort.abort(), { once: true })\n try {\n return json(await runTranscriptCleanup(segments, (messages, signal) => ctx.ai.streamChat({ model: languageModel, messages, signal }), abort.signal))\n }\n catch (error) {\n throw error instanceof TranscriptCleanupUnreadableError ? new HttpError(502, error.message) : error\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ai.js","names":[],"sources":["../../../src/server/handlers/ai.ts"],"sourcesContent":["import { createHash, randomUUID } from 'node:crypto'\nimport type { JSONContent } from '@tiptap/core'\nimport { jsonSchema, type ModelMessage, tool, type ToolSet } from 'ai'\nimport { z } from 'zod'\nimport {\n AGENT_LIMITS,\n AGENT_OP_OUTCOMES,\n AGENT_OP_TYPES,\n AGENT_TOOL,\n AI_ID_PATTERN,\n AI_TASKS,\n type AgentOp,\n type AgentOpProblem,\n agentOpTarget,\n agentRetryMessage,\n agentUnreadableProblem,\n aiBlockSchema,\n checkAgentOps,\n CHAT_LIMITS,\n type BlockLines,\n type ChatToolCall,\n completeBodySchema,\n documentChatMessages,\n generateBodySchema,\n generateProblem,\n type ModelAgentOp,\n modelRefSchema,\n readBlockLines,\n runCompletion,\n runGenerate,\n} from '../../features/ai/protocol'\nimport { blockExcerptText } from '../../core/block-snippet'\nimport { AGENT_OP_ID, type ChangeSetHunk } from '../../core/change-set'\nimport type { ExtractMethod, ExtractRequest, ExtractResponse } from '../../core/backend-types'\nimport { PROPERTY_TYPES } from '../../core/properties'\nimport { aiBlockTypesOf } from '../../features/composites/schema'\nimport { ASSET_DOCUMENT_ID, ASSET_NAME } from '../../features/media/schema'\nimport {\n runTranscriptCleanup,\n TranscriptCleanupUnreadableError,\n transcriptCleanupRequestSchema,\n} from '../../integrations/transcript-ai/protocol'\nimport { STRUCTURE_MODES } from '../../settings'\nimport { HttpError } from '../errors'\nimport { BIBLIOGRAPHY_PAGES, bibliographyText, describeImage, extractBibliography, isScanned, joinPages, MAX_OCR_PAGES, MAX_TRANSCRIBE_BYTES, ocrPages, pdfPagesText, RasterError, transcribe, type TranscriptionResult } from '../extract'\nimport { DOI_PATTERN } from '../crossref'\nimport { blockExcerptOf, blockLinesOfContent } from '../document-blocks'\nimport { openPdf } from '../pdf'\nimport { changeSetOwner } from '../../adapters/change-sets/owner'\nimport { mayEditDocument } from '../readable-documents'\nimport { loadCurrentContent } from '../stored-content'\nimport { type AiHandlerContext, authenticated, enforceRateLimit, eventStream, type HandlerRequest, type HandlerResponse, json, port, readBody } from './context'\nimport { type ToolContext, toolRegistry, type ToolRegistry } from '../tools'\n\n/** What a context without `tools` offers the model: nothing beside `edit_document`. */\nconst NO_TOOLS = toolRegistry()\n\n/** Turns of the chat that may end in read-only tool calls before the model has to answer, and how many such calls one turn may run. */\nconst MAX_TOOL_ROUNDS = 5\nconst MAX_TOOL_CALLS_PER_ROUND = 8\nconst TOOL_CALLS_LIMIT_OUTPUT = `Nicht ausgeführt: höchstens ${MAX_TOOL_CALLS_PER_ROUND} Werkzeugaufrufe pro Antwort. Wiederhole diesen Aufruf, wenn du ihn noch brauchst.`\nconst EDIT_DEFERRED = `Nicht ausgeführt: In derselben Antwort fehlten noch die Ergebnisse der Lesewerkzeuge. Rufe ${AGENT_TOOL} erneut auf, wenn die Änderung noch passt.`\n\n/** The input of a tool call as the history keeps it: always an object. */\nconst toolInput = (input: unknown): Record<string, unknown> => typeof input === 'object' && input !== null && !Array.isArray(input) ? input as Record<string, unknown> : {}\n\n/** Runs one call of a registered tool and answers the text the model reads, cut to `CHAT_LIMITS.toolOutput`. A failure is that text too, so the model can go on without the tool. */\nasync function runTool(registry: ToolRegistry, context: ToolContext, call: { toolName: string, input: unknown }): Promise<string> {\n try {\n return (await registry.providers.get(call.toolName)!.handle(context, call.input)).slice(0, CHAT_LIMITS.toolOutput)\n }\n catch (error) {\n if (context.signal.aborted) throw error\n const { statusMessage, message } = error as { statusMessage?: string, message?: string }\n return `Fehler: ${statusMessage ?? message ?? 'Unbekannter Fehler'}`.slice(0, CHAT_LIMITS.toolOutput)\n }\n}\n\nconst blockId = z.string().min(1).max(100)\nconst opId = z.string().regex(AGENT_OP_ID)\nconst markdown = z.string().min(1).max(AGENT_LIMITS.markdown)\n/** A `convert` target: a `CONVERT_TARGET_IDS` entry, `typedBlock:<kind>`, or a host block id (`aiContext.blocks` with a conversion provider). */\nconst convertTarget = z.string().min(1).max(100)\n\n/** The Document an operation or a decision belongs to when it is not the open one. */\nconst documentId = z.string().min(1).max(200)\n\nconst opSchema = z.discriminatedUnion('type', [\n z.object({ op: opId, type: z.literal('update'), documentId: documentId.optional(), id: blockId, markdown }),\n z.object({ op: opId, type: z.literal('delete'), documentId: documentId.optional(), id: blockId }),\n z.object({ op: opId, type: z.literal('add'), documentId: documentId.optional(), afterId: blockId.nullable(), markdown }),\n z.object({ op: opId, type: z.literal('convert'), documentId: documentId.optional(), id: blockId, to: convertTarget }),\n])\n\nconst label = z.string().min(1).max(100)\nconst blockTypeSchema = z.object({\n // Same characters as the `::: <Block type>` fence opener accepts.\n id: z.string().regex(AI_ID_PATTERN).max(50),\n label,\n purpose: z.string().min(1).max(500).optional(),\n fields: z.array(z.object({\n key: z.string().min(1).max(50),\n label,\n type: z.enum(PROPERTY_TYPES),\n options: z.array(z.object({ value: z.string().min(1).max(100), label })).max(50).optional(),\n })).max(20),\n})\n\n/** A read-only tool call of an earlier turn (the `tool` event the client kept): replayed to the model with its result. */\nconst toolCallSchema = z.object({\n name: z.string().min(1).max(64),\n input: z.record(z.string(), z.unknown()),\n output: z.string().max(CHAT_LIMITS.toolOutput),\n}) satisfies z.ZodType<ChatToolCall>\n\nconst messageSchema = z.discriminatedUnion('role', [\n z.object({\n role: z.literal('user'),\n content: z.string().min(1).max(CHAT_LIMITS.message),\n feedback: z.array(z.object({ op: opId, type: z.enum(AGENT_OP_TYPES), target: blockId.nullable(), outcome: z.enum(AGENT_OP_OUTCOMES), documentId: documentId.optional() })).max(500).optional(),\n }),\n z.object({\n role: z.literal('assistant'),\n content: z.string().max(CHAT_LIMITS.message),\n ops: z.array(opSchema).max(AGENT_LIMITS.ops).optional(),\n dropped: z.number().int().min(0).max(1000).optional(),\n tools: z.array(toolCallSchema).max(CHAT_LIMITS.tools).optional(),\n }).refine(m => m.content || m.ops?.length || m.dropped || m.tools?.length, 'Leere Antwort im Verlauf'),\n])\n\nexport const chatBodySchema = z.object({\n messages: z.array(messageSchema).min(1).max(CHAT_LIMITS.messages).refine(messages => messages.at(-1)!.role === 'user', 'Die letzte Nachricht muss von der Nutzerin bzw. dem Nutzer sein'),\n document: z.string().max(CHAT_LIMITS.document),\n /** The ID of the open Document: operations naming it (or no Document) are edits of the open Document, any other `documentId` aims at another Document of the workspace. */\n documentId: documentId.optional(),\n /** Block IDs edits are limited to (the selected blocks); null or missing: the whole document. */\n scope: z.array(blockId).min(1).max(AGENT_LIMITS.scope).nullish(),\n /** Number of the first new operation (`op<n>`), so IDs stay unique across the conversation. */\n nextOp: z.number().int().min(1).max(1_000_000).default(1),\n model: modelRefSchema,\n /** The editor's Block types (`blockTypesOf`), for the block model in the system prompt. Ignored when the server resolves them itself (`AiHandlerContext.blockTypes`). */\n blockTypes: z.array(blockTypeSchema).max(50),\n /** The editor's host blocks (`aiContext.blocks`), for the block model in the system prompt. */\n blocks: z.array(aiBlockSchema).max(50).default([]),\n /** Block types `convert` may turn a block into. */\n targets: z.array(convertTarget).max(100),\n /** The user's preferred structure of new content (settings `blocks.structure`). */\n structure: z.enum(STRUCTURE_MODES),\n})\n\n/**\n * The tools of one chat turn: `edit_document` (deliberately loose, so malformed calls reach `checkAgentOps` and get a precise\n * retry message) and, with `reads`, the registry's read-only tools. With `workspace` (the host keeps change sets and a mutator)\n * the operations of `edit_document` may name another Document by `documentId`.\n */\nfunction agentTools(registry: ToolRegistry, { workspace, reads }: { workspace: boolean, reads: boolean }): ToolSet {\n return {\n [AGENT_TOOL]: tool({\n description: workspace\n ? 'Schlägt Änderungen vor. Operationen am geöffneten Dokument zeigt die Nutzerin bzw. der Nutzer im Dokument zur Prüfung an; Operationen mit documentId (anderes Dokument des Arbeitsbereichs) landen in einem Änderungssatz, den die Nutzerin bzw. der Nutzer je Dokument prüft und übernimmt oder ablehnt.'\n : 'Schlägt Änderungen am Dokument vor. Jede Operation wird der Nutzerin bzw. dem Nutzer im Dokument zur Prüfung angezeigt.',\n inputSchema: jsonSchema<{ ops: unknown[] }>({\n type: 'object',\n properties: {\n ops: {\n type: 'array',\n description: 'Die Operationen in Dokumentreihenfolge.',\n items: {\n type: 'object',\n properties: {\n type: { type: 'string', enum: [...AGENT_OP_TYPES], description: 'update: Blockinhalt ersetzen; delete: Block löschen; add: Blöcke einfügen; convert: Blocktyp ändern.' },\n ...(workspace && { documentId: { type: 'string', description: 'Nur für Blöcke eines anderen Dokuments: dessen ID (zusammen mit den Block-IDs aus find_blocks oder read_block). Es muss ein Dokument sein, das die Nutzerin bzw. der Nutzer bearbeiten darf. Ohne Angabe gilt das geöffnete Dokument.' } }),\n id: { type: 'string', description: 'update/delete/convert: ID des Blocks aus dem Dokument.' },\n afterId: { type: ['string', 'null'], description: 'add (Pflicht): ID des Blocks, nach dem eingefügt wird; für den Dokumentanfang ausdrücklich null.' },\n markdown: { type: 'string', description: 'update/add: der neue Inhalt als Markdown, ohne Block-ID.' },\n to: { type: 'string', description: 'convert: der neue Blocktyp, einer der im Systemprompt erlaubten Werte (z. B. paragraph, heading2, composite:task).' },\n },\n required: ['type'],\n },\n },\n },\n required: ['ops'],\n }),\n }),\n ...(reads && registry.tools),\n }\n}\n\n/**\n * Document chat: answers the last message of `messages` about `document`, or proposes edit operations through the\n * `edit_document` tool. Operations pointing to IDs that are unknown or outside `scope`, an `add` without `afterId`, an\n * operation inside a block that another operation replaces or deletes, or converting into a type not in\n * `targets`, make the model retry (at most `AGENT_LIMITS.retries` times); what is still invalid then, or left out by a\n * corrected list, is dropped. The system prompt describes the editor's block model from `blockTypes`, `blocks` and `targets`. SSE like\n * `/api/ai/generate`: `delta` (text), `ops` (`{ ops: AgentOp[], dropped: AgentOpProblem[], retries: number }`, once,\n * before `done`), `change-set` (the stored `ChangeSet`, once, when operations aimed at other Documents), `done`, `error`.\n * Operations of the open Document (`documentId` absent or the body's) are `ops`; operations naming another Document the\n * user may edit (`mayEditDocument`) become one `ChangeSet` (`ChangeSetStore`) for the user to review per Document, when the\n * host has the `changeSets` port and a mutator; else such an operation is a `document` problem the model retries. All\n * operations share one numbering (`op<n>`).\n */\nexport async function chatPost(ctx: AiHandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await enforceRateLimit(ctx, request)\n const { messages, document, documentId: openDocumentId, scope, nextOp, model, blockTypes: clientBlockTypes, blocks, targets, structure } = await readBody(request, chatBodySchema)\n const blockTypes = ctx.blockTypes ? aiBlockTypesOf(ctx.blockTypes) : clientBlockTypes\n // Unknown preset or missing key must fail as a plain JSON error before the SSE response starts.\n const languageModel = await ctx.ai.resolveModel(model, user)\n if (!messages.at(-1)!.content.trim()) throw new HttpError(400, 'Nachricht fehlt')\n\n const lines = readBlockLines(document)\n const allowed = scope ? new Set(scope.filter(id => lines.ids.has(id))) : null\n if (allowed && !allowed.size) throw new HttpError(400, 'Die Auswahl enthält keine Blöcke des Dokuments')\n const context = { lines, allowed, targets: new Set(targets) }\n const registry = ctx.tools ?? NO_TOOLS\n // Operations may aim at other Documents only where accepting them is possible: stored change sets and the mutator that applies them.\n const changeSets = ctx.mutator ? ctx.ports.changeSets : undefined\n const workspace = changeSets !== undefined\n const editTools = agentTools(registry, { workspace, reads: false })\n const allTools = agentTools(registry, { workspace, reads: true })\n\n return eventStream(request, async (emit, signal) => {\n try {\n let conversation: ModelMessage[] = documentChatMessages(messages, document, allowed && [...allowed], { blockTypes, blocks, targets, structure }, registry.infos)\n let answered = false\n let result: { valid: ModelAgentOp[], problems: AgentOpProblem[] } | null = null\n // Problems of attempts that were retried: an operation the corrected list leaves out counts as dropped too.\n const retried: AgentOpProblem[] = []\n let retries = 0\n // Turns that ended in read-only tool calls: the server ran them and the model answers again.\n let rounds = 0\n const toolContext: ToolContext = { ports: ctx.ports, user, signal, liveContent: id => ctx.collab?.liveContent(id) ?? null, ...(openDocumentId && { documentId: openDocumentId }) }\n // The other Documents the operations name, as far as the user may edit them: loaded once per request (the model's read and its edit see the same content).\n const loaded = new Map<string, Promise<{ content: JSONContent, lines: BlockLines } | null>>()\n const documentOf = (id: string) => {\n let entry = loaded.get(id)\n if (!entry) {\n entry = (async () => {\n if (!await mayEditDocument(ctx.ports, user, id)) return null\n const content = toolContext.liveContent(id) ?? await loadCurrentContent(ctx.ports.documents, id)\n return content && { content, lines: blockLinesOfContent(content) }\n })()\n loaded.set(id, entry)\n }\n return entry\n }\n for (;;) {\n // `edit_document` calls are collected for checking; calls of the registered tools are run right after the turn.\n const calls: { toolCallId: string, input: unknown, invalid: boolean }[] = []\n const reads: { toolCallId: string, toolName: string, input: unknown }[] = []\n const retry = retries > 0\n let text = ''\n for await (const part of ctx.ai.streamChatTurn({\n model: languageModel,\n messages: conversation,\n // Retries only correct the operations: no second answer text, nothing to read.\n tools: retry || rounds >= MAX_TOOL_ROUNDS ? editTools : allTools,\n toolChoice: retry ? { type: 'tool', toolName: AGENT_TOOL } : 'auto',\n signal,\n })) {\n if (part.type === 'tool-call') {\n if (part.toolName === AGENT_TOOL) calls.push(part)\n else if (registry.providers.has(part.toolName)) reads.push(part)\n }\n else if (!retry) {\n // The text of a later turn is a new paragraph of the answer.\n if (!text && answered) emit('delta', JSON.stringify({ text: '\\n\\n' }))\n text += part.text\n answered = true\n emit('delta', JSON.stringify({ text: part.text }))\n }\n }\n\n if (reads.length && !retry && rounds < MAX_TOOL_ROUNDS) {\n rounds++\n const outputs = await Promise.all(reads.map((call, i) => i < MAX_TOOL_CALLS_PER_ROUND ? runTool(registry, toolContext, call) : TOOL_CALLS_LIMIT_OUTPUT))\n reads.forEach((call, i) => emit('tool', JSON.stringify({ name: call.toolName, input: toolInput(call.input), output: outputs[i]! } satisfies ChatToolCall)))\n conversation = [\n ...conversation,\n {\n role: 'assistant',\n content: [\n ...(text ? [{ type: 'text' as const, text }] : []),\n ...reads.map(call => ({ type: 'tool-call' as const, toolCallId: call.toolCallId, toolName: call.toolName, input: call.input ?? {} })),\n ...calls.map(call => ({ type: 'tool-call' as const, toolCallId: call.toolCallId, toolName: AGENT_TOOL, input: call.input ?? {} })),\n ],\n },\n {\n role: 'tool',\n content: [\n ...reads.map((call, i) => ({ type: 'tool-result' as const, toolCallId: call.toolCallId, toolName: call.toolName, output: { type: 'text' as const, value: outputs[i]! } })),\n // An edit proposed in the same turn was written before its context was complete: not applied.\n ...calls.map(call => ({ type: 'tool-result' as const, toolCallId: call.toolCallId, toolName: AGENT_TOOL, output: { type: 'text' as const, value: EDIT_DEFERRED } })),\n ],\n },\n ]\n continue\n }\n if (!calls.length) break\n\n const unreadable = calls.some(call => call.invalid || !Array.isArray((call.input as { ops?: unknown } | null)?.ops))\n const raw = calls.flatMap(call => (call.input as { ops?: unknown } | null)?.ops).filter(op => op !== undefined)\n const documents = new Map<string, BlockLines>()\n if (changeSets) {\n const named = new Set(raw.flatMap(op => typeof (op as { documentId?: unknown } | null)?.documentId === 'string' ? [(op as { documentId: string }).documentId.trim()] : []))\n named.delete(openDocumentId ?? '')\n // At most one Document per operation; the limit of operations bounds the loads.\n for (const id of [...named].slice(0, AGENT_LIMITS.ops)) {\n const entry = id ? await documentOf(id) : null\n if (entry) documents.set(id, entry.lines)\n }\n }\n const checked = checkAgentOps(raw, { ...context, ...(openDocumentId && { documentId: openDocumentId }), documents })\n if (unreadable) checked.problems.unshift(agentUnreadableProblem())\n result = checked\n if (!checked.problems.length || retries === AGENT_LIMITS.retries) break\n\n retried.push(...checked.problems)\n retries++\n const correction = agentRetryMessage(checked.problems, !!allowed)\n conversation = [\n ...conversation,\n {\n role: 'assistant',\n content: [\n ...(text ? [{ type: 'text' as const, text }] : []),\n ...calls.map(call => ({ type: 'tool-call' as const, toolCallId: call.toolCallId, toolName: AGENT_TOOL, input: call.input ?? {} })),\n ],\n },\n { role: 'tool', content: calls.map(call => ({ type: 'tool-result' as const, toolCallId: call.toolCallId, toolName: AGENT_TOOL, output: { type: 'text' as const, value: correction } })) },\n ]\n }\n\n if (result) {\n const all: AgentOp[] = result.valid.map((op, i) => ({ ...op, op: `op${nextOp + i}` }))\n // The open Document's operations are the editor's in-document review; the others become a change set.\n const ops = all.filter(op => op.documentId === undefined)\n const foreign = all.filter(op => op.documentId !== undefined)\n const targets = new Set(all.map(op => agentOpTarget(op)))\n const dropped = [...result.problems]\n for (const problem of retried) {\n if (problem.id !== null && !targets.has(problem.id) && !dropped.some(d => d.id === problem.id && d.type === problem.type)) dropped.push(problem)\n }\n if (!answered && !all.length) {\n emit('delta', JSON.stringify({ text: 'Ich konnte keine gültigen Änderungen vorschlagen.' }))\n }\n if (foreign.length) {\n const hunks: ChangeSetHunk[] = await Promise.all(foreign.map(async (op): Promise<ChangeSetHunk> => {\n const documentId = op.documentId!\n const target = agentOpTarget(op)\n const entry = await documentOf(documentId)\n const excerpt = target === null || !entry ? null : blockExcerptOf(entry.content, target)\n return { op: { ...op, documentId }, status: 'pending', target: excerpt && { type: excerpt.type, text: blockExcerptText(excerpt.text) } }\n }))\n emit('change-set', JSON.stringify(await changeSets!.create({ by: changeSetOwner(user), hunks })))\n }\n emit('ops', JSON.stringify({ ops, dropped, retries }))\n }\n emit('done', '{}')\n \n }\n catch (error) {\n if (!signal.aborted) {\n const { statusMessage, message } = error as { statusMessage?: string, message?: string }\n emit('error', JSON.stringify({ message: statusMessage ?? message ?? 'Unbekannter Fehler' }))\n }\n }\n })\n}\n\n// The body schemas live with the protocol (the browser-direct adapter validates with them too); the handlers keep exporting them.\nexport { completeBodySchema, generateBodySchema }\n\n/** Short inline continuation (ghost text) of the text before the caret. Plain JSON: `{ text }`, empty when nothing fits. */\nexport async function completePost(ctx: AiHandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await enforceRateLimit(ctx, request)\n const { before, model } = await readBody(request, completeBodySchema)\n const languageModel = await ctx.ai.resolveModel(model, user)\n\n // Typing on the client aborts its fetch; stop the upstream request with it.\n const abort = new AbortController()\n request.signal?.addEventListener('abort', () => abort.abort(), { once: true })\n return json({ text: await runCompletion(before, (messages, signal) => ctx.ai.streamChat({ model: languageModel, messages, signal }), abort.signal) })\n}\n\n/** Streams one text task (rewrite, translate, generate, …) as SSE: `delta` (`{ text }`), `done`, `error` (`{ message }`). */\nexport async function generatePost(ctx: AiHandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await enforceRateLimit(ctx, request)\n const { task: taskId, model, ...input } = await readBody(request, generateBodySchema)\n // Unknown preset or missing key must fail as a plain JSON error before the SSE response starts.\n const languageModel = await ctx.ai.resolveModel(model, user)\n const task = AI_TASKS[taskId]\n const problem = generateProblem(task, input)\n if (problem) throw new HttpError(400, problem)\n const users = task.needsUsers ? (await port(ctx, 'directory').listUsers()).map(u => u.username) : undefined\n\n return eventStream(request, async (emit, signal) => {\n for await (const { event, data } of runGenerate(task, input, users, (messages, streamSignal) => ctx.ai.streamChat({ model: languageModel, messages, signal: streamSignal }), signal)) {\n emit(event, JSON.stringify(data))\n }\n })\n}\n\n/**\n * The models the client may choose from (without keys) — the `models` port's `listModels` for the user, else the\n * server-defined presets — and the default preset id per model role, for the client's model profiles. Requires an\n * authenticated user through the `identity` port; not rate limited, since it calls no model.\n */\nexport async function modelsGet(ctx: AiHandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await authenticated(ctx, request)\n return json({ presets: await ctx.ai.listModels(user), roles: ctx.ai.roles })\n}\n\nconst sha256Hex = (data: string | Uint8Array): string => createHash('sha256').update(data).digest('hex')\n\n/** PDF pages read by `pdf-text` and `ocr` unless the request caps them lower. */\nconst DEFAULT_MAX_PAGES = 200\n\n/** Body schema of `extractPost`. */\nexport const extractBodySchema = z.object({\n documentId: z.string().regex(ASSET_DOCUMENT_ID),\n name: z.string().regex(ASSET_NAME).optional(),\n method: z.enum(['auto', 'transcription', 'pdf-text', 'ocr', 'description', 'bibliography']),\n model: modelRefSchema,\n bibliography: z.boolean().optional(),\n doi: z.string().regex(DOI_PATTERN).optional(),\n maxPages: z.number().int().min(1).max(500).default(DEFAULT_MAX_PAGES),\n}).refine(body => body.name !== undefined || (body.method === 'bibliography' && body.doi !== undefined), {\n message: 'name fehlt (nur eine Literaturabfrage mit DOI braucht keine Datei)',\n path: ['name'],\n}) satisfies z.ZodType<ExtractRequest>\n\n/** The method an `ExtractRequest` runs for an asset of `mimeType`: `auto` picks by type; 415 when the type does not fit the method. */\nfunction extractMethodOf(requested: ExtractMethod, mimeType: string, hasDoi: boolean): ExtractResponse['method'] {\n const audioVideo = /^(?:audio|video)\\//.test(mimeType)\n const image = mimeType.startsWith('image/')\n const pdf = mimeType === 'application/pdf'\n const fits: Record<ExtractMethod, boolean> = {\n 'auto': audioVideo || image || pdf,\n 'transcription': audioVideo,\n 'description': image,\n 'pdf-text': pdf,\n 'ocr': pdf,\n // A given DOI needs no document text, so any asset can stand for the work.\n 'bibliography': pdf || hasDoi,\n }\n if (!fits[requested]) throw new HttpError(415, `Dateityp ${mimeType} wird für diese Auswertung nicht unterstützt`)\n if (requested !== 'auto') return requested\n if (audioVideo) return 'transcription'\n return image ? 'description' : 'pdf-text'\n}\n\n/**\n * Turns a stored asset into readable content (`ExtractRequest` → `ExtractResponse`): audio/video into a transcription\n * with segment timestamps, a PDF into its text per page (OCR through the model when it has no text layer), an image\n * into a description, bibliographic data from Crossref or the model. The raw Markdown is stored as an asset of the same\n * document (`output`), with the SHA-256 of input and output. Errors: 404 unknown asset, 415 type not supported, 413\n * oversize audio, 422 unreadable PDF, 501 transcription or OCR unavailable, 502 upstream failure.\n */\nexport async function extractPost(ctx: AiHandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await enforceRateLimit(ctx, request)\n const body = await readBody(request, extractBodySchema)\n if (body.name === undefined) {\n // Only a DOI lookup comes without a file (the schema allows nothing else): no asset, no output, empty hashes.\n const { bib, warnings } = await extractBibliography({ doi: body.doi, text: '', resolveModel: () => ctx.ai.resolveModel(body.model, user), signal: request.signal })\n return json({ method: 'bibliography', markdown: '', bib, inputHash: '', outputHash: sha256Hex(''), output: null, warnings } satisfies ExtractResponse)\n }\n const assets = port(ctx, 'assets')\n const asset = await assets.loadAsset(body.documentId, body.name)\n if (!asset) throw new HttpError(404, 'Datei nicht gefunden')\n const inputHash = sha256Hex(asset.data)\n let method = extractMethodOf(body.method, asset.record.mimeType, body.doi !== undefined)\n // Closing the page aborts the client's fetch; the upstream requests stop with it.\n const { signal } = request\n const resolveModel = () => ctx.ai.resolveModel(body.model, user)\n\n const warnings: string[] = []\n let markdown = ''\n let model: string | undefined\n let segments: TranscriptionResult['segments']\n let bib: ExtractResponse['bib']\n\n if (method === 'transcription') {\n const config = ctx.ai.transcription\n if (!config?.endpoint || !config.model) throw new HttpError(501, 'Transkription nicht konfiguriert')\n if (asset.data.byteLength > MAX_TRANSCRIBE_BYTES) throw new HttpError(413, `Datei ist größer als ${MAX_TRANSCRIBE_BYTES / 1024 / 1024} MB`)\n ;({ markdown, segments } = await transcribe(config, asset, signal))\n model = config.model\n }\n else if (method === 'description') {\n markdown = await describeImage(await resolveModel(), asset.data, asset.record.mimeType, signal)\n }\n else if (asset.record.mimeType !== 'application/pdf') {\n // `bibliography` of a non-PDF asset: only reachable with a DOI.\n const found = await extractBibliography({ doi: body.doi, text: '', resolveModel, signal })\n bib = found.bib\n warnings.push(...found.warnings)\n }\n else {\n const pdf = await openPdf(asset.data)\n try {\n if (method === 'bibliography') {\n const pages = await pdfPagesText(pdf, Math.min(pdf.pageCount, BIBLIOGRAPHY_PAGES), signal)\n const found = await extractBibliography({ doi: body.doi, text: bibliographyText(pages), resolveModel, signal })\n bib = found.bib\n warnings.push(...found.warnings)\n }\n else {\n const count = Math.min(pdf.pageCount, body.maxPages)\n if (pdf.pageCount > count) warnings.push('pages-truncated')\n let pages = await pdfPagesText(pdf, count, signal)\n if (body.method === 'auto' && isScanned(pages)) method = 'ocr'\n if (method === 'ocr') {\n // One vision call per page: the OCR reads fewer pages than the text layer would.\n const ocrCount = Math.min(count, MAX_OCR_PAGES)\n try {\n pages = await ocrPages(pdf, ocrCount, resolveModel, signal)\n if (ocrCount < count) warnings.push('ocr-truncated')\n }\n catch (error) {\n if (!(error instanceof RasterError)) throw error\n // Pages cannot be rendered here: keep what the text layer has.\n if (!pages.some(Boolean)) throw new HttpError(501, 'OCR nicht verfügbar')\n warnings.push('ocr-unavailable')\n method = 'pdf-text'\n }\n }\n markdown = joinPages(pages)\n if (body.bibliography) {\n try {\n const found = await extractBibliography({ doi: body.doi, text: bibliographyText(pages), resolveModel, signal })\n bib = found.bib\n warnings.push(...found.warnings)\n }\n catch (error) {\n // The text is the result; missing bibliographic data must not lose it.\n if (signal?.aborted) throw error\n warnings.push('bibliography-failed')\n }\n }\n }\n }\n finally {\n await pdf.close()\n }\n }\n\n const outputHash = sha256Hex(markdown)\n let output: string | null = null\n if (method !== 'bibliography' && markdown) {\n const data = Buffer.from(markdown, 'utf8')\n output = `${randomUUID()}.md`\n await assets.storeAsset(body.documentId, {\n name: output,\n filename: `${asset.record.filename}.${method}.md`.slice(0, 255),\n mimeType: 'text/markdown',\n size: data.byteLength,\n createdAt: new Date().toISOString(),\n sha256: outputHash,\n data,\n })\n }\n return json({ method, markdown, ...(segments && { segments }), ...(bib && { bib }), ...(model && { model }), inputHash, outputHash, output, warnings } satisfies ExtractResponse)\n}\n\n/** Body schema of `transcriptCleanupPost`. */\nexport const transcriptCleanupBodySchema = transcriptCleanupRequestSchema\n\n/**\n * Cleans transcript segments (filler words, punctuation, casing, fragmented sentences) without touching speakers or\n * order: the model only returns a new text per segment ID. Plain JSON: `{ cleaned, failed }`.\n */\nexport async function transcriptCleanupPost(ctx: AiHandlerContext, request: HandlerRequest): Promise<HandlerResponse> {\n const user = await enforceRateLimit(ctx, request)\n const { segments, model } = await readBody(request, transcriptCleanupBodySchema)\n const languageModel = await ctx.ai.resolveModel(model, user)\n\n // Closing the menu or the page aborts the client's fetch; stop the upstream requests with it.\n const abort = new AbortController()\n request.signal?.addEventListener('abort', () => abort.abort(), { once: true })\n try {\n return json(await runTranscriptCleanup(segments, (messages, signal) => ctx.ai.streamChat({ model: languageModel, messages, signal }), abort.signal))\n }\n catch (error) {\n throw error instanceof TranscriptCleanupUnreadableError ? new HttpError(502, error.message) : error\n }\n}\n"],"mappings":"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"}
|
package/dist/server/tools.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export interface ToolContext {
|
|
|
12
12
|
user: object;
|
|
13
13
|
/** Aborts when the client cancels the chat. */
|
|
14
14
|
signal: AbortSignal;
|
|
15
|
+
/** The Document open in the chat, as the client sent it (`documentId` of the chat body); absent for a chat without a Document. */
|
|
16
|
+
documentId?: string;
|
|
15
17
|
/** The current content of a Document that is open in the collaboration server (unsaved edits included), `null` when it is not. */
|
|
16
18
|
liveContent(id: string): JSONContent | null;
|
|
17
19
|
}
|
package/dist/server/tools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","names":[],"sources":["../../src/server/tools.ts"],"sourcesContent":["import type { JSONContent } from '@tiptap/core'\nimport type { ToolSet } from 'ai'\nimport { AGENT_TOOL, type ChatToolInfo } from '../features/ai/protocol'\nimport type { EditorPorts } from './ports'\n\n// The document chat's tools besides `edit_document`. `edit_document` is the chat's own tool (its calls are checked,\n// retried and streamed as `ops`, see `chatPost`); everything registered here is read-only: the server runs the call, feeds\n// the result back to the model and goes on with the next turn.\n\n/** What a provider's `handle` knows about the request it runs for. */\nexport interface ToolContext {\n /** The ports of the backend (the index, when the host has one, is already decorated). */\n ports: EditorPorts\n /** The user behind the request; every read must stay inside what they may read. */\n user: object\n /** Aborts when the client cancels the chat. */\n signal: AbortSignal\n /** The current content of a Document that is open in the collaboration server (unsaved edits included), `null` when it is not. */\n liveContent(id: string): JSONContent | null\n}\n\n/**\n * A read-only tool of the document chat: its definition for the model (`tool`, without `execute`) and the server-side\n * handler. `handle` gets the model's raw `input` (validate it yourself) and answers the text the model reads as the\n * tool result; a refusal or an unusable input is an answer too, so the model can react. Throwing is for failures the model\n * cannot fix: the chat hands the message back as the result.\n */\nexport interface ToolProvider {\n /** The name the model calls; unique, and not `edit_document`. */\n name: string\n /** The AI SDK tool definition: description and input schema, no `execute`. */\n tool: ToolSet[string]\n handle(ctx: ToolContext, input: unknown): Promise<string>\n}\n\n/** `providers` as the chat uses them. */\nexport interface ToolRegistry {\n /** The tool definitions by name, for the model. */\n tools: ToolSet\n providers: ReadonlyMap<string, ToolProvider>\n /** Name and description of each tool, for the system prompt. */\n infos: ChatToolInfo[]\n}\n\n/** Indexes `providers` by name; throws for a missing or duplicate name and for `edit_document`, which the chat keeps for itself. */\nexport function toolRegistry(providers: readonly ToolProvider[] = []): ToolRegistry {\n const byName = new Map<string, ToolProvider>()\n for (const provider of providers) {\n if (!provider.name) throw new Error('@skaile/forge-editor: a tool provider needs a name')\n if (provider.name === AGENT_TOOL) throw new Error(`@skaile/forge-editor: \"${AGENT_TOOL}\" is the document chat's own tool and cannot be registered`)\n if (byName.has(provider.name)) throw new Error(`@skaile/forge-editor: tool \"${provider.name}\" is registered twice`)\n byName.set(provider.name, provider)\n }\n return {\n tools: Object.fromEntries([...byName].map(([name, { tool }]) => [name, tool])),\n providers: byName,\n infos: [...byName.values()].map(({ name, tool }) => ({ name, description: typeof tool.description === 'string' ? tool.description : '' })),\n }\n}\n"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"tools.js","names":[],"sources":["../../src/server/tools.ts"],"sourcesContent":["import type { JSONContent } from '@tiptap/core'\nimport type { ToolSet } from 'ai'\nimport { AGENT_TOOL, type ChatToolInfo } from '../features/ai/protocol'\nimport type { EditorPorts } from './ports'\n\n// The document chat's tools besides `edit_document`. `edit_document` is the chat's own tool (its calls are checked,\n// retried and streamed as `ops`, see `chatPost`); everything registered here is read-only: the server runs the call, feeds\n// the result back to the model and goes on with the next turn.\n\n/** What a provider's `handle` knows about the request it runs for. */\nexport interface ToolContext {\n /** The ports of the backend (the index, when the host has one, is already decorated). */\n ports: EditorPorts\n /** The user behind the request; every read must stay inside what they may read. */\n user: object\n /** Aborts when the client cancels the chat. */\n signal: AbortSignal\n /** The Document open in the chat, as the client sent it (`documentId` of the chat body); absent for a chat without a Document. */\n documentId?: string\n /** The current content of a Document that is open in the collaboration server (unsaved edits included), `null` when it is not. */\n liveContent(id: string): JSONContent | null\n}\n\n/**\n * A read-only tool of the document chat: its definition for the model (`tool`, without `execute`) and the server-side\n * handler. `handle` gets the model's raw `input` (validate it yourself) and answers the text the model reads as the\n * tool result; a refusal or an unusable input is an answer too, so the model can react. Throwing is for failures the model\n * cannot fix: the chat hands the message back as the result.\n */\nexport interface ToolProvider {\n /** The name the model calls; unique, and not `edit_document`. */\n name: string\n /** The AI SDK tool definition: description and input schema, no `execute`. */\n tool: ToolSet[string]\n handle(ctx: ToolContext, input: unknown): Promise<string>\n}\n\n/** `providers` as the chat uses them. */\nexport interface ToolRegistry {\n /** The tool definitions by name, for the model. */\n tools: ToolSet\n providers: ReadonlyMap<string, ToolProvider>\n /** Name and description of each tool, for the system prompt. */\n infos: ChatToolInfo[]\n}\n\n/** Indexes `providers` by name; throws for a missing or duplicate name and for `edit_document`, which the chat keeps for itself. */\nexport function toolRegistry(providers: readonly ToolProvider[] = []): ToolRegistry {\n const byName = new Map<string, ToolProvider>()\n for (const provider of providers) {\n if (!provider.name) throw new Error('@skaile/forge-editor: a tool provider needs a name')\n if (provider.name === AGENT_TOOL) throw new Error(`@skaile/forge-editor: \"${AGENT_TOOL}\" is the document chat's own tool and cannot be registered`)\n if (byName.has(provider.name)) throw new Error(`@skaile/forge-editor: tool \"${provider.name}\" is registered twice`)\n byName.set(provider.name, provider)\n }\n return {\n tools: Object.fromEntries([...byName].map(([name, { tool }]) => [name, tool])),\n providers: byName,\n infos: [...byName.values()].map(({ name, tool }) => ({ name, description: typeof tool.description === 'string' ? tool.description : '' })),\n }\n}\n"],"mappings":";;;AA+CA,SAAgB,aAAa,YAAqC,CAAC,GAAiB;CAClF,MAAM,yBAAS,IAAI,IAA0B;CAC7C,KAAK,MAAM,YAAY,WAAW;EAChC,IAAI,CAAC,SAAS,MAAM,MAAM,IAAI,MAAM,oDAAoD;EACxF,IAAI,SAAS,SAAA,iBAAqB,MAAM,IAAI,MAAM,0BAA0B,WAAW,2DAA2D;EAClJ,IAAI,OAAO,IAAI,SAAS,IAAI,GAAG,MAAM,IAAI,MAAM,+BAA+B,SAAS,KAAK,sBAAsB;EAClH,OAAO,IAAI,SAAS,MAAM,QAAQ;CACpC;CACA,OAAO;EACL,OAAO,OAAO,YAAY,CAAC,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,MAAM,IAAI,CAAC,CAAC;EAC7E,WAAW;EACX,OAAO,CAAC,GAAG,OAAO,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,MAAM,YAAY;GAAE;GAAM,aAAa,OAAO,KAAK,gBAAgB,WAAW,KAAK,cAAc;EAAG,EAAE;CAC3I;AACF"}
|
|
@@ -5,10 +5,10 @@ import { basketById } from "../features/composites/baskets.js";
|
|
|
5
5
|
import { useUiKit } from "../vue/kit/use-ui-kit.js";
|
|
6
6
|
import { useEditorSettings } from "../vue/settings.js";
|
|
7
7
|
import { useEditorI18n } from "../vue/use-editor-i18n.js";
|
|
8
|
+
import _sfc_main$1 from "../features/composites/components/DocumentBadges.js";
|
|
8
9
|
import { DRAG_MIME, focusCrumbs, indexItems, isOutlineHeader, itemBasket } from "./tree-items.js";
|
|
9
10
|
import { refocusRow, rowPosition } from "./row-focus.js";
|
|
10
11
|
import { rowOf } from "./tree-model.js";
|
|
11
|
-
import _sfc_main$1 from "../features/composites/components/DocumentBadges.js";
|
|
12
12
|
import { useTreeModel } from "./use-tree-model.js";
|
|
13
13
|
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, mergeModels, normalizeClass, normalizeStyle, openBlock, renderList, renderSlot, toDisplayString, unref, useModel, withCtx, withKeys, withModifiers } from "vue";
|
|
14
14
|
//#region src/tree/DocumentTree.vue
|
|
@@ -3,10 +3,10 @@ import { useEditorSettings } from "../vue/settings.js";
|
|
|
3
3
|
import { useEditorContext } from "../vue/context.js";
|
|
4
4
|
import { useEditorI18n } from "../vue/use-editor-i18n.js";
|
|
5
5
|
import { useStoreRef } from "../vue/store-ref.js";
|
|
6
|
+
import { useFileImport } from "../features/media/vue.js";
|
|
6
7
|
import { treeView } from "./tree-view-model.js";
|
|
7
8
|
import { createTreeModel } from "./tree-model.js";
|
|
8
9
|
import { createTreeExecutor, treeEditorOf } from "./tree-host.js";
|
|
9
|
-
import { useFileImport } from "../features/media/vue.js";
|
|
10
10
|
import { computed, onScopeDispose, shallowRef, watch } from "vue";
|
|
11
11
|
//#region src/tree/use-tree-model.ts
|
|
12
12
|
/**
|
|
@@ -82,21 +82,21 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
82
82
|
editor: import("vue").ComputedRef<Editor | undefined>;
|
|
83
83
|
preset: ComposedEditor<Component>;
|
|
84
84
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
85
|
+
"update:modelValue": (content: JSONContent) => any;
|
|
86
|
+
ready: (editor: Editor) => any;
|
|
87
|
+
settingsOpen: (tab?: string | undefined) => any;
|
|
85
88
|
"update:sidebarOpen": (value: boolean) => any;
|
|
86
89
|
"update:commentsOpen": (value: boolean) => any;
|
|
87
|
-
ready: (editor: Editor) => any;
|
|
88
|
-
"update:modelValue": (content: JSONContent) => any;
|
|
89
90
|
metadataCreate: (list: string, item: MetadataItem) => any;
|
|
90
91
|
contentError: (error: Error) => any;
|
|
91
|
-
settingsOpen: (tab?: string | undefined) => any;
|
|
92
92
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
93
|
+
"onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
|
|
94
|
+
onReady?: ((editor: Editor) => any) | undefined;
|
|
95
|
+
onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
|
|
93
96
|
"onUpdate:sidebarOpen"?: ((value: boolean) => any) | undefined;
|
|
94
97
|
"onUpdate:commentsOpen"?: ((value: boolean) => any) | undefined;
|
|
95
|
-
onReady?: ((editor: Editor) => any) | undefined;
|
|
96
|
-
"onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
|
|
97
98
|
onMetadataCreate?: ((list: string, item: MetadataItem) => any) | undefined;
|
|
98
99
|
onContentError?: ((error: Error) => any) | undefined;
|
|
99
|
-
onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
|
|
100
100
|
}>, {
|
|
101
101
|
content: ContentLevel | readonly FeatureId[];
|
|
102
102
|
ai: boolean;
|
|
@@ -41,21 +41,21 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
41
41
|
editor: import("vue").ComputedRef<Editor | undefined>;
|
|
42
42
|
preset: EditorPreset<Component>;
|
|
43
43
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
44
|
+
"update:modelValue": (content: JSONContent) => any;
|
|
45
|
+
ready: (editor: Editor) => any;
|
|
46
|
+
settingsOpen: (tab?: string | undefined) => any;
|
|
44
47
|
"update:sidebarOpen": (value: boolean) => any;
|
|
45
48
|
"update:commentsOpen": (value: boolean) => any;
|
|
46
|
-
ready: (editor: Editor) => any;
|
|
47
|
-
"update:modelValue": (content: JSONContent) => any;
|
|
48
49
|
metadataCreate: (list: string, item: MetadataItem) => any;
|
|
49
50
|
contentError: (error: Error) => any;
|
|
50
|
-
settingsOpen: (tab?: string | undefined) => any;
|
|
51
51
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
52
|
+
"onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
|
|
53
|
+
onReady?: ((editor: Editor) => any) | undefined;
|
|
54
|
+
onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
|
|
52
55
|
"onUpdate:sidebarOpen"?: ((value: boolean) => any) | undefined;
|
|
53
56
|
"onUpdate:commentsOpen"?: ((value: boolean) => any) | undefined;
|
|
54
|
-
onReady?: ((editor: Editor) => any) | undefined;
|
|
55
|
-
"onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
|
|
56
57
|
onMetadataCreate?: ((list: string, item: MetadataItem) => any) | undefined;
|
|
57
58
|
onContentError?: ((error: Error) => any) | undefined;
|
|
58
|
-
onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
|
|
59
59
|
}>, {
|
|
60
60
|
editable: boolean;
|
|
61
61
|
settingsUi: "internal" | "external";
|
|
@@ -43,21 +43,21 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
43
43
|
settingsOpen: import("vue").Ref<boolean, boolean>;
|
|
44
44
|
settingsTab: import("vue").Ref<string | undefined, string | undefined>;
|
|
45
45
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
46
|
+
"update:modelValue": (content: JSONContent) => any;
|
|
47
|
+
ready: (editor: Editor) => any;
|
|
48
|
+
settingsOpen: (tab?: string | undefined) => any;
|
|
46
49
|
"update:sidebarOpen": (value: boolean) => any;
|
|
47
50
|
"update:commentsOpen": (value: boolean) => any;
|
|
48
|
-
ready: (editor: Editor) => any;
|
|
49
|
-
"update:modelValue": (content: JSONContent) => any;
|
|
50
51
|
metadataCreate: (list: string, item: MetadataItem) => any;
|
|
51
52
|
contentError: (error: Error) => any;
|
|
52
|
-
settingsOpen: (tab?: string | undefined) => any;
|
|
53
53
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
54
|
+
"onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
|
|
55
|
+
onReady?: ((editor: Editor) => any) | undefined;
|
|
56
|
+
onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
|
|
54
57
|
"onUpdate:sidebarOpen"?: ((value: boolean) => any) | undefined;
|
|
55
58
|
"onUpdate:commentsOpen"?: ((value: boolean) => any) | undefined;
|
|
56
|
-
onReady?: ((editor: Editor) => any) | undefined;
|
|
57
|
-
"onUpdate:modelValue"?: ((content: JSONContent) => any) | undefined;
|
|
58
59
|
onMetadataCreate?: ((list: string, item: MetadataItem) => any) | undefined;
|
|
59
60
|
onContentError?: ((error: Error) => any) | undefined;
|
|
60
|
-
onSettingsOpen?: ((tab?: string | undefined) => any) | undefined;
|
|
61
61
|
}>, {
|
|
62
62
|
editable: boolean;
|
|
63
63
|
settingsUi: "internal" | "external";
|
package/dist/vue/compose.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { coreUiFeature } from "../core/core-feature.js";
|
|
2
2
|
import { AI_BINDINGS, layerCompositions } from "../presets/layer.js";
|
|
3
|
-
import { mediaFeature } from "../features/media/vue.js";
|
|
4
3
|
import _sfc_main from "./components/EditorCommandPalette.js";
|
|
5
4
|
import { aiFeature } from "../features/ai/vue.js";
|
|
6
5
|
import { authorsFeature } from "../features/authors/vue.js";
|
|
@@ -8,6 +7,7 @@ import { blockUiFeature } from "../features/block-ui/vue.js";
|
|
|
8
7
|
import { collabFeature } from "../features/collab/vue.js";
|
|
9
8
|
import { commentsFeature } from "../features/comments/vue.js";
|
|
10
9
|
import { compositeFeature } from "../features/composites/vue.js";
|
|
10
|
+
import { mediaFeature } from "../features/media/vue.js";
|
|
11
11
|
import { themesFeature } from "../features/themes/vue.js";
|
|
12
12
|
import { pruefungFeature } from "../features/pruefung/vue.js";
|
|
13
13
|
import { exploreFeature } from "../features/explore/vue.js";
|
package/dist/vue/index.js
CHANGED
|
@@ -11,8 +11,8 @@ import { vueSuggestionRenderer } from "./suggestion-renderer.js";
|
|
|
11
11
|
import _sfc_main$2 from "./components/EditorSettingsPanel.js";
|
|
12
12
|
import _sfc_main$1 from "./components/EditorSettingsModal.js";
|
|
13
13
|
import _sfc_main$3 from "./components/EditorSurface.js";
|
|
14
|
-
import { useNotifier } from "./use-notifier.js";
|
|
15
14
|
import _sfc_main from "./components/EditorCommandPalette.js";
|
|
15
|
+
import { useNotifier } from "./use-notifier.js";
|
|
16
16
|
import _sfc_main$5 from "./components/SettingsSection.js";
|
|
17
17
|
import _sfc_main$4 from "./components/EditorToolbar.js";
|
|
18
18
|
export { EDITOR_CONTEXT, EDITOR_UI, _sfc_main as EditorCommandPalette, _sfc_main$1 as EditorSettingsModal, _sfc_main$2 as EditorSettingsPanel, _sfc_main$3 as EditorSurface, _sfc_main$4 as EditorToolbar, _sfc_main$5 as SettingsSection, _sfc_main$6 as SuggestionList, VUE_UI_KIT, attachNotifier, createEditor, createQueuedNotifier, storeFromRef, trackedStore, useEditorContext, useEditorI18n, useEditorSettings, useEditorUi, useNotifier, useStoreRef, useUiKit, useWritableStoreRef, vueSuggestionRenderer };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skaile/forge-editor",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Vue and React editor library on TipTap v3 (Vue: Nuxt UI; React: shadcn/ui): framework-free features with Vue and React bindings, simple and advanced presets, tree-shakeable entries. Built ESM with type declarations.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -703,7 +703,7 @@
|
|
|
703
703
|
"@lezer/highlight": "^1.2.1",
|
|
704
704
|
"@nuxt/ui": "^4.11.1",
|
|
705
705
|
"@skaile/forge-app-actions": "^0.1.0 || ^0.2.0 || ^0.3.0 || ^0.4.0",
|
|
706
|
-
"@skaile/forge-chat": "^0.2.
|
|
706
|
+
"@skaile/forge-chat": "^0.2.3",
|
|
707
707
|
"@tiptap/core": "^3.31.3",
|
|
708
708
|
"@tiptap/pm": "^3.31.3",
|
|
709
709
|
"@tiptap/react": "^3.31.3",
|