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