@ecsplico/editor 2.0.0 → 2.1.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 +18 -0
- package/README.md +4 -3
- package/dist/adapters/anchors.d.ts +80 -0
- package/dist/adapters/anchors.js +270 -0
- package/dist/adapters/anchors.js.map +1 -0
- package/dist/adapters/comments/client.d.ts +64 -0
- package/dist/adapters/comments/client.js +536 -0
- package/dist/adapters/comments/client.js.map +1 -0
- package/dist/adapters/comments/drizzle.d.ts +430 -0
- package/dist/adapters/comments/drizzle.js +236 -0
- package/dist/adapters/comments/drizzle.js.map +1 -0
- package/dist/adapters/comments/errors.d.ts +9 -0
- package/dist/adapters/comments/errors.js +19 -0
- package/dist/adapters/comments/errors.js.map +1 -0
- package/dist/adapters/comments/json.d.ts +19 -0
- package/dist/adapters/comments/json.js +94 -0
- package/dist/adapters/comments/json.js.map +1 -0
- package/dist/adapters/comments/record.js +78 -0
- package/dist/adapters/comments/record.js.map +1 -0
- package/dist/adapters/comments/server.d.ts +23 -0
- package/dist/adapters/comments/server.js +102 -0
- package/dist/adapters/comments/server.js.map +1 -0
- package/dist/adapters/comments/types.d.ts +109 -0
- package/dist/adapters/documents.js +108 -0
- package/dist/adapters/documents.js.map +1 -0
- package/dist/adapters/drizzle.d.ts +293 -0
- package/dist/adapters/drizzle.js +157 -0
- package/dist/adapters/drizzle.js.map +1 -0
- package/dist/adapters/files.js +30 -0
- package/dist/adapters/files.js.map +1 -0
- package/dist/adapters/forge.d.ts +58 -0
- package/dist/adapters/forge.js +91 -0
- package/dist/adapters/forge.js.map +1 -0
- package/dist/adapters/index.d.ts +13 -0
- package/dist/adapters/index.js +0 -0
- package/dist/adapters/json.d.ts +24 -0
- package/dist/adapters/json.js +128 -0
- package/dist/adapters/json.js.map +1 -0
- package/dist/adapters/nitro.d.ts +61 -0
- package/dist/adapters/nitro.js +212 -0
- package/dist/adapters/nitro.js.map +1 -0
- package/dist/adapters/routes.js +16 -0
- package/dist/adapters/routes.js.map +1 -0
- package/dist/adapters/shared.d.ts +37 -0
- package/dist/adapters/shared.js +32 -0
- package/dist/adapters/shared.js.map +1 -0
- package/dist/adapters/vue.d.ts +67 -0
- package/dist/adapters/vue.js +187 -0
- package/dist/adapters/vue.js.map +1 -0
- package/dist/code/CodeSurface.vue.d.ts +3 -3
- package/dist/core/doc-anchors.d.ts +5 -0
- package/dist/core/doc-anchors.js +4 -3
- package/dist/core/doc-anchors.js.map +1 -1
- package/dist/core/editor-ui.js +57 -1
- package/dist/core/editor-ui.js.map +1 -1
- package/dist/core/surface-session.js +139 -0
- package/dist/core/surface-session.js.map +1 -0
- package/dist/document/DocumentSurface.d.ts +1 -1
- package/dist/document/DocumentSurface.vue.d.ts +3 -3
- package/dist/features/ai/agent-op-review.d.ts +2 -2
- package/dist/features/ai/agent-op-review.js.map +1 -1
- package/dist/features/ai/agent-ops.d.ts +1 -1
- package/dist/features/ai/agent-ops.js +9 -6
- package/dist/features/ai/agent-ops.js.map +1 -1
- package/dist/features/ai/ai-apply.d.ts +1 -1
- package/dist/features/ai/ai-review.js +10 -1
- package/dist/features/ai/ai-review.js.map +1 -1
- package/dist/features/ai/components/AiSelectionMenu.js +78 -252
- package/dist/features/ai/components/AiSelectionMenu.js.map +1 -1
- package/dist/features/ai/components/AiSelectionMenu.vue.d.ts +2 -1
- package/dist/features/ai/components/AiSidebar.js +2 -2
- package/dist/features/ai/components/DocumentChatAnswer.js +1 -1
- package/dist/features/ai/components/DocumentChatOps.js +1 -0
- package/dist/features/ai/components/DocumentChatOps.js.map +1 -1
- package/dist/features/ai/components/ModelQuickSwitch.js +1 -1
- package/dist/features/ai/components/SettingsAiTab.js +1 -1
- package/dist/features/ai/components/SettingsModelProfiles.js +1 -1
- package/dist/features/ai/document-chat.d.ts +3 -2
- package/dist/features/ai/document-chat.js +16 -19
- package/dist/features/ai/document-chat.js.map +1 -1
- package/dist/features/ai/index.d.ts +5 -3
- package/dist/features/ai/index.js +4 -2
- package/dist/features/ai/protocol/agent-ops.d.ts +22 -17
- package/dist/features/ai/protocol/agent-ops.js +65 -39
- package/dist/features/ai/protocol/agent-ops.js.map +1 -1
- package/dist/features/ai/protocol/block-lines.d.ts +89 -0
- package/dist/features/ai/protocol/block-lines.js +177 -0
- package/dist/features/ai/protocol/block-lines.js.map +1 -0
- package/dist/features/ai/protocol/chat.js +10 -10
- package/dist/features/ai/protocol/chat.js.map +1 -1
- package/dist/features/ai/protocol/index.d.ts +3 -2
- package/dist/features/ai/protocol/index.js +3 -2
- package/dist/features/ai/react/AiSelectionMenu.d.ts +3 -2
- package/dist/features/ai/react/AiSelectionMenu.js +64 -255
- package/dist/features/ai/react/AiSelectionMenu.js.map +1 -1
- package/dist/features/ai/react/AiSidebar.js +2 -2
- package/dist/features/ai/react/DocumentChatAnswer.js +1 -1
- package/dist/features/ai/react/DocumentChatOps.js +1 -0
- package/dist/features/ai/react/DocumentChatOps.js.map +1 -1
- package/dist/features/ai/react/ModelQuickSwitch.js +1 -1
- package/dist/features/ai/react/SettingsAiTab.js +1 -1
- package/dist/features/ai/react/SettingsModelProfiles.js +1 -1
- package/dist/features/ai/react/use-ai-selection-flow.js +34 -0
- package/dist/features/ai/react/use-ai-selection-flow.js.map +1 -0
- package/dist/features/ai/selection-flow.d.ts +133 -0
- package/dist/features/ai/selection-flow.js +283 -0
- package/dist/features/ai/selection-flow.js.map +1 -0
- package/dist/features/ai/use-ai-assistant.d.ts +1 -1
- package/dist/features/ai/use-document-chat.d.ts +1 -1
- package/dist/features/authors/components/AuthorColorsPanel.js +1 -1
- package/dist/features/authors/feature.js +1 -1
- package/dist/features/authors/index.js +1 -1
- package/dist/features/authors/react/AuthorColorsPanel.js +1 -1
- package/dist/features/comments/components/CommentBubble.js +1 -1
- package/dist/features/comments/components/CommentComposer.js +1 -1
- package/dist/features/comments/components/CommentThreadView.js +5 -2
- package/dist/features/comments/components/CommentThreadView.js.map +1 -1
- package/dist/features/comments/components/CommentsMargin.js +108 -230
- package/dist/features/comments/components/CommentsMargin.js.map +1 -1
- package/dist/features/comments/components/CommentsOverlay.js +1 -1
- package/dist/features/comments/feature.js +1 -1
- package/dist/features/comments/index.d.ts +4 -2
- package/dist/features/comments/index.js +4 -2
- package/dist/features/comments/margin-controller.d.ts +44 -0
- package/dist/features/comments/margin-controller.js +132 -0
- package/dist/features/comments/margin-controller.js.map +1 -0
- package/dist/features/comments/margin-layout.d.ts +55 -0
- package/dist/features/comments/margin-layout.js +79 -0
- package/dist/features/comments/margin-layout.js.map +1 -0
- package/dist/features/comments/model.d.ts +8 -0
- package/dist/features/comments/model.js +60 -34
- package/dist/features/comments/model.js.map +1 -1
- package/dist/features/comments/react/CommentBubble.js +1 -1
- package/dist/features/comments/react/CommentComposer.js +1 -1
- package/dist/features/comments/react/CommentThreadView.js +2 -2
- package/dist/features/comments/react/CommentThreadView.js.map +1 -1
- package/dist/features/comments/react/CommentsMargin.d.ts +0 -5
- package/dist/features/comments/react/CommentsMargin.js +85 -251
- package/dist/features/comments/react/CommentsMargin.js.map +1 -1
- package/dist/features/comments/react/CommentsOverlay.js +1 -1
- package/dist/features/comments/react/use-comments.js +1 -1
- package/dist/features/comments/react/use-margin-layout.js +45 -0
- package/dist/features/comments/react/use-margin-layout.js.map +1 -0
- package/dist/features/comments/react.d.ts +1 -1
- package/dist/features/comments/schema.d.ts +23 -0
- package/dist/features/comments/schema.js.map +1 -1
- package/dist/features/comments/snapshot-equality.js +40 -0
- package/dist/features/comments/snapshot-equality.js.map +1 -0
- package/dist/features/comments/thread-anchor.js +33 -0
- package/dist/features/comments/thread-anchor.js.map +1 -0
- package/dist/features/comments/use-comments.d.ts +3 -1
- package/dist/features/comments/use-comments.js +1 -0
- package/dist/features/comments/use-comments.js.map +1 -1
- package/dist/features/comments/use-margin-layout.js +29 -0
- package/dist/features/comments/use-margin-layout.js.map +1 -0
- package/dist/features/comments/vue.d.ts +1 -1
- package/dist/features/composites/accepted-children.d.ts +21 -0
- package/dist/features/composites/accepted-children.js +126 -0
- package/dist/features/composites/accepted-children.js.map +1 -0
- package/dist/features/composites/components/CompositeDetails.js +1 -1
- package/dist/features/composites/components/CompositeInspector.js +38 -164
- package/dist/features/composites/components/CompositeInspector.js.map +1 -1
- package/dist/features/composites/feature.d.ts +2 -1
- package/dist/features/composites/feature.js +8 -2
- package/dist/features/composites/feature.js.map +1 -1
- package/dist/features/composites/index.d.ts +5 -3
- package/dist/features/composites/index.js +8 -6
- package/dist/features/composites/inspector-model.d.ts +97 -0
- package/dist/features/composites/inspector-model.js +255 -0
- package/dist/features/composites/inspector-model.js.map +1 -0
- package/dist/features/composites/kinds.d.ts +11 -0
- package/dist/features/composites/kinds.js +17 -2
- package/dist/features/composites/kinds.js.map +1 -1
- package/dist/features/composites/outline.d.ts +18 -5
- package/dist/features/composites/outline.js +44 -12
- package/dist/features/composites/outline.js.map +1 -1
- package/dist/features/composites/react/CompositeDetails.js +1 -1
- package/dist/features/composites/react/CompositeInspector.d.ts +1 -1
- package/dist/features/composites/react/CompositeInspector.js +21 -180
- package/dist/features/composites/react/CompositeInspector.js.map +1 -1
- package/dist/features/composites/react/CompositeStatusMenu.d.ts +1 -1
- package/dist/features/composites/react/CompositeView.js +1 -1
- package/dist/features/composites/react/use-composite-inspector.js +29 -0
- package/dist/features/composites/react/use-composite-inspector.js.map +1 -0
- package/dist/features/composites/schema.d.ts +2 -2
- package/dist/features/composites/schema.js +2 -2
- package/dist/features/composites/schema.js.map +1 -1
- package/dist/features/composites/use-composite-anchors.d.ts +1 -1
- package/dist/features/composites/use-composite-inspector.js +41 -0
- package/dist/features/composites/use-composite-inspector.js.map +1 -0
- package/dist/features/composites/use-composite-rows.d.ts +1 -1
- package/dist/features/media/index.d.ts +1 -1
- package/dist/features/media/vue.d.ts +1 -1
- package/dist/features/mentions/index.d.ts +3 -3
- package/dist/features/mentions/index.js +3 -3
- package/dist/features/mentions/schema.d.ts +1 -1
- package/dist/features/mentions/schema.js +1 -1
- package/dist/features/themes/index.d.ts +1 -1
- package/dist/features/themes/index.js +1 -1
- package/dist/features/transcript/feature.js +1 -1
- package/dist/features/transcript/index.js +2 -2
- package/dist/i18n/de/ai.js +2 -0
- package/dist/i18n/de/ai.js.map +1 -1
- package/dist/i18n/de/composites.js +8 -1
- package/dist/i18n/de/composites.js.map +1 -1
- package/dist/i18n/de/tree.js +14 -1
- package/dist/i18n/de/tree.js.map +1 -1
- package/dist/i18n/de.d.ts +22 -0
- package/dist/i18n/en/ai.js +2 -0
- package/dist/i18n/en/ai.js.map +1 -1
- package/dist/i18n/en/composites.js +8 -1
- package/dist/i18n/en/composites.js.map +1 -1
- package/dist/i18n/en/tree.js +14 -1
- package/dist/i18n/en/tree.js.map +1 -1
- package/dist/integrations/composite-ai/composite-fill.js +2 -2
- package/dist/integrations/composite-ai/composite-summary.js +2 -2
- package/dist/integrations/composite-ai/index.js +2 -2
- package/dist/integrations/transcript-ai/transcript-cleanup.js +1 -1
- package/dist/markdown/MarkdownSurface.d.ts +1 -1
- package/dist/markdown/MarkdownSurface.js +2 -2
- package/dist/markdown/MarkdownSurface.vue.d.ts +7 -7
- package/dist/markdown/index.d.ts +2 -2
- package/dist/nuxt/runtime/server/routes/collab/[id].js +1 -1
- package/dist/react/EditorSurface.d.ts +1 -1
- package/dist/react/EditorSurface.js +43 -149
- package/dist/react/EditorSurface.js.map +1 -1
- package/dist/react/EditorToolbar.js +3 -40
- package/dist/react/EditorToolbar.js.map +1 -1
- package/dist/schema/advanced.js +2 -2
- package/dist/server/adapter.d.ts +2 -2
- package/dist/server/collab.js +1 -1
- package/dist/server/handlers/ai.d.ts +4 -2
- package/dist/server/handlers/ai.js +11 -9
- package/dist/server/handlers/ai.js.map +1 -1
- package/dist/server/handlers/context.d.ts +1 -1
- package/dist/server/handlers/editor.d.ts +1 -1
- package/dist/server/handlers/editor.js +2 -2
- package/dist/server/host.d.ts +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/settings/index.d.ts +1 -1
- package/dist/styles/tokens.css +1 -1
- package/dist/tree/DocumentTree.js +159 -387
- package/dist/tree/DocumentTree.js.map +1 -1
- package/dist/tree/drop-rules.js +37 -0
- package/dist/tree/drop-rules.js.map +1 -0
- package/dist/tree/react/DocumentTree.d.ts +2 -2
- package/dist/tree/react/DocumentTree.js +135 -397
- package/dist/tree/react/DocumentTree.js.map +1 -1
- package/dist/tree/react/use-tree-model.js +129 -0
- package/dist/tree/react/use-tree-model.js.map +1 -0
- package/dist/tree/react.js +1 -1
- package/dist/tree/tree-host.js +100 -0
- package/dist/tree/tree-host.js.map +1 -0
- package/dist/tree/tree-items.js +22 -43
- package/dist/tree/tree-items.js.map +1 -1
- package/dist/tree/tree-model.js +580 -0
- package/dist/tree/tree-model.js.map +1 -0
- package/dist/tree/tree-rows.js +77 -0
- package/dist/tree/tree-rows.js.map +1 -0
- package/dist/tree/use-tree-model.js +94 -0
- package/dist/tree/use-tree-model.js.map +1 -0
- package/dist/tree/vue.js +1 -1
- package/dist/vue/AdvancedEditor.vue.d.ts +4 -4
- package/dist/vue/SimpleEditor.vue.d.ts +2 -2
- package/dist/vue/components/EditorSurface.js +27 -87
- package/dist/vue/components/EditorSurface.js.map +1 -1
- package/dist/vue/components/EditorSurface.vue.d.ts +4 -4
- package/dist/vue/components/EditorToolbar.js +7 -44
- package/dist/vue/components/EditorToolbar.js.map +1 -1
- package/dist/vue/context.d.ts +1 -1
- package/dist/vue/context.js +1 -1
- package/dist/vue/context.js.map +1 -1
- package/dist/vue/use-editor-ui.d.ts +1 -1
- package/dist/vue/use-notifier.d.ts +1 -1
- package/docs/integration.md +97 -4
- package/docs/llms.md +9 -1
- package/docs/usage.md +10 -5
- package/package.json +67 -5
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BLOCK_LINE_IDS_HINT, stripBlockLineEcho } from "./block-lines.js";
|
|
1
2
|
//#region src/features/ai/protocol/agent-ops.ts
|
|
2
3
|
/** Name of the tool the chat model calls to propose edits. */
|
|
3
4
|
const AGENT_TOOL = "edit_document";
|
|
@@ -16,9 +17,9 @@ const AGENT_OP_TYPES = [
|
|
|
16
17
|
];
|
|
17
18
|
/**
|
|
18
19
|
* What became of an operation: decided in the review (`accepted`, `rejected`, `partial` for mixed hunks, `invalid`
|
|
19
|
-
* when its text changed underneath), still `pending`, or never reviewed: the
|
|
20
|
-
*
|
|
21
|
-
*
|
|
20
|
+
* when its text changed underneath), still `pending`, or never reviewed: the client found its block deleted since the
|
|
21
|
+
* document went to the model (`stale`) or refused it as out of scope (`blocked`), or the review found nothing to show
|
|
22
|
+
* (`unchanged`, `missing` block, `overlap` with open AI changes, `empty` result).
|
|
22
23
|
*/
|
|
23
24
|
const AGENT_OP_OUTCOMES = [
|
|
24
25
|
"accepted",
|
|
@@ -28,16 +29,11 @@ const AGENT_OP_OUTCOMES = [
|
|
|
28
29
|
"pending",
|
|
29
30
|
"unchanged",
|
|
30
31
|
"missing",
|
|
32
|
+
"stale",
|
|
31
33
|
"overlap",
|
|
32
34
|
"empty",
|
|
33
35
|
"blocked"
|
|
34
36
|
];
|
|
35
|
-
/** Block IDs of the chat document text (`[<id>] …` per line, see documentChatText). */
|
|
36
|
-
function documentBlockIds(document) {
|
|
37
|
-
const ids = /* @__PURE__ */ new Set();
|
|
38
|
-
for (const match of document.matchAll(/^\[([^\]\s]+)\] /gm)) ids.add(match[1]);
|
|
39
|
-
return ids;
|
|
40
|
-
}
|
|
41
37
|
/** The block an operation targets: `update`/`delete` its `id`, `add` its `afterId` (null: document start). */
|
|
42
38
|
function agentOpTarget(op) {
|
|
43
39
|
return op.type === "add" ? op.afterId : op.id;
|
|
@@ -54,6 +50,7 @@ const AGENT_TEXTS = {
|
|
|
54
50
|
missing: "nicht angezeigt, weil der Block nicht mehr existiert",
|
|
55
51
|
overlap: "nicht angezeigt, weil an der Stelle noch andere KI-Änderungen offen sind",
|
|
56
52
|
empty: "nicht angezeigt, weil der Inhalt leer ist",
|
|
53
|
+
stale: "nicht angezeigt: Block wurde inzwischen gelöscht",
|
|
57
54
|
blocked: "blockiert, weil er außerhalb des erlaubten Bereichs liegt"
|
|
58
55
|
},
|
|
59
56
|
summary: {
|
|
@@ -75,15 +72,19 @@ const AGENT_TEXTS = {
|
|
|
75
72
|
markdownMissing: "markdown fehlt",
|
|
76
73
|
markdownMissingUpdate: "markdown fehlt (zum Entfernen „delete“ verwenden)",
|
|
77
74
|
markdownTooLong: (max) => `markdown ist länger als ${max} Zeichen`,
|
|
78
|
-
unreadable: "die Eingabe war kein Objekt mit der Liste „ops“"
|
|
75
|
+
unreadable: "die Eingabe war kein Objekt mit der Liste „ops“",
|
|
76
|
+
afterIdMissing: "afterId fehlt; für den Dokumentanfang ausdrücklich null schreiben",
|
|
77
|
+
afterIdMissingScoped: "afterId fehlt; es muss ein ausgewählter Block sein",
|
|
78
|
+
inside: (target, owner, ownerIndex, deletes) => `Block ${target} liegt in ${owner}, das Operation ${ownerIndex} ${deletes ? "löscht" : "ersetzt"}`
|
|
79
79
|
},
|
|
80
80
|
retry: {
|
|
81
81
|
failed: "Fehler: Die Operationen wurden nicht angewendet, weil einige ungültig sind.",
|
|
82
82
|
failedPartial: "Fehler: Einige Operationen sind ungültig und wurden verworfen; die übrigen werden zur Prüfung angezeigt.",
|
|
83
83
|
invalidIds: (ids) => `Ungültige IDs: ${ids}`,
|
|
84
84
|
operation: (index, type, detail) => `- Operation ${index}${type ? ` (${type})` : ""}: ${detail}`,
|
|
85
|
+
inside: "Eine Operation auf einen Block innerhalb eines ersetzten oder gelöschten Blocks entfällt: die Operation auf den äußeren Block gilt. Soll der innere Block mitgeändert werden, schreibe die Änderung in das Markdown der äußeren Operation.",
|
|
85
86
|
scoped: "Verwende nur IDs aus dem Bearbeitungsbereich (siehe Nachricht); erfinde keine IDs.",
|
|
86
|
-
unscoped:
|
|
87
|
+
unscoped: BLOCK_LINE_IDS_HINT,
|
|
87
88
|
again: (tool) => `Rufe ${tool} erneut mit der vollständigen, korrigierten Liste aller Operationen auf. Lass Operationen weg, die sich nicht korrigieren lassen.`,
|
|
88
89
|
againPartial: (tool) => `Rufe ${tool} erneut nur mit den korrigierten Fassungen der oben genannten Operationen auf; wiederhole die angezeigten nicht. Lass Operationen weg, die sich nicht korrigieren lassen.`
|
|
89
90
|
},
|
|
@@ -106,6 +107,7 @@ const AGENT_TEXTS = {
|
|
|
106
107
|
missing: "not shown because the block no longer exists",
|
|
107
108
|
overlap: "not shown because other AI changes are still open at that place",
|
|
108
109
|
empty: "not shown because the content is empty",
|
|
110
|
+
stale: "not shown: the block was deleted in the meantime",
|
|
109
111
|
blocked: "blocked because it lies outside the allowed scope"
|
|
110
112
|
},
|
|
111
113
|
summary: {
|
|
@@ -127,13 +129,17 @@ const AGENT_TEXTS = {
|
|
|
127
129
|
markdownMissing: "markdown is missing",
|
|
128
130
|
markdownMissingUpdate: "markdown is missing (use \"delete\" to remove a block)",
|
|
129
131
|
markdownTooLong: (max) => `markdown is longer than ${max} characters`,
|
|
130
|
-
unreadable: "the input was not an object with the list \"ops\""
|
|
132
|
+
unreadable: "the input was not an object with the list \"ops\"",
|
|
133
|
+
afterIdMissing: "afterId is missing; write null explicitly for the start of the document",
|
|
134
|
+
afterIdMissingScoped: "afterId is missing; it must be a selected block",
|
|
135
|
+
inside: (target, owner, ownerIndex, deletes) => `block ${target} lies inside ${owner}, which operation ${ownerIndex} ${deletes ? "deletes" : "replaces"}`
|
|
131
136
|
},
|
|
132
137
|
retry: {
|
|
133
138
|
failed: "Error: The operations were not applied because some of them are invalid.",
|
|
134
139
|
failedPartial: "Error: Some operations are invalid and were dropped; the others are shown for review.",
|
|
135
140
|
invalidIds: (ids) => `Invalid IDs: ${ids}`,
|
|
136
141
|
operation: (index, type, detail) => `- Operation ${index}${type ? ` (${type})` : ""}: ${detail}`,
|
|
142
|
+
inside: "An operation on a block inside a replaced or deleted block is dropped: the operation on the outer block applies. To change the inner block as well, write the change into the Markdown of the outer operation.",
|
|
137
143
|
scoped: "Use only IDs from the editing scope (see message); do not invent IDs.",
|
|
138
144
|
unscoped: "Use only IDs that appear in square brackets in the document; do not invent IDs.",
|
|
139
145
|
again: (tool) => `Call ${tool} again with the complete, corrected list of all operations. Leave out operations that cannot be corrected.`,
|
|
@@ -160,30 +166,21 @@ function agentOpSummary(op, { locale = "de" } = {}) {
|
|
|
160
166
|
if (op.type !== "add") return text.block(op.type, op.target);
|
|
161
167
|
return op.target === null ? text.addStart : text.addAfter(op.target);
|
|
162
168
|
}
|
|
163
|
-
/** Removes an echoed `[<id>] ` line prefix of a known block from the model's Markdown. */
|
|
164
|
-
function stripIdPrefixes(markdown, known) {
|
|
165
|
-
return markdown.replace(/^\[([^\]\s]+)\] ?/gm, (prefix, id) => known(id) ? "" : prefix);
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Drops the indentation all non-blank lines share: lines echoed from a composite's body carry its two-space indent
|
|
169
|
-
* (see documentChatText), which would turn deeper nested lines into indented code.
|
|
170
|
-
*/
|
|
171
|
-
function dedent(markdown) {
|
|
172
|
-
const indents = markdown.split("\n").filter((line) => line.trim()).map((line) => /^[ \t]*/.exec(line)[0].length);
|
|
173
|
-
const common = indents.length ? Math.min(...indents) : 0;
|
|
174
|
-
return common ? markdown.replace(new RegExp(`^[ \\t]{0,${common}}`, "gm"), "") : markdown;
|
|
175
|
-
}
|
|
176
169
|
/**
|
|
177
|
-
* Checks the raw `ops` of `edit_document` calls: every ID must exist and lie in the scope (`add`
|
|
178
|
-
*
|
|
179
|
-
* block is updated, converted or deleted at most once
|
|
180
|
-
*
|
|
170
|
+
* Checks the raw `ops` of `edit_document` calls: every ID must exist and lie in the scope (`add` needs its `afterId`
|
|
171
|
+
* key, `null` for the document start, and in a selection scope an `afterId` from the selection), fields must be
|
|
172
|
+
* present, a `convert` target must be one of `targets`, a block is updated, converted or deleted at most once, and no
|
|
173
|
+
* operation may point into a block (`lines.below`) that another operation replaces (`update`) or deletes: the
|
|
174
|
+
* operation on the outer block wins. Several `add`s after the same block are fine; the client inserts them in the
|
|
175
|
+
* order they were written. Returns the valid operations in order and the problems of the others; the problems'
|
|
176
|
+
* `detail` is written in `context.locale` (default `de`).
|
|
181
177
|
*/
|
|
182
178
|
function checkAgentOps(raw, context) {
|
|
183
|
-
const
|
|
179
|
+
const accepted = [];
|
|
184
180
|
const problems = [];
|
|
185
181
|
const edited = /* @__PURE__ */ new Set();
|
|
186
182
|
const text = AGENT_TEXTS[context.locale ?? "de"].problem;
|
|
183
|
+
const known = (id) => context.lines.ids.has(id);
|
|
187
184
|
raw.forEach((entry, i) => {
|
|
188
185
|
const index = i + 1;
|
|
189
186
|
const value = entry && typeof entry === "object" ? entry : {};
|
|
@@ -195,22 +192,27 @@ function checkAgentOps(raw, context) {
|
|
|
195
192
|
reason,
|
|
196
193
|
detail
|
|
197
194
|
});
|
|
195
|
+
const accept = (op) => accepted.push({
|
|
196
|
+
op,
|
|
197
|
+
index
|
|
198
|
+
});
|
|
198
199
|
if (index > AGENT_LIMITS.ops) return problem("limit", null, text.limit(AGENT_LIMITS.ops));
|
|
199
200
|
if (!type || !AGENT_OP_TYPES.includes(type)) return problem("field", null, text.type(AGENT_OP_TYPES.join(", ")));
|
|
200
201
|
const idField = type === "add" ? "afterId" : "id";
|
|
201
202
|
const rawId = value[idField];
|
|
202
|
-
if (type === "add" &&
|
|
203
|
+
if (type === "add" && rawId === void 0) return problem("field", null, context.allowed ? text.afterIdMissingScoped : text.afterIdMissing);
|
|
204
|
+
if (type === "add" && rawId === null) {
|
|
203
205
|
if (context.allowed) return problem("scope", null, text.addAtStart);
|
|
204
206
|
} else if (typeof rawId !== "string" || !rawId.trim()) return problem("field", null, text.idMissing(idField));
|
|
205
207
|
const id = typeof rawId === "string" ? rawId.trim() : null;
|
|
206
208
|
if (id !== null) {
|
|
207
|
-
if (!
|
|
209
|
+
if (!known(id)) return problem("unknown", id, text.unknownId(id));
|
|
208
210
|
if (context.allowed && !context.allowed.has(id)) return problem("scope", id, text.outsideScope(id));
|
|
209
211
|
}
|
|
210
212
|
if (type === "delete") {
|
|
211
213
|
if (edited.has(id)) return problem("duplicate", id, text.duplicate(id));
|
|
212
214
|
edited.add(id);
|
|
213
|
-
return
|
|
215
|
+
return accept({
|
|
214
216
|
type,
|
|
215
217
|
id
|
|
216
218
|
});
|
|
@@ -221,28 +223,51 @@ function checkAgentOps(raw, context) {
|
|
|
221
223
|
if (!context.targets.has(to)) return problem("target", id, text.badTarget(to, [...context.targets].join(", ")));
|
|
222
224
|
if (edited.has(id)) return problem("duplicate", id, text.duplicate(id));
|
|
223
225
|
edited.add(id);
|
|
224
|
-
return
|
|
226
|
+
return accept({
|
|
225
227
|
type,
|
|
226
228
|
id,
|
|
227
229
|
to
|
|
228
230
|
});
|
|
229
231
|
}
|
|
230
|
-
const markdown = typeof value.markdown === "string" ?
|
|
232
|
+
const markdown = typeof value.markdown === "string" ? stripBlockLineEcho(value.markdown, known).trim() : "";
|
|
231
233
|
if (!markdown) return problem("field", id, type === "update" ? text.markdownMissingUpdate : text.markdownMissing);
|
|
232
234
|
if (markdown.length > AGENT_LIMITS.markdown) return problem("field", id, text.markdownTooLong(AGENT_LIMITS.markdown));
|
|
233
|
-
if (type === "add") return
|
|
235
|
+
if (type === "add") return accept({
|
|
234
236
|
type,
|
|
235
237
|
afterId: id,
|
|
236
238
|
markdown
|
|
237
239
|
});
|
|
238
240
|
if (edited.has(id)) return problem("duplicate", id, text.duplicate(id));
|
|
239
241
|
edited.add(id);
|
|
240
|
-
|
|
242
|
+
accept({
|
|
241
243
|
type: "update",
|
|
242
244
|
id,
|
|
243
245
|
markdown
|
|
244
246
|
});
|
|
245
247
|
});
|
|
248
|
+
const owners = accepted.filter(({ op }) => op.type === "update" || op.type === "delete");
|
|
249
|
+
const covering = ({ op }) => {
|
|
250
|
+
const target = agentOpTarget(op);
|
|
251
|
+
return target === null ? [] : owners.filter((owner) => context.lines.below(agentOpTarget(owner.op)).has(target));
|
|
252
|
+
};
|
|
253
|
+
const valid = [];
|
|
254
|
+
for (const entry of accepted) {
|
|
255
|
+
const cover = covering(entry);
|
|
256
|
+
const owner = cover.find((candidate) => !covering(candidate).length) ?? cover[0];
|
|
257
|
+
if (!owner) {
|
|
258
|
+
valid.push(entry.op);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
const target = agentOpTarget(entry.op);
|
|
262
|
+
problems.push({
|
|
263
|
+
index: entry.index,
|
|
264
|
+
type: entry.op.type,
|
|
265
|
+
id: target,
|
|
266
|
+
reason: "inside",
|
|
267
|
+
detail: text.inside(target, agentOpTarget(owner.op), owner.index, owner.op.type === "delete")
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
problems.sort((a, b) => a.index - b.index);
|
|
246
271
|
return {
|
|
247
272
|
valid,
|
|
248
273
|
problems
|
|
@@ -266,11 +291,12 @@ function agentUnreadableProblem({ locale = "de" } = {}) {
|
|
|
266
291
|
*/
|
|
267
292
|
function agentRetryMessage(problems, scoped, { locale = "de", tool = AGENT_TOOL, partial = false } = {}) {
|
|
268
293
|
const text = AGENT_TEXTS[locale].retry;
|
|
269
|
-
const ids = [...new Set(problems.map((p) => p.id).filter((id) => !!id))];
|
|
294
|
+
const ids = [...new Set(problems.filter((p) => p.reason !== "inside").map((p) => p.id).filter((id) => !!id))];
|
|
270
295
|
return [
|
|
271
296
|
partial ? text.failedPartial : text.failed,
|
|
272
297
|
...ids.length ? [text.invalidIds(ids.join(", "))] : [],
|
|
273
298
|
...problems.map((p) => text.operation(p.index, p.type, p.detail)),
|
|
299
|
+
...problems.some((p) => p.reason === "inside") ? [text.inside] : [],
|
|
274
300
|
scoped ? text.scoped : text.unscoped,
|
|
275
301
|
partial ? text.againPartial(tool) : text.again(tool)
|
|
276
302
|
].join("\n");
|
|
@@ -293,6 +319,6 @@ function agentFeedbackText(feedback, { locale = "de" } = {}) {
|
|
|
293
319
|
return AGENT_TEXTS[locale].feedback(feedback.map((f) => `${f.op} (${agentOpSummary(f, { locale })}) ${outcome[f.outcome]}`).join("; "));
|
|
294
320
|
}
|
|
295
321
|
//#endregion
|
|
296
|
-
export { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, checkAgentOps
|
|
322
|
+
export { AGENT_LIMITS, AGENT_OP_OUTCOMES, AGENT_OP_OUTCOME_TEXT, AGENT_OP_TYPES, AGENT_TOOL, agentFeedbackText, agentOpSummary, agentOpTarget, agentRetryMessage, agentShownMessage, agentUnreadableProblem, checkAgentOps };
|
|
297
323
|
|
|
298
324
|
//# sourceMappingURL=agent-ops.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-ops.js","names":[],"sources":["../../../../src/features/ai/protocol/agent-ops.ts"],"sourcesContent":["// i18n-ignore-file: model-facing tool results, operation checks and decision texts (German by default, English on request; the model's language is not the editor's)\n/**\n * Agent operations of the document chat: the model proposes block edits through the `edit_document` tool, the server\n * checks every block ID against the document and the scope, and the client shows the valid ones as in-document review.\n */\nimport type { Locale } from '../../../i18n'\n\n/** Name of the tool the chat model calls to propose edits. */\nexport const AGENT_TOOL = 'edit_document'\n\n/** Per-turn limits: operations, Markdown per operation, IDs in a selection scope; retries after invalid IDs. */\nexport const AGENT_LIMITS = { ops: 50, markdown: 8000, scope: 5000, retries: 2 } as const\n\nexport const AGENT_OP_TYPES = ['add', 'update', 'delete', 'convert'] as const\nexport type AgentOpType = typeof AGENT_OP_TYPES[number]\n\n/**\n * One proposed edit as the model writes it: `update` replaces block `id` by `markdown`, `delete` removes block `id`,\n * `add` inserts `markdown` (one or more blocks) after block `afterId` (`null`: at the start of the document), `convert`\n * turns block `id` into the block type `to` (a `CONVERT_TARGET_IDS` entry or `composite:<kind>`), keeping its content.\n */\nexport type ModelAgentOp =\n | { type: 'update', id: string, markdown: string }\n | { type: 'delete', id: string }\n | { type: 'add', afterId: string | null, markdown: string }\n | { type: 'convert', id: string, to: string }\n\n/** A checked operation with the conversation-wide ID (`op1`, `op2`, …) that decisions refer to. */\nexport type AgentOp = ModelAgentOp & { op: string }\n\n/**\n * What became of an operation: decided in the review (`accepted`, `rejected`, `partial` for mixed hunks, `invalid`\n * when its text changed underneath), still `pending`, or never reviewed: the review found nothing to show\n * (`unchanged`, `missing` block, `overlap` with open AI changes, `empty` result), or the client refused it as out of\n * scope (`blocked`).\n */\nexport const AGENT_OP_OUTCOMES = ['accepted', 'rejected', 'partial', 'invalid', 'pending', 'unchanged', 'missing', 'overlap', 'empty', 'blocked'] as const\nexport type AgentOpOutcome = typeof AGENT_OP_OUTCOMES[number]\n\n/** Decision state of an earlier operation, sent with the next user message. */\nexport interface AgentOpFeedback { op: string, type: AgentOpType, target: string | null, outcome: AgentOpOutcome }\n\n/**\n * Why an operation was dropped: unknown block ID, ID outside the scope, a missing/invalid field, a block edited twice,\n * too many ops, a `convert` target that is not offered.\n */\nexport type AgentOpProblemReason = 'unknown' | 'scope' | 'field' | 'duplicate' | 'limit' | 'target'\n\n/** A dropped operation: its 1-based position in the tool call, type and target as far as readable, and the offending ID. */\nexport interface AgentOpProblem { index: number, type: string | null, id: string | null, reason: AgentOpProblemReason, detail: string }\n\n/**\n * Where operations may point: `known` IDs of the document; `allowed` the selection scope (null: the whole document);\n * `targets` the block types `convert` may turn a block into; `locale` the language of the problem details (default `de`).\n */\nexport interface AgentOpContext { known: (id: string) => boolean, allowed: ReadonlySet<string> | null, targets: ReadonlySet<string>, locale?: Locale }\n\n/** Block IDs of the chat document text (`[<id>] …` per line, see documentChatText). */\nexport function documentBlockIds(document: string): Set<string> {\n const ids = new Set<string>()\n for (const match of document.matchAll(/^\\[([^\\]\\s]+)\\] /gm)) ids.add(match[1]!)\n return ids\n}\n\n/** The block an operation targets: `update`/`delete` its `id`, `add` its `afterId` (null: document start). */\nexport function agentOpTarget(op: ModelAgentOp): string | null {\n return op.type === 'add' ? op.afterId : op.id\n}\n\n/**\n * Every model-facing text of the agent protocol, per locale (the editor's own prompts in chat.ts stay German). Selected\n * by the `locale` option of the functions below; `de` is the default and what existing hosts receive.\n */\ninterface AgentTexts {\n /** How an outcome reads for the model. */\n outcome: Record<AgentOpOutcome, string>\n summary: {\n convert: (target: string | null, to: string) => string\n block: (type: string, target: string | null) => string\n addStart: string\n addAfter: (target: string) => string\n }\n problem: {\n limit: (max: number) => string\n type: (allowed: string) => string\n addAtStart: string\n idMissing: (field: string) => string\n unknownId: (id: string) => string\n outsideScope: (id: string) => string\n duplicate: (id: string) => string\n toMissing: string\n badTarget: (to: string, allowed: string) => string\n markdownMissing: string\n markdownMissingUpdate: string\n markdownTooLong: (max: number) => string\n unreadable: string\n }\n retry: {\n failed: string\n failedPartial: string\n invalidIds: (ids: string) => string\n operation: (index: number, type: string | null, detail: string) => string\n scoped: string\n unscoped: string\n again: (tool: string) => string\n againPartial: (tool: string) => string\n }\n shown: {\n shown: (ops: string) => string\n none: string\n dropped: (count: number) => string\n decisions: string\n }\n feedback: (decisions: string) => string\n}\n\nconst AGENT_TEXTS: Record<Locale, AgentTexts> = {\n de: {\n outcome: {\n accepted: 'übernommen',\n rejected: 'abgelehnt',\n partial: 'teilweise übernommen',\n invalid: 'verfallen, weil der Text inzwischen anders geändert wurde',\n pending: 'noch nicht entschieden',\n unchanged: 'nicht angezeigt, weil er dem vorhandenen Text entspricht',\n missing: 'nicht angezeigt, weil der Block nicht mehr existiert',\n overlap: 'nicht angezeigt, weil an der Stelle noch andere KI-Änderungen offen sind',\n empty: 'nicht angezeigt, weil der Inhalt leer ist',\n blocked: 'blockiert, weil er außerhalb des erlaubten Bereichs liegt',\n },\n summary: {\n convert: (target, to) => `convert Block ${target} → ${to}`,\n block: (type, target) => `${type} Block ${target}`,\n addStart: 'add am Dokumentanfang',\n addAfter: target => `add nach Block ${target}`,\n },\n problem: {\n limit: max => `mehr als ${max} Operationen`,\n type: allowed => `unbekannter oder fehlender Typ (erlaubt: ${allowed})`,\n addAtStart: 'Einfügen am Dokumentanfang liegt außerhalb der Auswahl; afterId muss ein ausgewählter Block sein',\n idMissing: field => `${field} fehlt`,\n unknownId: id => `die ID „${id}“ gibt es im Dokument nicht`,\n outsideScope: id => `die ID „${id}“ liegt außerhalb der Auswahl`,\n duplicate: id => `der Block „${id}“ wird mehrfach geändert oder gelöscht`,\n toMissing: 'to fehlt',\n badTarget: (to, allowed) => `„${to}“ ist kein erlaubter Blocktyp für to (erlaubt: ${allowed})`,\n markdownMissing: 'markdown fehlt',\n markdownMissingUpdate: 'markdown fehlt (zum Entfernen „delete“ verwenden)',\n markdownTooLong: max => `markdown ist länger als ${max} Zeichen`,\n unreadable: 'die Eingabe war kein Objekt mit der Liste „ops“',\n },\n retry: {\n failed: 'Fehler: Die Operationen wurden nicht angewendet, weil einige ungültig sind.',\n failedPartial: 'Fehler: Einige Operationen sind ungültig und wurden verworfen; die übrigen werden zur Prüfung angezeigt.',\n invalidIds: ids => `Ungültige IDs: ${ids}`,\n operation: (index, type, detail) => `- Operation ${index}${type ? ` (${type})` : ''}: ${detail}`,\n scoped: 'Verwende nur IDs aus dem Bearbeitungsbereich (siehe Nachricht); erfinde keine IDs.',\n unscoped: 'Verwende nur IDs, die im Dokument in eckigen Klammern stehen; erfinde keine IDs.',\n again: tool => `Rufe ${tool} erneut mit der vollständigen, korrigierten Liste aller Operationen auf. Lass Operationen weg, die sich nicht korrigieren lassen.`,\n againPartial: tool => `Rufe ${tool} erneut nur mit den korrigierten Fassungen der oben genannten Operationen auf; wiederhole die angezeigten nicht. Lass Operationen weg, die sich nicht korrigieren lassen.`,\n },\n shown: {\n shown: ops => `Zur Prüfung im Dokument angezeigt: ${ops}.`,\n none: 'Keine der Operationen wurde angezeigt.',\n dropped: count => `${count} Operation(en) mit ungültigen IDs wurden verworfen; die Nutzerin bzw. der Nutzer wurde darauf hingewiesen.`,\n decisions: 'Die Nutzerin bzw. der Nutzer übernimmt oder verwirft jede Änderung im Dokument; die Entscheidungen folgen mit einer späteren Nachricht.',\n },\n feedback: decisions => `Entscheidungen zu deinen letzten Vorschlägen: ${decisions}.`,\n },\n en: {\n outcome: {\n accepted: 'accepted',\n rejected: 'rejected',\n partial: 'partially accepted',\n invalid: 'lapsed because the text was changed in the meantime',\n pending: 'not decided yet',\n unchanged: 'not shown because it matches the existing text',\n missing: 'not shown because the block no longer exists',\n overlap: 'not shown because other AI changes are still open at that place',\n empty: 'not shown because the content is empty',\n blocked: 'blocked because it lies outside the allowed scope',\n },\n summary: {\n convert: (target, to) => `convert block ${target} → ${to}`,\n block: (type, target) => `${type} block ${target}`,\n addStart: 'add at the start of the document',\n addAfter: target => `add after block ${target}`,\n },\n problem: {\n limit: max => `more than ${max} operations`,\n type: allowed => `unknown or missing type (allowed: ${allowed})`,\n addAtStart: 'inserting at the start of the document is outside the selection; afterId must be a selected block',\n idMissing: field => `${field} is missing`,\n unknownId: id => `the ID \"${id}\" does not exist in the document`,\n outsideScope: id => `the ID \"${id}\" is outside the selection`,\n duplicate: id => `the block \"${id}\" is changed or deleted more than once`,\n toMissing: 'to is missing',\n badTarget: (to, allowed) => `\"${to}\" is not an allowed block type for to (allowed: ${allowed})`,\n markdownMissing: 'markdown is missing',\n markdownMissingUpdate: 'markdown is missing (use \"delete\" to remove a block)',\n markdownTooLong: max => `markdown is longer than ${max} characters`,\n unreadable: 'the input was not an object with the list \"ops\"',\n },\n retry: {\n failed: 'Error: The operations were not applied because some of them are invalid.',\n failedPartial: 'Error: Some operations are invalid and were dropped; the others are shown for review.',\n invalidIds: ids => `Invalid IDs: ${ids}`,\n operation: (index, type, detail) => `- Operation ${index}${type ? ` (${type})` : ''}: ${detail}`,\n scoped: 'Use only IDs from the editing scope (see message); do not invent IDs.',\n unscoped: 'Use only IDs that appear in square brackets in the document; do not invent IDs.',\n again: tool => `Call ${tool} again with the complete, corrected list of all operations. Leave out operations that cannot be corrected.`,\n againPartial: tool => `Call ${tool} again with corrected versions of only the operations listed above; do not repeat the shown ones. Leave out operations that cannot be corrected.`,\n },\n shown: {\n shown: ops => `Shown in the document for review: ${ops}.`,\n none: 'None of the operations were shown.',\n dropped: count => `${count} operation(s) with invalid IDs were dropped; the user has been notified.`,\n decisions: 'The user accepts or rejects each change in the document; the decisions follow with a later message.',\n },\n feedback: decisions => `Decisions on your latest proposals: ${decisions}.`,\n },\n}\n\n/** How an outcome reads for the model, per locale; the chat shows the catalog's `ai.opTitle.*` texts. */\nexport const AGENT_OP_OUTCOME_TEXT: Record<Locale, Record<AgentOpOutcome, string>> = {\n de: AGENT_TEXTS.de.outcome,\n en: AGENT_TEXTS.en.outcome,\n}\n\n/** Text options of the model-facing functions: `locale` selects the language (default `de`). */\nexport interface AgentTextOptions { locale?: Locale }\n\n/** `update Block X`, `add nach Block X`, `add am Dokumentanfang`, `convert Block X → composite:task` (`to` when known); English with `locale: 'en'`. */\nexport function agentOpSummary(op: { type: AgentOpType, target: string | null, to?: string }, { locale = 'de' }: AgentTextOptions = {}): string {\n const text = AGENT_TEXTS[locale].summary\n if (op.type === 'convert' && op.to) return text.convert(op.target, op.to)\n if (op.type !== 'add') return text.block(op.type, op.target)\n return op.target === null ? text.addStart : text.addAfter(op.target)\n}\n\n/** Removes an echoed `[<id>] ` line prefix of a known block from the model's Markdown. */\nfunction stripIdPrefixes(markdown: string, known: AgentOpContext['known']): string {\n return markdown.replace(/^\\[([^\\]\\s]+)\\] ?/gm, (prefix, id: string) => known(id) ? '' : prefix)\n}\n\n/**\n * Drops the indentation all non-blank lines share: lines echoed from a composite's body carry its two-space indent\n * (see documentChatText), which would turn deeper nested lines into indented code.\n */\nfunction dedent(markdown: string): string {\n const indents = markdown.split('\\n').filter(line => line.trim()).map(line => /^[ \\t]*/.exec(line)![0].length)\n const common = indents.length ? Math.min(...indents) : 0\n return common ? markdown.replace(new RegExp(`^[ \\\\t]{0,${common}}`, 'gm'), '') : markdown\n}\n\n/**\n * Checks the raw `ops` of `edit_document` calls: every ID must exist and lie in the scope (`add` in a selection scope\n * needs an `afterId` from the selection), fields must be present, a `convert` target must be one of `targets`, and a\n * block is updated, converted or deleted at most once. Returns the valid operations in order and the problems of the others;\n * the problems' `detail` is written in `context.locale` (default `de`).\n */\nexport function checkAgentOps(raw: readonly unknown[], context: AgentOpContext): { valid: ModelAgentOp[], problems: AgentOpProblem[] } {\n const valid: ModelAgentOp[] = []\n const problems: AgentOpProblem[] = []\n const edited = new Set<string>()\n const text = AGENT_TEXTS[context.locale ?? 'de'].problem\n\n raw.forEach((entry, i) => {\n const index = i + 1\n const value = (entry && typeof entry === 'object' ? entry : {}) as Record<string, unknown>\n const type = typeof value.type === 'string' ? value.type : null\n const problem = (reason: AgentOpProblemReason, id: string | null, detail: string) => problems.push({ index, type, id, reason, detail })\n\n if (index > AGENT_LIMITS.ops) return problem('limit', null, text.limit(AGENT_LIMITS.ops))\n if (!type || !(AGENT_OP_TYPES as readonly string[]).includes(type)) return problem('field', null, text.type(AGENT_OP_TYPES.join(', ')))\n\n const idField = type === 'add' ? 'afterId' : 'id'\n const rawId = value[idField]\n if (type === 'add' && (rawId === null || rawId === undefined)) {\n if (context.allowed) return problem('scope', null, text.addAtStart)\n }\n else if (typeof rawId !== 'string' || !rawId.trim()) {\n return problem('field', null, text.idMissing(idField))\n }\n const id = typeof rawId === 'string' ? rawId.trim() : null\n if (id !== null) {\n if (!context.known(id)) return problem('unknown', id, text.unknownId(id))\n if (context.allowed && !context.allowed.has(id)) return problem('scope', id, text.outsideScope(id))\n }\n\n if (type === 'delete') {\n if (edited.has(id!)) return problem('duplicate', id, text.duplicate(id!))\n edited.add(id!)\n return valid.push({ type, id: id! })\n }\n\n if (type === 'convert') {\n const to = typeof value.to === 'string' ? value.to.trim() : ''\n if (!to) return problem('field', id, text.toMissing)\n if (!context.targets.has(to)) return problem('target', id, text.badTarget(to, [...context.targets].join(', ')))\n if (edited.has(id!)) return problem('duplicate', id, text.duplicate(id!))\n edited.add(id!)\n return valid.push({ type, id: id!, to })\n }\n\n const markdown = typeof value.markdown === 'string' ? dedent(stripIdPrefixes(value.markdown, context.known)).trim() : ''\n if (!markdown) return problem('field', id, type === 'update' ? text.markdownMissingUpdate : text.markdownMissing)\n if (markdown.length > AGENT_LIMITS.markdown) return problem('field', id, text.markdownTooLong(AGENT_LIMITS.markdown))\n if (type === 'add') return valid.push({ type, afterId: id, markdown })\n if (edited.has(id!)) return problem('duplicate', id, text.duplicate(id!))\n edited.add(id!)\n valid.push({ type: 'update', id: id!, markdown })\n })\n return { valid, problems }\n}\n\n/** The problem for a tool call whose input was no object with the list `ops` (index 0), worded in `locale` (default `de`). */\nexport function agentUnreadableProblem({ locale = 'de' }: AgentTextOptions = {}): AgentOpProblem {\n return { index: 0, type: null, id: null, reason: 'field', detail: AGENT_TEXTS[locale].problem.unreadable }\n}\n\n/**\n * Tool result asking the model to call `tool` (default `edit_document`, the host's own tool name when it exposes the\n * operations under another one) again without the invalid operations' mistakes; worded in `locale` (default `de`).\n * By default nothing was applied and the model resends the whole list; with `partial` the host already shows the\n * valid operations, so the model resends only corrected versions of the listed ones.\n */\nexport function agentRetryMessage(\n problems: readonly AgentOpProblem[],\n scoped: boolean,\n { locale = 'de', tool = AGENT_TOOL, partial = false }: AgentTextOptions & { tool?: string, partial?: boolean } = {},\n): string {\n const text = AGENT_TEXTS[locale].retry\n const ids = [...new Set(problems.map(p => p.id).filter((id): id is string => !!id))]\n return [\n partial ? text.failedPartial : text.failed,\n ...(ids.length ? [text.invalidIds(ids.join(', '))] : []),\n ...problems.map(p => text.operation(p.index, p.type, p.detail)),\n scoped ? text.scoped : text.unscoped,\n partial ? text.againPartial(tool) : text.again(tool),\n ].join('\\n')\n}\n\n/** Tool result of a past `edit_document` call: which operations were shown for review, and how many were dropped; worded in `locale` (default `de`). */\nexport function agentShownMessage(ops: readonly AgentOp[], dropped: number, { locale = 'de' }: AgentTextOptions = {}): string {\n const text = AGENT_TEXTS[locale].shown\n const lines = ops.length\n ? [text.shown(ops.map(op => `${op.op} (${agentOpSummary({ ...op, target: agentOpTarget(op) }, { locale })})`).join(', '))]\n : [text.none]\n if (dropped) lines.push(text.dropped(dropped))\n lines.push(text.decisions)\n return lines.join(' ')\n}\n\n/** „Entscheidungen zu deinen letzten Vorschlägen: op1 (update Block X) abgelehnt; …“ (English with `locale: 'en'`), or '' without feedback. */\nexport function agentFeedbackText(feedback: readonly AgentOpFeedback[], { locale = 'de' }: AgentTextOptions = {}): string {\n if (!feedback.length) return ''\n const outcome = AGENT_TEXTS[locale].outcome\n return AGENT_TEXTS[locale].feedback(feedback.map(f => `${f.op} (${agentOpSummary(f, { locale })}) ${outcome[f.outcome]}`).join('; '))\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agent-ops.js","names":[],"sources":["../../../../src/features/ai/protocol/agent-ops.ts"],"sourcesContent":["// i18n-ignore-file: model-facing tool results, operation checks and decision texts (German by default, English on request; the model's language is not the editor's)\n/**\n * Agent operations of the document chat: the model proposes block edits through the `edit_document` tool, the server\n * checks every block ID against the document and the scope, and the client shows the valid ones as in-document review.\n */\nimport type { Locale } from '../../../i18n'\n\nimport { BLOCK_LINE_IDS_HINT, type BlockLines, stripBlockLineEcho } from './block-lines'\n\n/** Name of the tool the chat model calls to propose edits. */\nexport const AGENT_TOOL = 'edit_document'\n\n/** Per-turn limits: operations, Markdown per operation, IDs in a selection scope; retries after invalid IDs. */\nexport const AGENT_LIMITS = { ops: 50, markdown: 8000, scope: 5000, retries: 2 } as const\n\nexport const AGENT_OP_TYPES = ['add', 'update', 'delete', 'convert'] as const\nexport type AgentOpType = typeof AGENT_OP_TYPES[number]\n\n/**\n * One proposed edit as the model writes it: `update` replaces block `id` by `markdown`, `delete` removes block `id`,\n * `add` inserts `markdown` (one or more blocks) after block `afterId` (`null`: at the start of the document; the key\n * is required), `convert` turns block `id` into the block type `to` (a `CONVERT_TARGET_IDS` entry or\n * `composite:<kind>`), keeping its content.\n */\nexport type ModelAgentOp =\n | { type: 'update', id: string, markdown: string }\n | { type: 'delete', id: string }\n | { type: 'add', afterId: string | null, markdown: string }\n | { type: 'convert', id: string, to: string }\n\n/** A checked operation with the conversation-wide ID (`op1`, `op2`, …) that decisions refer to. */\nexport type AgentOp = ModelAgentOp & { op: string }\n\n/**\n * What became of an operation: decided in the review (`accepted`, `rejected`, `partial` for mixed hunks, `invalid`\n * when its text changed underneath), still `pending`, or never reviewed: the client found its block deleted since the\n * document went to the model (`stale`) or refused it as out of scope (`blocked`), or the review found nothing to show\n * (`unchanged`, `missing` block, `overlap` with open AI changes, `empty` result).\n */\nexport const AGENT_OP_OUTCOMES = ['accepted', 'rejected', 'partial', 'invalid', 'pending', 'unchanged', 'missing', 'stale', 'overlap', 'empty', 'blocked'] as const\nexport type AgentOpOutcome = typeof AGENT_OP_OUTCOMES[number]\n\n/** Decision state of an earlier operation, sent with the next user message. */\nexport interface AgentOpFeedback { op: string, type: AgentOpType, target: string | null, outcome: AgentOpOutcome }\n\n/**\n * Why an operation was dropped: unknown block ID, ID outside the scope, a missing/invalid field, a block edited twice,\n * too many ops, a `convert` target that is not offered, a block inside another block that an operation of the same\n * answer replaces or deletes (`inside`).\n */\nexport type AgentOpProblemReason = 'unknown' | 'scope' | 'field' | 'duplicate' | 'limit' | 'target' | 'inside'\n\n/** A dropped operation: its 1-based position in the tool call, type and target as far as readable, and the offending ID. */\nexport interface AgentOpProblem { index: number, type: string | null, id: string | null, reason: AgentOpProblemReason, detail: string }\n\n/**\n * Where operations may point: `lines` the block IDs of the document text and which blocks lie inside which (see\n * `readBlockLines`); `allowed` the selection scope (null: the whole document); `targets` the block types `convert`\n * may turn a block into; `locale` the language of the problem details (default `de`).\n */\nexport interface AgentOpContext { lines: BlockLines, allowed: ReadonlySet<string> | null, targets: ReadonlySet<string>, locale?: Locale }\n\n/** The block an operation targets: `update`/`delete` its `id`, `add` its `afterId` (null: document start). */\nexport function agentOpTarget(op: ModelAgentOp): string | null {\n return op.type === 'add' ? op.afterId : op.id\n}\n\n/**\n * Every model-facing text of the agent protocol, per locale (the editor's own prompts in chat.ts stay German). Selected\n * by the `locale` option of the functions below; `de` is the default and what existing hosts receive.\n */\ninterface AgentTexts {\n /** How an outcome reads for the model. */\n outcome: Record<AgentOpOutcome, string>\n summary: {\n convert: (target: string | null, to: string) => string\n block: (type: string, target: string | null) => string\n addStart: string\n addAfter: (target: string) => string\n }\n problem: {\n limit: (max: number) => string\n type: (allowed: string) => string\n addAtStart: string\n idMissing: (field: string) => string\n unknownId: (id: string) => string\n outsideScope: (id: string) => string\n duplicate: (id: string) => string\n toMissing: string\n badTarget: (to: string, allowed: string) => string\n markdownMissing: string\n markdownMissingUpdate: string\n markdownTooLong: (max: number) => string\n unreadable: string\n afterIdMissing: string\n afterIdMissingScoped: string\n inside: (target: string, owner: string, ownerIndex: number, deletes: boolean) => string\n }\n retry: {\n failed: string\n failedPartial: string\n invalidIds: (ids: string) => string\n operation: (index: number, type: string | null, detail: string) => string\n scoped: string\n unscoped: string\n inside: string\n again: (tool: string) => string\n againPartial: (tool: string) => string\n }\n shown: {\n shown: (ops: string) => string\n none: string\n dropped: (count: number) => string\n decisions: string\n }\n feedback: (decisions: string) => string\n}\n\nconst AGENT_TEXTS: Record<Locale, AgentTexts> = {\n de: {\n outcome: {\n accepted: 'übernommen',\n rejected: 'abgelehnt',\n partial: 'teilweise übernommen',\n invalid: 'verfallen, weil der Text inzwischen anders geändert wurde',\n pending: 'noch nicht entschieden',\n unchanged: 'nicht angezeigt, weil er dem vorhandenen Text entspricht',\n missing: 'nicht angezeigt, weil der Block nicht mehr existiert',\n overlap: 'nicht angezeigt, weil an der Stelle noch andere KI-Änderungen offen sind',\n empty: 'nicht angezeigt, weil der Inhalt leer ist',\n stale: 'nicht angezeigt: Block wurde inzwischen gelöscht',\n blocked: 'blockiert, weil er außerhalb des erlaubten Bereichs liegt',\n },\n summary: {\n convert: (target, to) => `convert Block ${target} → ${to}`,\n block: (type, target) => `${type} Block ${target}`,\n addStart: 'add am Dokumentanfang',\n addAfter: target => `add nach Block ${target}`,\n },\n problem: {\n limit: max => `mehr als ${max} Operationen`,\n type: allowed => `unbekannter oder fehlender Typ (erlaubt: ${allowed})`,\n addAtStart: 'Einfügen am Dokumentanfang liegt außerhalb der Auswahl; afterId muss ein ausgewählter Block sein',\n idMissing: field => `${field} fehlt`,\n unknownId: id => `die ID „${id}“ gibt es im Dokument nicht`,\n outsideScope: id => `die ID „${id}“ liegt außerhalb der Auswahl`,\n duplicate: id => `der Block „${id}“ wird mehrfach geändert oder gelöscht`,\n toMissing: 'to fehlt',\n badTarget: (to, allowed) => `„${to}“ ist kein erlaubter Blocktyp für to (erlaubt: ${allowed})`,\n markdownMissing: 'markdown fehlt',\n markdownMissingUpdate: 'markdown fehlt (zum Entfernen „delete“ verwenden)',\n markdownTooLong: max => `markdown ist länger als ${max} Zeichen`,\n unreadable: 'die Eingabe war kein Objekt mit der Liste „ops“',\n afterIdMissing: 'afterId fehlt; für den Dokumentanfang ausdrücklich null schreiben',\n afterIdMissingScoped: 'afterId fehlt; es muss ein ausgewählter Block sein',\n inside: (target, owner, ownerIndex, deletes) => `Block ${target} liegt in ${owner}, das Operation ${ownerIndex} ${deletes ? 'löscht' : 'ersetzt'}`,\n },\n retry: {\n failed: 'Fehler: Die Operationen wurden nicht angewendet, weil einige ungültig sind.',\n failedPartial: 'Fehler: Einige Operationen sind ungültig und wurden verworfen; die übrigen werden zur Prüfung angezeigt.',\n invalidIds: ids => `Ungültige IDs: ${ids}`,\n operation: (index, type, detail) => `- Operation ${index}${type ? ` (${type})` : ''}: ${detail}`,\n inside: 'Eine Operation auf einen Block innerhalb eines ersetzten oder gelöschten Blocks entfällt: die Operation auf den äußeren Block gilt. Soll der innere Block mitgeändert werden, schreibe die Änderung in das Markdown der äußeren Operation.',\n scoped: 'Verwende nur IDs aus dem Bearbeitungsbereich (siehe Nachricht); erfinde keine IDs.',\n unscoped: BLOCK_LINE_IDS_HINT,\n again: tool => `Rufe ${tool} erneut mit der vollständigen, korrigierten Liste aller Operationen auf. Lass Operationen weg, die sich nicht korrigieren lassen.`,\n againPartial: tool => `Rufe ${tool} erneut nur mit den korrigierten Fassungen der oben genannten Operationen auf; wiederhole die angezeigten nicht. Lass Operationen weg, die sich nicht korrigieren lassen.`,\n },\n shown: {\n shown: ops => `Zur Prüfung im Dokument angezeigt: ${ops}.`,\n none: 'Keine der Operationen wurde angezeigt.',\n dropped: count => `${count} Operation(en) mit ungültigen IDs wurden verworfen; die Nutzerin bzw. der Nutzer wurde darauf hingewiesen.`,\n decisions: 'Die Nutzerin bzw. der Nutzer übernimmt oder verwirft jede Änderung im Dokument; die Entscheidungen folgen mit einer späteren Nachricht.',\n },\n feedback: decisions => `Entscheidungen zu deinen letzten Vorschlägen: ${decisions}.`,\n },\n en: {\n outcome: {\n accepted: 'accepted',\n rejected: 'rejected',\n partial: 'partially accepted',\n invalid: 'lapsed because the text was changed in the meantime',\n pending: 'not decided yet',\n unchanged: 'not shown because it matches the existing text',\n missing: 'not shown because the block no longer exists',\n overlap: 'not shown because other AI changes are still open at that place',\n empty: 'not shown because the content is empty',\n stale: 'not shown: the block was deleted in the meantime',\n blocked: 'blocked because it lies outside the allowed scope',\n },\n summary: {\n convert: (target, to) => `convert block ${target} → ${to}`,\n block: (type, target) => `${type} block ${target}`,\n addStart: 'add at the start of the document',\n addAfter: target => `add after block ${target}`,\n },\n problem: {\n limit: max => `more than ${max} operations`,\n type: allowed => `unknown or missing type (allowed: ${allowed})`,\n addAtStart: 'inserting at the start of the document is outside the selection; afterId must be a selected block',\n idMissing: field => `${field} is missing`,\n unknownId: id => `the ID \"${id}\" does not exist in the document`,\n outsideScope: id => `the ID \"${id}\" is outside the selection`,\n duplicate: id => `the block \"${id}\" is changed or deleted more than once`,\n toMissing: 'to is missing',\n badTarget: (to, allowed) => `\"${to}\" is not an allowed block type for to (allowed: ${allowed})`,\n markdownMissing: 'markdown is missing',\n markdownMissingUpdate: 'markdown is missing (use \"delete\" to remove a block)',\n markdownTooLong: max => `markdown is longer than ${max} characters`,\n unreadable: 'the input was not an object with the list \"ops\"',\n afterIdMissing: 'afterId is missing; write null explicitly for the start of the document',\n afterIdMissingScoped: 'afterId is missing; it must be a selected block',\n inside: (target, owner, ownerIndex, deletes) => `block ${target} lies inside ${owner}, which operation ${ownerIndex} ${deletes ? 'deletes' : 'replaces'}`,\n },\n retry: {\n failed: 'Error: The operations were not applied because some of them are invalid.',\n failedPartial: 'Error: Some operations are invalid and were dropped; the others are shown for review.',\n invalidIds: ids => `Invalid IDs: ${ids}`,\n operation: (index, type, detail) => `- Operation ${index}${type ? ` (${type})` : ''}: ${detail}`,\n inside: 'An operation on a block inside a replaced or deleted block is dropped: the operation on the outer block applies. To change the inner block as well, write the change into the Markdown of the outer operation.',\n scoped: 'Use only IDs from the editing scope (see message); do not invent IDs.',\n unscoped: 'Use only IDs that appear in square brackets in the document; do not invent IDs.',\n again: tool => `Call ${tool} again with the complete, corrected list of all operations. Leave out operations that cannot be corrected.`,\n againPartial: tool => `Call ${tool} again with corrected versions of only the operations listed above; do not repeat the shown ones. Leave out operations that cannot be corrected.`,\n },\n shown: {\n shown: ops => `Shown in the document for review: ${ops}.`,\n none: 'None of the operations were shown.',\n dropped: count => `${count} operation(s) with invalid IDs were dropped; the user has been notified.`,\n decisions: 'The user accepts or rejects each change in the document; the decisions follow with a later message.',\n },\n feedback: decisions => `Decisions on your latest proposals: ${decisions}.`,\n },\n}\n\n/** How an outcome reads for the model, per locale; the chat shows the catalog's `ai.opTitle.*` texts. */\nexport const AGENT_OP_OUTCOME_TEXT: Record<Locale, Record<AgentOpOutcome, string>> = {\n de: AGENT_TEXTS.de.outcome,\n en: AGENT_TEXTS.en.outcome,\n}\n\n/** Text options of the model-facing functions: `locale` selects the language (default `de`). */\nexport interface AgentTextOptions { locale?: Locale }\n\n/** `update Block X`, `add nach Block X`, `add am Dokumentanfang`, `convert Block X → composite:task` (`to` when known); English with `locale: 'en'`. */\nexport function agentOpSummary(op: { type: AgentOpType, target: string | null, to?: string }, { locale = 'de' }: AgentTextOptions = {}): string {\n const text = AGENT_TEXTS[locale].summary\n if (op.type === 'convert' && op.to) return text.convert(op.target, op.to)\n if (op.type !== 'add') return text.block(op.type, op.target)\n return op.target === null ? text.addStart : text.addAfter(op.target)\n}\n\n/**\n * Checks the raw `ops` of `edit_document` calls: every ID must exist and lie in the scope (`add` needs its `afterId`\n * key, `null` for the document start, and in a selection scope an `afterId` from the selection), fields must be\n * present, a `convert` target must be one of `targets`, a block is updated, converted or deleted at most once, and no\n * operation may point into a block (`lines.below`) that another operation replaces (`update`) or deletes: the\n * operation on the outer block wins. Several `add`s after the same block are fine; the client inserts them in the\n * order they were written. Returns the valid operations in order and the problems of the others; the problems'\n * `detail` is written in `context.locale` (default `de`).\n */\nexport function checkAgentOps(raw: readonly unknown[], context: AgentOpContext): { valid: ModelAgentOp[], problems: AgentOpProblem[] } {\n const accepted: { op: ModelAgentOp, index: number }[] = []\n const problems: AgentOpProblem[] = []\n const edited = new Set<string>()\n const text = AGENT_TEXTS[context.locale ?? 'de'].problem\n const known = (id: string) => context.lines.ids.has(id)\n\n raw.forEach((entry, i) => {\n const index = i + 1\n const value = (entry && typeof entry === 'object' ? entry : {}) as Record<string, unknown>\n const type = typeof value.type === 'string' ? value.type : null\n const problem = (reason: AgentOpProblemReason, id: string | null, detail: string) => problems.push({ index, type, id, reason, detail })\n const accept = (op: ModelAgentOp) => accepted.push({ op, index })\n\n if (index > AGENT_LIMITS.ops) return problem('limit', null, text.limit(AGENT_LIMITS.ops))\n if (!type || !(AGENT_OP_TYPES as readonly string[]).includes(type)) return problem('field', null, text.type(AGENT_OP_TYPES.join(', ')))\n\n const idField = type === 'add' ? 'afterId' : 'id'\n const rawId = value[idField]\n // `afterId: null` is the document start; a forgotten key must not become one.\n if (type === 'add' && rawId === undefined) {\n return problem('field', null, context.allowed ? text.afterIdMissingScoped : text.afterIdMissing)\n }\n if (type === 'add' && rawId === null) {\n if (context.allowed) return problem('scope', null, text.addAtStart)\n }\n else if (typeof rawId !== 'string' || !rawId.trim()) {\n return problem('field', null, text.idMissing(idField))\n }\n const id = typeof rawId === 'string' ? rawId.trim() : null\n if (id !== null) {\n if (!known(id)) return problem('unknown', id, text.unknownId(id))\n if (context.allowed && !context.allowed.has(id)) return problem('scope', id, text.outsideScope(id))\n }\n\n if (type === 'delete') {\n if (edited.has(id!)) return problem('duplicate', id, text.duplicate(id!))\n edited.add(id!)\n return accept({ type, id: id! })\n }\n\n if (type === 'convert') {\n const to = typeof value.to === 'string' ? value.to.trim() : ''\n if (!to) return problem('field', id, text.toMissing)\n if (!context.targets.has(to)) return problem('target', id, text.badTarget(to, [...context.targets].join(', ')))\n if (edited.has(id!)) return problem('duplicate', id, text.duplicate(id!))\n edited.add(id!)\n return accept({ type, id: id!, to })\n }\n\n const markdown = typeof value.markdown === 'string' ? stripBlockLineEcho(value.markdown, known).trim() : ''\n if (!markdown) return problem('field', id, type === 'update' ? text.markdownMissingUpdate : text.markdownMissing)\n if (markdown.length > AGENT_LIMITS.markdown) return problem('field', id, text.markdownTooLong(AGENT_LIMITS.markdown))\n if (type === 'add') return accept({ type, afterId: id, markdown })\n if (edited.has(id!)) return problem('duplicate', id, text.duplicate(id!))\n edited.add(id!)\n accept({ type: 'update', id: id!, markdown })\n })\n\n // An operation on a block inside another operation's block is dropped, naming the outermost operation that covers it.\n const owners = accepted.filter(({ op }) => op.type === 'update' || op.type === 'delete')\n const covering = ({ op }: typeof accepted[number]) => {\n const target = agentOpTarget(op)\n return target === null ? [] : owners.filter(owner => context.lines.below(agentOpTarget(owner.op)!).has(target))\n }\n const valid: ModelAgentOp[] = []\n for (const entry of accepted) {\n const cover = covering(entry)\n const owner = cover.find(candidate => !covering(candidate).length) ?? cover[0]\n if (!owner) {\n valid.push(entry.op)\n continue\n }\n const target = agentOpTarget(entry.op)!\n problems.push({\n index: entry.index,\n type: entry.op.type,\n id: target,\n reason: 'inside',\n detail: text.inside(target, agentOpTarget(owner.op)!, owner.index, owner.op.type === 'delete'),\n })\n }\n problems.sort((a, b) => a.index - b.index)\n return { valid, problems }\n}\n\n/** The problem for a tool call whose input was no object with the list `ops` (index 0), worded in `locale` (default `de`). */\nexport function agentUnreadableProblem({ locale = 'de' }: AgentTextOptions = {}): AgentOpProblem {\n return { index: 0, type: null, id: null, reason: 'field', detail: AGENT_TEXTS[locale].problem.unreadable }\n}\n\n/**\n * Tool result asking the model to call `tool` (default `edit_document`, the host's own tool name when it exposes the\n * operations under another one) again without the invalid operations' mistakes; worded in `locale` (default `de`).\n * By default nothing was applied and the model resends the whole list; with `partial` the host already shows the\n * valid operations, so the model resends only corrected versions of the listed ones.\n */\nexport function agentRetryMessage(\n problems: readonly AgentOpProblem[],\n scoped: boolean,\n { locale = 'de', tool = AGENT_TOOL, partial = false }: AgentTextOptions & { tool?: string, partial?: boolean } = {},\n): string {\n const text = AGENT_TEXTS[locale].retry\n const ids = [...new Set(problems.filter(p => p.reason !== 'inside').map(p => p.id).filter((id): id is string => !!id))]\n return [\n partial ? text.failedPartial : text.failed,\n ...(ids.length ? [text.invalidIds(ids.join(', '))] : []),\n ...problems.map(p => text.operation(p.index, p.type, p.detail)),\n ...(problems.some(p => p.reason === 'inside') ? [text.inside] : []),\n scoped ? text.scoped : text.unscoped,\n partial ? text.againPartial(tool) : text.again(tool),\n ].join('\\n')\n}\n\n/** Tool result of a past `edit_document` call: which operations were shown for review, and how many were dropped; worded in `locale` (default `de`). */\nexport function agentShownMessage(ops: readonly AgentOp[], dropped: number, { locale = 'de' }: AgentTextOptions = {}): string {\n const text = AGENT_TEXTS[locale].shown\n const lines = ops.length\n ? [text.shown(ops.map(op => `${op.op} (${agentOpSummary({ ...op, target: agentOpTarget(op) }, { locale })})`).join(', '))]\n : [text.none]\n if (dropped) lines.push(text.dropped(dropped))\n lines.push(text.decisions)\n return lines.join(' ')\n}\n\n/** „Entscheidungen zu deinen letzten Vorschlägen: op1 (update Block X) abgelehnt; …“ (English with `locale: 'en'`), or '' without feedback. */\nexport function agentFeedbackText(feedback: readonly AgentOpFeedback[], { locale = 'de' }: AgentTextOptions = {}): string {\n if (!feedback.length) return ''\n const outcome = AGENT_TEXTS[locale].outcome\n return AGENT_TEXTS[locale].feedback(feedback.map(f => `${f.op} (${agentOpSummary(f, { locale })}) ${outcome[f.outcome]}`).join('; '))\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
//#region src/features/ai/protocol/block-lines.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The line format of the document as the chat model sees it, in one place: how the lines are written (the primitives
|
|
4
|
+
* `documentChatText` formats its lines with), how they are read back (the block IDs of a text and which blocks lie
|
|
5
|
+
* inside which, ignoring fenced code), how an echoed line prefix is removed from the model's Markdown, and how the
|
|
6
|
+
* prompt describes all of that. Framework-free and free of ProseMirror: the browser writes the text, the server reads it.
|
|
7
|
+
*
|
|
8
|
+
* A line is `[<block id>] <prefix><text>`. The prefix carries list items (`- `, `1. `), quotes (`> `) and the nesting
|
|
9
|
+
* of a block inside a Baustein or host block (two spaces per level). A Baustein is a header line, its body lines and a
|
|
10
|
+
* closing line `:::` without ID. Code blocks keep their own lines: the first one carries the ID, the others start at
|
|
11
|
+
* the left edge, so their content can look like anything.
|
|
12
|
+
*/
|
|
13
|
+
/** The line shown instead of the rest of a document that did not fit. */
|
|
14
|
+
export declare const TRUNCATED_LINE = "[…] (Rest des Dokuments gekürzt)";
|
|
15
|
+
/** A line of block `id` (none: an ID-less line), indented by `prefix`, holding `text`. */
|
|
16
|
+
export declare function blockLine(id: string | null, prefix: string, text: string): string;
|
|
17
|
+
/** `markdown` on one line: hard breaks and wrapped lines would start new "blocks". */
|
|
18
|
+
export declare function singleBlockLine(markdown: string): string;
|
|
19
|
+
/** The prefix of blocks inside the block whose lines carry `prefix` (the body of a Baustein or host block). */
|
|
20
|
+
export declare function nestedLinePrefix(prefix: string): string;
|
|
21
|
+
/** The prefix of the blocks of a quote. */
|
|
22
|
+
export declare function quoteLinePrefix(prefix: string): string;
|
|
23
|
+
/** The prefix of the blocks of list item `number` (1-based; ignored in a bullet list). */
|
|
24
|
+
export declare function listLinePrefix(prefix: string, ordered: boolean, number: number): string;
|
|
25
|
+
/** The prefix of the blocks of a list item after its first: they continue under its marker instead of repeating it. */
|
|
26
|
+
export declare function continuedLinePrefix(prefix: string): string;
|
|
27
|
+
/** One chrome row of a Baustein (`## title`, `> description`, `Tags: #a`) without its block marker, on one line. */
|
|
28
|
+
export declare function compositeChromePart(markdown: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* The header text of a Baustein: `::: <kind> {meta}` (the meta as JSON only when it has entries), then every non-empty
|
|
31
|
+
* chrome part (title, description, tags) separated by ` | `.
|
|
32
|
+
*/
|
|
33
|
+
export declare function compositeLineHeader(kind: string, meta: unknown, chrome: readonly string[]): string;
|
|
34
|
+
/** The closing line of a Baustein whose header line is indented by `prefix`: no ID. */
|
|
35
|
+
export declare function compositeLineClose(prefix: string): string;
|
|
36
|
+
/** The block IDs of a document text and which blocks lie inside which. */
|
|
37
|
+
export interface BlockLines {
|
|
38
|
+
/** Every block ID that starts a line (lines inside fenced code do not count). */
|
|
39
|
+
ids: ReadonlySet<string>;
|
|
40
|
+
/** The IDs of all blocks nested below block `id`: the lines indented under its header line (empty for a block without nested blocks). */
|
|
41
|
+
below: (id: string) => ReadonlySet<string>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Reads the lines of `text` (as `documentChatText` writes them). Lines inside a fenced code block are code, never
|
|
45
|
+
* block lines, so a code line like `[ghost] text` does not become an ID; only a line that carries an ID opens a fence
|
|
46
|
+
* (a stray line of backticks inside the code cannot swallow what follows).
|
|
47
|
+
*
|
|
48
|
+
* Nesting follows the indentation: the blocks on the lines under a header line that are indented further belong to its
|
|
49
|
+
* block. Lines of list items (their marker, and what continues under it) never head a block of their own: a paragraph
|
|
50
|
+
* and its nested list are siblings, nothing lies "inside" the paragraph. A block without lines of its own (a list, a
|
|
51
|
+
* quote) has no nested blocks; a block's own ID on a nested line (a child without an ID of its own) is not "inside".
|
|
52
|
+
*/
|
|
53
|
+
export declare function readBlockLines(text: string): BlockLines;
|
|
54
|
+
/**
|
|
55
|
+
* Removes what a model echoes from the document lines into its Markdown: a `[<id>] ` prefix of a known block at the
|
|
56
|
+
* start of a line (not inside a fenced code block of its own Markdown, which keeps lines that look like one), and the
|
|
57
|
+
* indentation all non-blank lines share (lines echoed from a Baustein's body carry its indent, which would turn deeper
|
|
58
|
+
* nested lines into indented code).
|
|
59
|
+
*/
|
|
60
|
+
export declare function stripBlockLineEcho(markdown: string, known: (id: string) => boolean): string;
|
|
61
|
+
/**
|
|
62
|
+
* Removes the list and quote markers (`- `, `1. `, `> `) a model echoes from the lines of list items and quotes at the
|
|
63
|
+
* start of each line of its Markdown.
|
|
64
|
+
*/
|
|
65
|
+
export declare function stripListMarkerEcho(markdown: string): string;
|
|
66
|
+
/** The block ID of the prompt's example line (also the target of its citation example). */
|
|
67
|
+
export declare const BLOCK_LINE_EXAMPLE_ID = "0f3c9a52-7d1e-4b8a-9c2f-5e6d7a8b9c0d";
|
|
68
|
+
/** The text of the prompt's example line: a speaker line. */
|
|
69
|
+
export declare const BLOCK_LINE_EXAMPLE_TEXT = "**Anna:** Wir starten im Mai.";
|
|
70
|
+
/** The example line of the prompt, written by `blockLine` like every line of the document. */
|
|
71
|
+
export declare const BLOCK_LINE_EXAMPLE: string;
|
|
72
|
+
/** The label of the document in the user message. */
|
|
73
|
+
export declare const BLOCK_LINES_LABEL = "Dokument (aktueller Stand, eine Zeile pro Block, beginnend mit der Block-ID)";
|
|
74
|
+
/** What the first lines of the system prompt say about the document's lines. */
|
|
75
|
+
export declare const BLOCK_LINE_INTRO: string;
|
|
76
|
+
/** The list and quote blocks with the characters their lines carry. */
|
|
77
|
+
export declare const BLOCK_LINE_MARKERS: string;
|
|
78
|
+
/** What the prompt says about lists and quotes in the document. */
|
|
79
|
+
export declare const BLOCK_LINE_LIST_TEXT = "Listenpunkte und Zitate stehen nach der ID mit ihrem Zeichen; jeder Absatz darin hat eine eigene ID.";
|
|
80
|
+
/** What the prompt says about the lines of a host block with block content (a callout). */
|
|
81
|
+
export declare const BLOCK_LINE_HOST_TEXT: string;
|
|
82
|
+
/** What the prompt says about the lines of a Baustein. */
|
|
83
|
+
export declare const BLOCK_LINE_COMPOSITE_TEXT: string;
|
|
84
|
+
/** What the prompt says about the Markdown of an operation: no echo of the line prefix (`stripBlockLineEcho` removes it when it comes anyway). */
|
|
85
|
+
export declare const BLOCK_LINE_ECHO_TEXT = "„markdown“ enthält nur den neuen Inhalt, ohne Block-ID und ohne das Listen- oder Zitatzeichen der Zeile.";
|
|
86
|
+
/** What the retry message says about IDs. */
|
|
87
|
+
export declare const BLOCK_LINE_IDS_HINT = "Verwende nur IDs, die im Dokument in eckigen Klammern stehen; erfinde keine IDs.";
|
|
88
|
+
//#endregion
|
|
89
|
+
//# sourceMappingURL=block-lines.d.ts.map
|