@superdoc-dev/sdk 1.19.2 → 1.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action-primitives/doc-index.cjs +215 -0
- package/dist/action-primitives/doc-index.d.ts +83 -0
- package/dist/action-primitives/doc-index.d.ts.map +1 -0
- package/dist/action-primitives/doc-index.js +211 -0
- package/dist/action-primitives/engine.cjs +204 -0
- package/dist/action-primitives/engine.d.ts +71 -0
- package/dist/action-primitives/engine.d.ts.map +1 -0
- package/dist/action-primitives/engine.js +196 -0
- package/dist/action-primitives/receipt.cjs +39 -0
- package/dist/action-primitives/receipt.d.ts +49 -0
- package/dist/action-primitives/receipt.d.ts.map +1 -0
- package/dist/action-primitives/receipt.js +32 -0
- package/dist/action-primitives/resolve.cjs +252 -0
- package/dist/action-primitives/resolve.d.ts +56 -0
- package/dist/action-primitives/resolve.d.ts.map +1 -0
- package/dist/action-primitives/resolve.js +246 -0
- package/dist/action-primitives/session-cache.cjs +43 -0
- package/dist/action-primitives/session-cache.d.ts +19 -0
- package/dist/action-primitives/session-cache.d.ts.map +1 -0
- package/dist/action-primitives/session-cache.js +37 -0
- package/dist/action-primitives/tools/list-transform.cjs +661 -0
- package/dist/action-primitives/tools/list-transform.d.ts +98 -0
- package/dist/action-primitives/tools/list-transform.d.ts.map +1 -0
- package/dist/action-primitives/tools/list-transform.js +656 -0
- package/dist/action-primitives/tools/structure-insert.cjs +1343 -0
- package/dist/action-primitives/tools/structure-insert.d.ts +183 -0
- package/dist/action-primitives/tools/structure-insert.d.ts.map +1 -0
- package/dist/action-primitives/tools/structure-insert.js +1338 -0
- package/dist/action-primitives/tools/text-transform.cjs +669 -0
- package/dist/action-primitives/tools/text-transform.d.ts +64 -0
- package/dist/action-primitives/tools/text-transform.d.ts.map +1 -0
- package/dist/action-primitives/tools/text-transform.js +664 -0
- package/dist/action-primitives/types.d.ts +36 -0
- package/dist/action-primitives/types.d.ts.map +1 -0
- package/dist/action-primitives/types.js +15 -0
- package/dist/agent/actions.cjs +5381 -0
- package/dist/agent/actions.d.ts +404 -0
- package/dist/agent/actions.d.ts.map +1 -0
- package/dist/agent/actions.js +5373 -0
- package/dist/agent/catalog.cjs +483 -0
- package/dist/agent/catalog.d.ts +103 -0
- package/dist/agent/catalog.d.ts.map +1 -0
- package/dist/agent/catalog.js +471 -0
- package/dist/agent/doc-snapshot.cjs +663 -0
- package/dist/agent/doc-snapshot.d.ts +247 -0
- package/dist/agent/doc-snapshot.d.ts.map +1 -0
- package/dist/agent/doc-snapshot.js +657 -0
- package/dist/agent/index.d.ts +16 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +15 -0
- package/dist/agent/ir.cjs +170 -0
- package/dist/agent/ir.d.ts +216 -0
- package/dist/agent/ir.d.ts.map +1 -0
- package/dist/agent/ir.js +181 -0
- package/dist/agent/operation-catalog.cjs +418 -0
- package/dist/agent/operation-catalog.d.ts +36 -0
- package/dist/agent/operation-catalog.d.ts.map +1 -0
- package/dist/agent/operation-catalog.js +446 -0
- package/dist/agent/runtime.cjs +501 -0
- package/dist/agent/runtime.d.ts +120 -0
- package/dist/agent/runtime.d.ts.map +1 -0
- package/dist/agent/runtime.js +493 -0
- package/dist/embedded-prompts.generated.cjs +13 -0
- package/dist/embedded-prompts.generated.d.ts +4 -0
- package/dist/embedded-prompts.generated.d.ts.map +1 -0
- package/dist/embedded-prompts.generated.js +9 -0
- package/dist/generated/client.cjs +18 -0
- package/dist/generated/client.d.ts +134 -0
- package/dist/generated/client.d.ts.map +1 -1
- package/dist/generated/client.js +18 -0
- package/dist/generated/contract.cjs +622 -3
- package/dist/generated/contract.d.ts.map +1 -1
- package/dist/generated/contract.js +622 -3
- package/dist/index.cjs +3 -7
- package/dist/index.d.ts +8 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -8
- package/dist/presets/core.cjs +454 -0
- package/dist/presets/core.d.ts +20 -0
- package/dist/presets/core.d.ts.map +1 -0
- package/dist/presets/core.js +447 -0
- package/dist/presets.cjs +55 -8
- package/dist/presets.d.ts +39 -8
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +53 -8
- package/dist/prompts/mcp-prompt.md +23 -0
- package/dist/prompts/system-prompt.md +108 -0
- package/dist/runtime/transport-common.cjs +8 -0
- package/dist/runtime/transport-common.d.ts.map +1 -1
- package/dist/runtime/transport-common.js +8 -0
- package/dist/tools.cjs +46 -11
- package/dist/tools.d.ts +55 -8
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +45 -13
- package/package.json +9 -8
- package/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- package/tools/__pycache__/intent_dispatch_generated.cpython-312.pyc +0 -0
- package/tools/catalog.json +14 -0
- package/tools/tools-policy.json +1 -1
- package/tools/tools.anthropic.json +14 -0
- package/tools/tools.generic.json +14 -0
- package/tools/tools.openai.json +14 -0
- package/tools/tools.vercel.json +14 -0
|
@@ -0,0 +1,661 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var engine = require('../engine.cjs');
|
|
4
|
+
var resolve = require('../resolve.cjs');
|
|
5
|
+
|
|
6
|
+
const LIST_TRANSFORM_ACTIONS = ['insert_many', 'append_new_list'];
|
|
7
|
+
const CHANGE_MODES = ['direct', 'tracked'];
|
|
8
|
+
const POSITIONS = ['before', 'after'];
|
|
9
|
+
const LIST_KINDS = ['bullet', 'ordered'];
|
|
10
|
+
const LIST_PAGE_LIMIT = 250;
|
|
11
|
+
function parseAction(raw) {
|
|
12
|
+
if (typeof raw !== 'string') {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
return LIST_TRANSFORM_ACTIONS.find((action) => action === raw);
|
|
16
|
+
}
|
|
17
|
+
function parsePosition(raw) {
|
|
18
|
+
if (raw == null) {
|
|
19
|
+
return 'after';
|
|
20
|
+
}
|
|
21
|
+
if (typeof raw !== 'string') {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
return POSITIONS.find((position) => position === raw);
|
|
25
|
+
}
|
|
26
|
+
function parseChangeMode(raw) {
|
|
27
|
+
if (raw == null) {
|
|
28
|
+
return 'direct';
|
|
29
|
+
}
|
|
30
|
+
if (typeof raw !== 'string') {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
return CHANGE_MODES.find((mode) => mode === raw);
|
|
34
|
+
}
|
|
35
|
+
function parseListKind(raw, fallback) {
|
|
36
|
+
if (raw == null) {
|
|
37
|
+
return fallback;
|
|
38
|
+
}
|
|
39
|
+
if (typeof raw !== 'string') {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
return LIST_KINDS.find((kind) => kind === raw);
|
|
43
|
+
}
|
|
44
|
+
function parsePreset(raw, kind) {
|
|
45
|
+
if (typeof raw === 'string' && raw.length > 0) {
|
|
46
|
+
return raw;
|
|
47
|
+
}
|
|
48
|
+
return kind === 'bullet' ? 'disc' : 'decimal';
|
|
49
|
+
}
|
|
50
|
+
function parseItems(raw) {
|
|
51
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
const values = [];
|
|
55
|
+
for (const entry of raw) {
|
|
56
|
+
if (typeof entry !== 'string' || entry.length === 0) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
values.push(entry);
|
|
60
|
+
}
|
|
61
|
+
return values;
|
|
62
|
+
}
|
|
63
|
+
function parseOptionalText(raw) {
|
|
64
|
+
if (raw == null) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
if (typeof raw !== 'string') {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
const trimmed = raw.trim();
|
|
71
|
+
return trimmed.length === 0 ? undefined : trimmed;
|
|
72
|
+
}
|
|
73
|
+
function parseHeadingLevel(raw) {
|
|
74
|
+
if (raw == null) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
if (typeof raw !== 'number' || !Number.isInteger(raw) || raw < 1 || raw > 6) {
|
|
78
|
+
return Number.NaN;
|
|
79
|
+
}
|
|
80
|
+
return raw;
|
|
81
|
+
}
|
|
82
|
+
function resolveTargetAddress(context, resolvedTarget) {
|
|
83
|
+
if (resolvedTarget.entity.kind === 'listItem') {
|
|
84
|
+
return engine.workflowStepSuccess({
|
|
85
|
+
target: {
|
|
86
|
+
kind: 'block',
|
|
87
|
+
nodeType: 'listItem',
|
|
88
|
+
nodeId: resolvedTarget.entity.nodeId,
|
|
89
|
+
},
|
|
90
|
+
listId: resolvedTarget.entity.listId,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (resolvedTarget.entity.kind === 'block' && resolvedTarget.entity.nodeType === 'listItem') {
|
|
94
|
+
const listItem = context.index.lists.find((entry) => entry.nodeId === resolvedTarget.entity.nodeId);
|
|
95
|
+
return engine.workflowStepSuccess({
|
|
96
|
+
target: {
|
|
97
|
+
kind: 'block',
|
|
98
|
+
nodeType: 'listItem',
|
|
99
|
+
nodeId: resolvedTarget.entity.nodeId,
|
|
100
|
+
},
|
|
101
|
+
listId: listItem?.listId,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return engine.workflowStepFailure({
|
|
105
|
+
status: 'failed',
|
|
106
|
+
phase: 'resolve',
|
|
107
|
+
code: 'WORKFLOW_TARGET_KIND_UNSUPPORTED',
|
|
108
|
+
message: `superdoc_list_transform requires a list item target but resolved to ${resolvedTarget.entity.kind}.`,
|
|
109
|
+
details: {
|
|
110
|
+
entityKind: resolvedTarget.entity.kind,
|
|
111
|
+
...(resolvedTarget.entity.kind === 'block' ? { nodeType: resolvedTarget.entity.nodeType } : {}),
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function resolveSingleListAppendTarget(context) {
|
|
116
|
+
const listIds = new Map();
|
|
117
|
+
for (const item of context.index.lists) {
|
|
118
|
+
const existing = listIds.get(item.listId);
|
|
119
|
+
if (existing == null) {
|
|
120
|
+
listIds.set(item.listId, [item]);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
existing.push(item);
|
|
124
|
+
}
|
|
125
|
+
if (listIds.size === 0) {
|
|
126
|
+
return engine.workflowStepSuccess({
|
|
127
|
+
target: undefined,
|
|
128
|
+
listId: undefined,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const sortedAllItems = [...context.index.lists].sort((left, right) => {
|
|
132
|
+
const leftOrder = left.apiOrdinal ?? left.indexOrdinal;
|
|
133
|
+
const rightOrder = right.apiOrdinal ?? right.indexOrdinal;
|
|
134
|
+
return leftOrder - rightOrder;
|
|
135
|
+
});
|
|
136
|
+
const firstListId = sortedAllItems[0]?.listId;
|
|
137
|
+
const onlyList = firstListId == null ? undefined : [firstListId, listIds.get(firstListId) ?? []];
|
|
138
|
+
if (onlyList == null) {
|
|
139
|
+
return engine.workflowStepFailure({
|
|
140
|
+
status: 'failed',
|
|
141
|
+
phase: 'resolve',
|
|
142
|
+
code: 'WORKFLOW_LIST_TARGET_REQUIRED',
|
|
143
|
+
message: 'superdoc_list_transform requires target when no list items are indexed.',
|
|
144
|
+
details: {
|
|
145
|
+
listCount: 0,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const [listId, items] = onlyList;
|
|
150
|
+
if (items.length === 0) {
|
|
151
|
+
return engine.workflowStepFailure({
|
|
152
|
+
status: 'failed',
|
|
153
|
+
phase: 'resolve',
|
|
154
|
+
code: 'WORKFLOW_LIST_TARGET_REQUIRED',
|
|
155
|
+
message: 'superdoc_list_transform requires target when the only list has no addressable items.',
|
|
156
|
+
details: {
|
|
157
|
+
listId,
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
const sorted = [...items].sort((left, right) => {
|
|
162
|
+
const leftOrder = left.apiOrdinal ?? left.indexOrdinal;
|
|
163
|
+
const rightOrder = right.apiOrdinal ?? right.indexOrdinal;
|
|
164
|
+
return leftOrder - rightOrder;
|
|
165
|
+
});
|
|
166
|
+
const tail = sorted[sorted.length - 1];
|
|
167
|
+
if (tail == null) {
|
|
168
|
+
return engine.workflowStepFailure({
|
|
169
|
+
status: 'failed',
|
|
170
|
+
phase: 'resolve',
|
|
171
|
+
code: 'WORKFLOW_LIST_TARGET_REQUIRED',
|
|
172
|
+
message: 'superdoc_list_transform could not identify a list tail item for append.',
|
|
173
|
+
details: { listId },
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return engine.workflowStepSuccess({
|
|
177
|
+
target: {
|
|
178
|
+
kind: 'block',
|
|
179
|
+
nodeType: 'listItem',
|
|
180
|
+
nodeId: tail.nodeId,
|
|
181
|
+
},
|
|
182
|
+
listId,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
const TEXTUAL_ORDERED_LIST_PATTERN = /^\s*(?:\(\d+\)|\d+[.)]|[A-Z][.)])\s+\S/;
|
|
186
|
+
const TEXTUAL_BULLET_LIST_PATTERN = /^\s*(?:[-*•‣◦])\s+\S/;
|
|
187
|
+
function detectApparentTextualListTail(context, kind) {
|
|
188
|
+
const pattern = kind === 'bullet' ? TEXTUAL_BULLET_LIST_PATTERN : TEXTUAL_ORDERED_LIST_PATTERN;
|
|
189
|
+
const candidateBlocks = context.index.blocks.filter((block) => {
|
|
190
|
+
if (block.nodeType !== 'paragraph' && block.nodeType !== 'heading' && block.nodeType !== 'listItem') {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
return typeof block.textPreview === 'string' && pattern.test(block.textPreview);
|
|
194
|
+
});
|
|
195
|
+
if (candidateBlocks.length === 0) {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
let bestRun = [];
|
|
199
|
+
let currentRun = [];
|
|
200
|
+
let previousOrdinal;
|
|
201
|
+
for (const block of candidateBlocks) {
|
|
202
|
+
if (previousOrdinal == null || block.ordinal === previousOrdinal + 1) {
|
|
203
|
+
currentRun.push(block);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
if (currentRun.length > bestRun.length) {
|
|
207
|
+
bestRun = currentRun;
|
|
208
|
+
}
|
|
209
|
+
currentRun = [block];
|
|
210
|
+
}
|
|
211
|
+
previousOrdinal = block.ordinal;
|
|
212
|
+
}
|
|
213
|
+
if (currentRun.length > bestRun.length) {
|
|
214
|
+
bestRun = currentRun;
|
|
215
|
+
}
|
|
216
|
+
const tail = (bestRun.length >= 2 ? bestRun : candidateBlocks)[(bestRun.length >= 2 ? bestRun : candidateBlocks).length - 1];
|
|
217
|
+
if (tail == null || (tail.nodeType !== 'paragraph' && tail.nodeType !== 'heading' && tail.nodeType !== 'listItem')) {
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
kind: 'block',
|
|
222
|
+
nodeType: tail.nodeType,
|
|
223
|
+
nodeId: tail.nodeId,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function resolveStep(context) {
|
|
227
|
+
const action = parseAction(context.args.action);
|
|
228
|
+
if (action == null) {
|
|
229
|
+
return engine.workflowStepFailure({
|
|
230
|
+
status: 'failed',
|
|
231
|
+
phase: 'resolve',
|
|
232
|
+
code: 'WORKFLOW_LIST_TRANSFORM_ACTION_INVALID',
|
|
233
|
+
message: 'superdoc_list_transform requires action to be "insert_many" or "append_new_list".',
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
const items = parseItems(context.args.items);
|
|
237
|
+
if (items == null) {
|
|
238
|
+
return engine.workflowStepFailure({
|
|
239
|
+
status: 'failed',
|
|
240
|
+
phase: 'resolve',
|
|
241
|
+
code: 'WORKFLOW_LIST_ITEMS_REQUIRED',
|
|
242
|
+
message: 'insert_many requires a non-empty items array of non-empty strings.',
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
const listKind = parseListKind(context.args.kind ?? context.args.listKind, action === 'append_new_list' ? 'bullet' : 'ordered');
|
|
246
|
+
if (listKind == null) {
|
|
247
|
+
return engine.workflowStepFailure({
|
|
248
|
+
status: 'failed',
|
|
249
|
+
phase: 'resolve',
|
|
250
|
+
code: 'WORKFLOW_LIST_KIND_INVALID',
|
|
251
|
+
message: 'kind/listKind must be "bullet" or "ordered".',
|
|
252
|
+
details: { received: context.args.kind ?? context.args.listKind },
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
const preset = parsePreset(context.args.preset, listKind);
|
|
256
|
+
const headingText = parseOptionalText(context.args.headingText);
|
|
257
|
+
const headingLevel = parseHeadingLevel(context.args.headingLevel);
|
|
258
|
+
if (Number.isNaN(headingLevel)) {
|
|
259
|
+
return engine.workflowStepFailure({
|
|
260
|
+
status: 'failed',
|
|
261
|
+
phase: 'resolve',
|
|
262
|
+
code: 'WORKFLOW_LIST_HEADING_LEVEL_INVALID',
|
|
263
|
+
message: 'headingLevel must be an integer from 1 to 6 when provided.',
|
|
264
|
+
details: { received: context.args.headingLevel },
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
const changeMode = parseChangeMode(context.args.changeMode);
|
|
268
|
+
if (changeMode == null) {
|
|
269
|
+
return engine.workflowStepFailure({
|
|
270
|
+
status: 'failed',
|
|
271
|
+
phase: 'resolve',
|
|
272
|
+
code: 'WORKFLOW_LIST_CHANGE_MODE_INVALID',
|
|
273
|
+
message: 'changeMode must be "direct" or "tracked".',
|
|
274
|
+
details: { received: context.args.changeMode },
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
const position = parsePosition(context.args.position);
|
|
278
|
+
if (position == null) {
|
|
279
|
+
return engine.workflowStepFailure({
|
|
280
|
+
status: 'failed',
|
|
281
|
+
phase: 'resolve',
|
|
282
|
+
code: 'WORKFLOW_LIST_POSITION_INVALID',
|
|
283
|
+
message: 'position must be "before" or "after".',
|
|
284
|
+
details: { received: context.args.position },
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
if (action === 'append_new_list') {
|
|
288
|
+
return engine.workflowStepSuccess({
|
|
289
|
+
action,
|
|
290
|
+
items,
|
|
291
|
+
changeMode,
|
|
292
|
+
position: 'after',
|
|
293
|
+
createNewList: true,
|
|
294
|
+
targetSource: 'document_end',
|
|
295
|
+
deterministicTarget: true,
|
|
296
|
+
listKind,
|
|
297
|
+
preset,
|
|
298
|
+
headingText,
|
|
299
|
+
headingLevel,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
if (headingText != null || headingLevel != null) {
|
|
303
|
+
return engine.workflowStepFailure({
|
|
304
|
+
status: 'failed',
|
|
305
|
+
phase: 'resolve',
|
|
306
|
+
code: 'WORKFLOW_LIST_HEADING_UNSUPPORTED',
|
|
307
|
+
message: 'headingText/headingLevel are only supported for action "append_new_list".',
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
if (context.args.target == null) {
|
|
311
|
+
if (position === 'before') {
|
|
312
|
+
return engine.workflowStepFailure({
|
|
313
|
+
status: 'failed',
|
|
314
|
+
phase: 'resolve',
|
|
315
|
+
code: 'WORKFLOW_LIST_TARGET_REQUIRED',
|
|
316
|
+
message: 'position "before" requires an explicit deterministic target for superdoc_list_transform.',
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
const autoTarget = resolveSingleListAppendTarget(context);
|
|
320
|
+
if (!autoTarget.ok) {
|
|
321
|
+
return autoTarget;
|
|
322
|
+
}
|
|
323
|
+
return engine.workflowStepSuccess({
|
|
324
|
+
action,
|
|
325
|
+
items,
|
|
326
|
+
changeMode,
|
|
327
|
+
position: 'after',
|
|
328
|
+
target: autoTarget.value.target,
|
|
329
|
+
createAfterTarget: autoTarget.value.target == null ? detectApparentTextualListTail(context, listKind) : undefined,
|
|
330
|
+
createNewList: autoTarget.value.target == null,
|
|
331
|
+
targetSource: autoTarget.value.target == null && detectApparentTextualListTail(context, listKind) != null
|
|
332
|
+
? 'apparent_textual_list'
|
|
333
|
+
: 'auto_single_list',
|
|
334
|
+
deterministicTarget: false,
|
|
335
|
+
listKind,
|
|
336
|
+
preset,
|
|
337
|
+
listId: autoTarget.value.listId,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
const resolved = resolve.resolveWorkflowTargetFromUnknown(context.index, context.args.target);
|
|
341
|
+
if (!resolved.ok) {
|
|
342
|
+
if (position === 'after') {
|
|
343
|
+
const createAfterTarget = detectApparentTextualListTail(context, listKind);
|
|
344
|
+
if (createAfterTarget != null) {
|
|
345
|
+
return engine.workflowStepSuccess({
|
|
346
|
+
action,
|
|
347
|
+
items,
|
|
348
|
+
changeMode,
|
|
349
|
+
position,
|
|
350
|
+
createAfterTarget,
|
|
351
|
+
createNewList: true,
|
|
352
|
+
targetSource: 'apparent_textual_list',
|
|
353
|
+
deterministicTarget: false,
|
|
354
|
+
listKind,
|
|
355
|
+
preset,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return engine.workflowStepFailure({
|
|
360
|
+
status: 'failed',
|
|
361
|
+
phase: 'resolve',
|
|
362
|
+
code: `WORKFLOW_${resolved.code}`,
|
|
363
|
+
message: resolved.message,
|
|
364
|
+
details: {
|
|
365
|
+
targetArgKey: 'target',
|
|
366
|
+
...resolved.details,
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
const targetAddress = resolveTargetAddress(context, resolved.target);
|
|
371
|
+
if (!targetAddress.ok) {
|
|
372
|
+
return targetAddress;
|
|
373
|
+
}
|
|
374
|
+
return engine.workflowStepSuccess({
|
|
375
|
+
action,
|
|
376
|
+
items,
|
|
377
|
+
changeMode,
|
|
378
|
+
position,
|
|
379
|
+
target: targetAddress.value.target,
|
|
380
|
+
createNewList: false,
|
|
381
|
+
targetSource: 'provided',
|
|
382
|
+
deterministicTarget: true,
|
|
383
|
+
listKind,
|
|
384
|
+
preset,
|
|
385
|
+
listId: targetAddress.value.listId,
|
|
386
|
+
request: resolved.request,
|
|
387
|
+
resolvedTarget: resolved.target,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
function planStep(_context, resolved) {
|
|
391
|
+
if (resolved.items.length === 0) {
|
|
392
|
+
return engine.workflowStepFailure({
|
|
393
|
+
status: 'failed',
|
|
394
|
+
phase: 'plan',
|
|
395
|
+
code: 'WORKFLOW_LIST_TRANSFORM_STEP_BUILD_FAILED',
|
|
396
|
+
message: 'insert_many requires at least one item.',
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
return engine.workflowStepSuccess({
|
|
400
|
+
action: resolved.action,
|
|
401
|
+
changeMode: resolved.changeMode,
|
|
402
|
+
position: resolved.position,
|
|
403
|
+
target: resolved.target,
|
|
404
|
+
createAfterTarget: resolved.createAfterTarget,
|
|
405
|
+
createNewList: resolved.createNewList,
|
|
406
|
+
items: resolved.items,
|
|
407
|
+
stepCount: resolved.items.length,
|
|
408
|
+
targetSource: resolved.targetSource,
|
|
409
|
+
deterministicTarget: resolved.deterministicTarget,
|
|
410
|
+
listKind: resolved.listKind,
|
|
411
|
+
preset: resolved.preset,
|
|
412
|
+
headingText: resolved.headingText,
|
|
413
|
+
headingLevel: resolved.headingLevel,
|
|
414
|
+
listId: resolved.listId,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
async function executeStep(context, _resolved, plan) {
|
|
418
|
+
let target = plan.target;
|
|
419
|
+
const inserts = [];
|
|
420
|
+
const insertedNodeIds = [];
|
|
421
|
+
const trackedChangeRefs = [];
|
|
422
|
+
if (plan.createNewList) {
|
|
423
|
+
let previous;
|
|
424
|
+
let headingNodeId;
|
|
425
|
+
const headingDetachedFromList = false;
|
|
426
|
+
const headingDirectFormattingReset = false;
|
|
427
|
+
if (plan.createAfterTarget != null) {
|
|
428
|
+
previous =
|
|
429
|
+
plan.createAfterTarget.nodeType === 'listItem'
|
|
430
|
+
? undefined
|
|
431
|
+
: {
|
|
432
|
+
kind: 'block',
|
|
433
|
+
nodeType: plan.createAfterTarget.nodeType,
|
|
434
|
+
nodeId: plan.createAfterTarget.nodeId,
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
if (plan.headingText != null) {
|
|
438
|
+
// Generated list labels must stay outside list/outline numbering. In
|
|
439
|
+
// template-heavy legal docs, HeadingN styles often carry numbering, so a
|
|
440
|
+
// plain paragraph is safer than doc.create.heading for this workflow.
|
|
441
|
+
const result = await context.documentHandle.create.paragraph({
|
|
442
|
+
text: plan.headingText,
|
|
443
|
+
changeMode: plan.changeMode,
|
|
444
|
+
at: previous == null
|
|
445
|
+
? { kind: 'documentEnd' }
|
|
446
|
+
: {
|
|
447
|
+
kind: 'after',
|
|
448
|
+
target: previous,
|
|
449
|
+
},
|
|
450
|
+
}, context.invokeOptions);
|
|
451
|
+
headingNodeId = result.paragraph.nodeId;
|
|
452
|
+
previous = {
|
|
453
|
+
kind: 'block',
|
|
454
|
+
nodeType: 'paragraph',
|
|
455
|
+
nodeId: result.paragraph.nodeId,
|
|
456
|
+
};
|
|
457
|
+
trackedChangeRefs.push(...(result.trackedChangeRefs ?? []));
|
|
458
|
+
}
|
|
459
|
+
const [firstText, ...remainingItems] = plan.items;
|
|
460
|
+
if (firstText == null) {
|
|
461
|
+
return engine.workflowStepFailure({
|
|
462
|
+
status: 'failed',
|
|
463
|
+
phase: 'execute',
|
|
464
|
+
code: 'WORKFLOW_LIST_TRANSFORM_STEP_BUILD_FAILED',
|
|
465
|
+
message: 'append_new_list requires at least one item.',
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
const firstParagraphResult = await context.documentHandle.create.paragraph({
|
|
469
|
+
text: firstText,
|
|
470
|
+
changeMode: plan.changeMode,
|
|
471
|
+
at: previous == null
|
|
472
|
+
? { kind: 'documentEnd' }
|
|
473
|
+
: {
|
|
474
|
+
kind: 'after',
|
|
475
|
+
target: previous,
|
|
476
|
+
},
|
|
477
|
+
}, context.invokeOptions);
|
|
478
|
+
const firstParagraph = {
|
|
479
|
+
kind: 'block',
|
|
480
|
+
nodeType: 'paragraph',
|
|
481
|
+
nodeId: firstParagraphResult.paragraph.nodeId,
|
|
482
|
+
};
|
|
483
|
+
const createdList = await context.documentHandle.lists.create({
|
|
484
|
+
mode: 'fromParagraphs',
|
|
485
|
+
target: firstParagraph,
|
|
486
|
+
kind: plan.listKind,
|
|
487
|
+
preset: plan.preset,
|
|
488
|
+
sequence: { mode: 'new' },
|
|
489
|
+
}, context.invokeOptions);
|
|
490
|
+
let appendTarget = createdList.item;
|
|
491
|
+
const firstRefs = firstParagraphResult.trackedChangeRefs ?? [];
|
|
492
|
+
trackedChangeRefs.push(...firstRefs);
|
|
493
|
+
insertedNodeIds.push(appendTarget.nodeId);
|
|
494
|
+
inserts.push({
|
|
495
|
+
text: firstText,
|
|
496
|
+
nodeId: appendTarget.nodeId,
|
|
497
|
+
trackedChangeRefIds: firstRefs.map((ref) => ref.entityId),
|
|
498
|
+
});
|
|
499
|
+
for (const text of remainingItems) {
|
|
500
|
+
const result = await context.documentHandle.lists.insert({
|
|
501
|
+
target: appendTarget,
|
|
502
|
+
position: 'after',
|
|
503
|
+
text,
|
|
504
|
+
changeMode: plan.changeMode,
|
|
505
|
+
}, context.invokeOptions);
|
|
506
|
+
appendTarget = result.item;
|
|
507
|
+
insertedNodeIds.push(result.item.nodeId);
|
|
508
|
+
const refs = result.trackedChangeRefs ?? [];
|
|
509
|
+
trackedChangeRefs.push(...refs);
|
|
510
|
+
inserts.push({
|
|
511
|
+
text,
|
|
512
|
+
nodeId: result.item.nodeId,
|
|
513
|
+
trackedChangeRefIds: refs.map((ref) => ref.entityId),
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
return engine.workflowStepSuccess({
|
|
517
|
+
action: plan.action,
|
|
518
|
+
changeMode: plan.changeMode,
|
|
519
|
+
position: plan.position,
|
|
520
|
+
targetSource: plan.targetSource,
|
|
521
|
+
deterministicTarget: plan.deterministicTarget,
|
|
522
|
+
listKind: plan.listKind,
|
|
523
|
+
preset: plan.preset,
|
|
524
|
+
headingText: plan.headingText,
|
|
525
|
+
headingLevel: plan.headingLevel,
|
|
526
|
+
headingNodeId,
|
|
527
|
+
headingDirectFormattingReset,
|
|
528
|
+
headingDetachedFromList,
|
|
529
|
+
listId: createdList.listId,
|
|
530
|
+
insertedCount: inserts.length,
|
|
531
|
+
insertedNodeIds,
|
|
532
|
+
inserts,
|
|
533
|
+
trackedChangeRefs,
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
if (target == null) {
|
|
537
|
+
return engine.workflowStepFailure({
|
|
538
|
+
status: 'failed',
|
|
539
|
+
phase: 'execute',
|
|
540
|
+
code: 'WORKFLOW_LIST_TARGET_REQUIRED',
|
|
541
|
+
message: 'superdoc_list_transform could not resolve an existing list target.',
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
for (const text of plan.items) {
|
|
545
|
+
const result = await context.documentHandle.lists.insert({
|
|
546
|
+
target,
|
|
547
|
+
position: plan.position,
|
|
548
|
+
text,
|
|
549
|
+
changeMode: plan.changeMode,
|
|
550
|
+
}, context.invokeOptions);
|
|
551
|
+
insertedNodeIds.push(result.item.nodeId);
|
|
552
|
+
const refs = result.trackedChangeRefs ?? [];
|
|
553
|
+
trackedChangeRefs.push(...refs);
|
|
554
|
+
inserts.push({
|
|
555
|
+
text,
|
|
556
|
+
nodeId: result.item.nodeId,
|
|
557
|
+
trackedChangeRefIds: refs.map((ref) => ref.entityId),
|
|
558
|
+
});
|
|
559
|
+
if (plan.position === 'after') {
|
|
560
|
+
target = result.item;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return engine.workflowStepSuccess({
|
|
564
|
+
action: plan.action,
|
|
565
|
+
changeMode: plan.changeMode,
|
|
566
|
+
position: plan.position,
|
|
567
|
+
targetSource: plan.targetSource,
|
|
568
|
+
deterministicTarget: plan.deterministicTarget,
|
|
569
|
+
listKind: plan.listKind,
|
|
570
|
+
preset: plan.preset,
|
|
571
|
+
headingText: plan.headingText,
|
|
572
|
+
headingLevel: plan.headingLevel,
|
|
573
|
+
listId: plan.listId,
|
|
574
|
+
insertedCount: inserts.length,
|
|
575
|
+
insertedNodeIds,
|
|
576
|
+
inserts,
|
|
577
|
+
trackedChangeRefs,
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
async function listAllListItems(documentHandle, invokeOptions) {
|
|
581
|
+
const items = [];
|
|
582
|
+
let offset = 0;
|
|
583
|
+
while (true) {
|
|
584
|
+
const page = await documentHandle.lists.list({ offset, limit: LIST_PAGE_LIMIT }, invokeOptions);
|
|
585
|
+
items.push(...page.items);
|
|
586
|
+
offset += page.items.length;
|
|
587
|
+
if (page.items.length === 0 || offset >= page.total) {
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return items;
|
|
592
|
+
}
|
|
593
|
+
async function verifyStep(context, _resolved, _plan, execution) {
|
|
594
|
+
const postListInventory = await listAllListItems(context.documentHandle, context.invokeOptions);
|
|
595
|
+
const byNodeId = new Map(postListInventory.map((item) => [item.address.nodeId, item]));
|
|
596
|
+
let insertedPresent = 0;
|
|
597
|
+
let textsVerified = 0;
|
|
598
|
+
for (const insert of execution.inserts) {
|
|
599
|
+
const listed = byNodeId.get(insert.nodeId);
|
|
600
|
+
if (listed != null) {
|
|
601
|
+
insertedPresent += 1;
|
|
602
|
+
if (listed.text === insert.text) {
|
|
603
|
+
textsVerified += 1;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
let headingVerified;
|
|
608
|
+
if (execution.headingText != null) {
|
|
609
|
+
const currentText = await context.documentHandle.getText({}, context.invokeOptions);
|
|
610
|
+
headingVerified = currentText.includes(execution.headingText);
|
|
611
|
+
}
|
|
612
|
+
const insertedExpected = execution.inserts.length;
|
|
613
|
+
const textsExpected = execution.inserts.length;
|
|
614
|
+
const passed = insertedPresent === insertedExpected && textsVerified === textsExpected && (headingVerified ?? true);
|
|
615
|
+
const summary = `list insert checks nodeIds=${insertedPresent}/${insertedExpected} texts=${textsVerified}/${textsExpected}; headingVerified=${headingVerified ?? 'n/a'}; trackedRefs=${execution.trackedChangeRefs.length}.`;
|
|
616
|
+
if (!passed) {
|
|
617
|
+
return engine.workflowStepFailure({
|
|
618
|
+
status: 'failed',
|
|
619
|
+
phase: 'verify',
|
|
620
|
+
code: 'WORKFLOW_LIST_TRANSFORM_VERIFICATION_FAILED',
|
|
621
|
+
message: 'superdoc_list_transform verification failed.',
|
|
622
|
+
details: {
|
|
623
|
+
summary,
|
|
624
|
+
insertedPresent,
|
|
625
|
+
insertedExpected,
|
|
626
|
+
textsVerified,
|
|
627
|
+
textsExpected,
|
|
628
|
+
headingVerified,
|
|
629
|
+
},
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
return engine.workflowStepSuccess({
|
|
633
|
+
action: execution.action,
|
|
634
|
+
passed,
|
|
635
|
+
summary,
|
|
636
|
+
checks: {
|
|
637
|
+
insertedPresent,
|
|
638
|
+
insertedExpected,
|
|
639
|
+
textsVerified,
|
|
640
|
+
textsExpected,
|
|
641
|
+
headingVerified,
|
|
642
|
+
trackedChangeRefs: execution.trackedChangeRefs.length,
|
|
643
|
+
},
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
async function runSuperdocListTransformWorkflow(input) {
|
|
647
|
+
return engine.runWorkflowEngine({
|
|
648
|
+
documentHandle: input.documentHandle,
|
|
649
|
+
toolName: 'superdoc_list_transform',
|
|
650
|
+
args: input.args,
|
|
651
|
+
invokeOptions: input.invokeOptions,
|
|
652
|
+
hooks: {
|
|
653
|
+
resolve: async (context) => resolveStep(context),
|
|
654
|
+
plan: async (context, resolved) => planStep(context, resolved),
|
|
655
|
+
execute: async (context, resolved, plan) => executeStep(context, resolved, plan),
|
|
656
|
+
verify: async (context, resolved, plan, execution) => verifyStep(context, resolved, plan, execution),
|
|
657
|
+
},
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
exports.runSuperdocListTransformWorkflow = runSuperdocListTransformWorkflow;
|