@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,669 @@
1
+ 'use strict';
2
+
3
+ var engine = require('../engine.cjs');
4
+ var resolve = require('../resolve.cjs');
5
+
6
+ const TEXT_TRANSFORM_ACTIONS = ['replace_all', 'delete_all', 'rewrite_block', 'fill_placeholders'];
7
+ const CHANGE_MODES = ['direct', 'tracked'];
8
+ const REWRITE_BLOCK_SUPPORTED_NODE_TYPES = ['paragraph', 'heading', 'listItem'];
9
+ function isObjectRecord(value) {
10
+ return value != null && typeof value === 'object' && !Array.isArray(value);
11
+ }
12
+ function coerceBoolean(raw, fallback) {
13
+ if (typeof raw === 'boolean') {
14
+ return raw;
15
+ }
16
+ return fallback;
17
+ }
18
+ function parseAction(raw) {
19
+ if (typeof raw !== 'string') {
20
+ return undefined;
21
+ }
22
+ return TEXT_TRANSFORM_ACTIONS.find((action) => action === raw);
23
+ }
24
+ function parseChangeMode(raw) {
25
+ if (raw == null) {
26
+ return 'direct';
27
+ }
28
+ if (typeof raw !== 'string') {
29
+ return undefined;
30
+ }
31
+ return CHANGE_MODES.find((mode) => mode === raw);
32
+ }
33
+ function parseText(raw) {
34
+ if (typeof raw !== 'string') {
35
+ return undefined;
36
+ }
37
+ if (raw.length === 0) {
38
+ return undefined;
39
+ }
40
+ return raw;
41
+ }
42
+ function textIncludes(text, pattern, caseSensitive) {
43
+ if (caseSensitive) {
44
+ return text.includes(pattern);
45
+ }
46
+ return text.toLowerCase().includes(pattern.toLowerCase());
47
+ }
48
+ const DATE_CANDIDATE_PATTERN = /\b(?:\d{1,2}\s+[A-Z][a-z]+\s+\d{4}|[A-Z][a-z]+\s+\d{1,2},\s*\d{4}|\d{4}-\d{2}-\d{2}|\d{1,2}\/\d{1,2}\/\d{2,4})\b/g;
49
+ function dateLikePattern(text) {
50
+ DATE_CANDIDATE_PATTERN.lastIndex = 0;
51
+ const matched = DATE_CANDIDATE_PATTERN.test(text.trim());
52
+ DATE_CANDIDATE_PATTERN.lastIndex = 0;
53
+ return matched;
54
+ }
55
+ function isGenericDateDescriptor(text) {
56
+ const normalized = text
57
+ .trim()
58
+ .toLowerCase()
59
+ .replace(/["'“”‘’]/g, '')
60
+ .replace(/\s+/g, ' ');
61
+ return new Set([
62
+ 'date',
63
+ 'the date',
64
+ 'top date',
65
+ 'contract date',
66
+ 'agreement date',
67
+ 'effective date',
68
+ 'signature date',
69
+ 'sign date',
70
+ 'current date',
71
+ ]).has(normalized);
72
+ }
73
+ function uniqueDateCandidates(text, exclude) {
74
+ const excludeLower = exclude.toLowerCase();
75
+ const dates = new Set();
76
+ DATE_CANDIDATE_PATTERN.lastIndex = 0;
77
+ for (const match of text.matchAll(DATE_CANDIDATE_PATTERN)) {
78
+ const value = match[0];
79
+ if (value.toLowerCase() !== excludeLower) {
80
+ dates.add(value);
81
+ }
82
+ }
83
+ return [...dates];
84
+ }
85
+ function withPreflightedEdits(resolved, editPreflight) {
86
+ return {
87
+ ...resolved,
88
+ edits: editPreflight.filter((edit) => edit.sourcePresent).map(({ sourcePresent: _sourcePresent, ...edit }) => edit),
89
+ };
90
+ }
91
+ async function resolveDescriptorBackedEdits(context, resolved, currentText, editPreflight) {
92
+ if (resolved.action !== 'replace_all') {
93
+ return editPreflight;
94
+ }
95
+ let resolvedPreflight = editPreflight.map((edit) => {
96
+ const replacement = edit.replace ?? '';
97
+ if (!dateLikePattern(replacement)) {
98
+ return edit;
99
+ }
100
+ const candidates = uniqueDateCandidates(currentText, replacement);
101
+ const shouldResolveGenericDate = isGenericDateDescriptor(edit.find) && candidates.length > 0;
102
+ const shouldResolveMissingLiteralDate = !edit.sourcePresent && edit.find === replacement && candidates.length === 1;
103
+ if (!shouldResolveGenericDate && !shouldResolveMissingLiteralDate) {
104
+ return edit;
105
+ }
106
+ return {
107
+ ...edit,
108
+ find: candidates[0] ?? edit.find,
109
+ sourcePresent: true,
110
+ };
111
+ });
112
+ const needsIndentedHeadingFallback = resolvedPreflight.some((edit) => !edit.sourcePresent && edit.find.toLowerCase().includes('indented heading') && (edit.replace ?? '').length > 0);
113
+ if (!needsIndentedHeadingFallback) {
114
+ return resolvedPreflight;
115
+ }
116
+ const page = await context.documentHandle.blocks.list({ offset: 0, limit: 100, includeText: true }, context.invokeOptions);
117
+ const candidate = page.blocks.find((block) => {
118
+ const record = block;
119
+ const text = typeof block.text === 'string'
120
+ ? block.text.trim()
121
+ : typeof block.textPreview === 'string'
122
+ ? block.textPreview.trim()
123
+ : '';
124
+ if (block.nodeType !== 'paragraph' || text.length === 0 || text.length > 80) {
125
+ return false;
126
+ }
127
+ return (record.bold === true ||
128
+ String(record.styleId ?? '')
129
+ .toLowerCase()
130
+ .includes('heading'));
131
+ });
132
+ const candidateText = typeof candidate?.text === 'string'
133
+ ? candidate.text.trim()
134
+ : typeof candidate?.textPreview === 'string'
135
+ ? candidate.textPreview.trim()
136
+ : undefined;
137
+ if (candidateText == null || candidateText.length === 0) {
138
+ return resolvedPreflight;
139
+ }
140
+ resolvedPreflight = resolvedPreflight.map((edit) => {
141
+ if (edit.sourcePresent || !edit.find.toLowerCase().includes('indented heading')) {
142
+ return edit;
143
+ }
144
+ return {
145
+ ...edit,
146
+ find: candidateText,
147
+ sourcePresent: true,
148
+ };
149
+ });
150
+ return resolvedPreflight;
151
+ }
152
+ function parseEdits(raw) {
153
+ if (!Array.isArray(raw) || raw.length === 0) {
154
+ return undefined;
155
+ }
156
+ const edits = [];
157
+ for (const entry of raw) {
158
+ if (!isObjectRecord(entry)) {
159
+ return undefined;
160
+ }
161
+ const find = entry.find;
162
+ if (typeof find !== 'string' || find.length === 0) {
163
+ return undefined;
164
+ }
165
+ const replace = entry.replace;
166
+ if (replace != null && typeof replace !== 'string') {
167
+ return undefined;
168
+ }
169
+ edits.push({
170
+ find,
171
+ replace: typeof replace === 'string' ? replace : undefined,
172
+ });
173
+ }
174
+ return edits;
175
+ }
176
+ function parsePlaceholderValues(raw) {
177
+ if (raw == null)
178
+ return [];
179
+ if (!Array.isArray(raw))
180
+ return undefined;
181
+ const values = [];
182
+ for (const entry of raw) {
183
+ if (typeof entry === 'string' && entry.length > 0) {
184
+ values.push(entry);
185
+ continue;
186
+ }
187
+ if (isObjectRecord(entry) && typeof entry.value === 'string' && entry.value.length > 0) {
188
+ values.push(entry.value);
189
+ continue;
190
+ }
191
+ return undefined;
192
+ }
193
+ return values;
194
+ }
195
+ function rewriteBlockSupported(nodeType) {
196
+ return REWRITE_BLOCK_SUPPORTED_NODE_TYPES.some((value) => value === nodeType);
197
+ }
198
+ function resolveStep(context) {
199
+ const action = parseAction(context.args.action);
200
+ if (action == null) {
201
+ return engine.workflowStepFailure({
202
+ status: 'failed',
203
+ phase: 'resolve',
204
+ code: 'WORKFLOW_TEXT_TRANSFORM_ACTION_INVALID',
205
+ message: 'superdoc_text_transform requires action to be one of replace_all, delete_all, rewrite_block, fill_placeholders.',
206
+ });
207
+ }
208
+ const changeMode = parseChangeMode(context.args.changeMode);
209
+ if (changeMode == null) {
210
+ return engine.workflowStepFailure({
211
+ status: 'failed',
212
+ phase: 'resolve',
213
+ code: 'WORKFLOW_TEXT_TRANSFORM_CHANGE_MODE_INVALID',
214
+ message: 'changeMode must be "direct" or "tracked".',
215
+ details: { received: context.args.changeMode },
216
+ });
217
+ }
218
+ const preserveStyle = coerceBoolean(context.args.preserveStyle, true);
219
+ const caseSensitive = coerceBoolean(context.args.caseSensitive, false);
220
+ const edits = parseEdits(context.args.edits) ?? [];
221
+ const placeholderValues = parsePlaceholderValues(context.args.values ?? context.args.fields);
222
+ if (placeholderValues == null) {
223
+ return engine.workflowStepFailure({
224
+ status: 'failed',
225
+ phase: 'resolve',
226
+ code: 'WORKFLOW_PLACEHOLDER_VALUES_INVALID',
227
+ message: 'values/fields must be an array of strings or {value} objects.',
228
+ });
229
+ }
230
+ if (action === 'rewrite_block') {
231
+ if (context.args.target == null) {
232
+ return engine.workflowStepFailure({
233
+ status: 'failed',
234
+ phase: 'resolve',
235
+ code: 'WORKFLOW_TARGET_REQUIRED',
236
+ message: 'rewrite_block requires a deterministic target selector.',
237
+ details: { expectedArgKey: 'target' },
238
+ });
239
+ }
240
+ const resolved = resolve.resolveWorkflowTargetFromUnknown(context.index, context.args.target);
241
+ if (!resolved.ok) {
242
+ return engine.workflowStepFailure({
243
+ status: 'failed',
244
+ phase: 'resolve',
245
+ code: `WORKFLOW_${resolved.code}`,
246
+ message: resolved.message,
247
+ details: {
248
+ targetArgKey: 'target',
249
+ ...resolved.details,
250
+ },
251
+ });
252
+ }
253
+ if (resolved.target.entity.kind !== 'block') {
254
+ return engine.workflowStepFailure({
255
+ status: 'failed',
256
+ phase: 'resolve',
257
+ code: 'WORKFLOW_TARGET_KIND_UNSUPPORTED',
258
+ message: `rewrite_block requires a block target but resolved to ${resolved.target.entity.kind}.`,
259
+ details: {
260
+ targetArgKey: 'target',
261
+ entityKind: resolved.target.entity.kind,
262
+ },
263
+ });
264
+ }
265
+ if (!rewriteBlockSupported(resolved.target.entity.nodeType)) {
266
+ return engine.workflowStepFailure({
267
+ status: 'failed',
268
+ phase: 'resolve',
269
+ code: 'WORKFLOW_TARGET_KIND_UNSUPPORTED',
270
+ message: `rewrite_block does not support block nodeType "${resolved.target.entity.nodeType}".`,
271
+ details: {
272
+ targetArgKey: 'target',
273
+ entityKind: resolved.target.entity.kind,
274
+ nodeType: resolved.target.entity.nodeType,
275
+ supportedNodeTypes: [...REWRITE_BLOCK_SUPPORTED_NODE_TYPES],
276
+ },
277
+ });
278
+ }
279
+ const rewriteText = parseText(context.args.text);
280
+ if (rewriteText == null) {
281
+ return engine.workflowStepFailure({
282
+ status: 'failed',
283
+ phase: 'resolve',
284
+ code: 'WORKFLOW_TEXT_REQUIRED',
285
+ message: 'rewrite_block requires non-empty text.',
286
+ });
287
+ }
288
+ return engine.workflowStepSuccess({
289
+ action,
290
+ changeMode,
291
+ preserveStyle,
292
+ caseSensitive,
293
+ edits: [],
294
+ placeholderValues: [],
295
+ rewriteText,
296
+ targetArgKey: 'target',
297
+ request: resolved.request,
298
+ target: resolved.target,
299
+ });
300
+ }
301
+ if (edits.length === 0 && !(action === 'fill_placeholders' && placeholderValues.length > 0)) {
302
+ return engine.workflowStepFailure({
303
+ status: 'failed',
304
+ phase: 'resolve',
305
+ code: 'WORKFLOW_EDITS_REQUIRED',
306
+ message: `${action} requires a non-empty edits array with {find, replace?} entries.`,
307
+ });
308
+ }
309
+ if (action === 'replace_all' || action === 'fill_placeholders') {
310
+ const missingReplace = edits.find((edit) => edit.replace == null);
311
+ if (missingReplace != null) {
312
+ return engine.workflowStepFailure({
313
+ status: 'failed',
314
+ phase: 'resolve',
315
+ code: 'WORKFLOW_REPLACE_VALUE_REQUIRED',
316
+ message: `${action} requires replace text for every edit entry.`,
317
+ details: { missingFind: missingReplace.find },
318
+ });
319
+ }
320
+ }
321
+ return engine.workflowStepSuccess({
322
+ action,
323
+ changeMode,
324
+ preserveStyle,
325
+ caseSensitive,
326
+ edits,
327
+ placeholderValues: placeholderValues.length > 0
328
+ ? placeholderValues
329
+ : edits.map((edit) => edit.replace ?? '').filter((value) => value.length > 0),
330
+ });
331
+ }
332
+ function rewriteStyle(preserveStyle) {
333
+ if (!preserveStyle) {
334
+ return undefined;
335
+ }
336
+ return {
337
+ inline: { mode: 'preserve' },
338
+ paragraph: { mode: 'preserve' },
339
+ };
340
+ }
341
+ function buildReplaceAllSteps(resolved) {
342
+ return resolved.edits.map((edit, index) => ({
343
+ id: `replace-${index + 1}`,
344
+ op: 'text.rewrite',
345
+ where: {
346
+ by: 'select',
347
+ select: {
348
+ type: 'text',
349
+ pattern: edit.find,
350
+ mode: 'contains',
351
+ caseSensitive: resolved.caseSensitive,
352
+ },
353
+ require: 'all',
354
+ },
355
+ args: {
356
+ replacement: { text: edit.replace ?? '' },
357
+ style: rewriteStyle(resolved.preserveStyle),
358
+ },
359
+ }));
360
+ }
361
+ function buildDeleteAllSteps(resolved) {
362
+ return resolved.edits.map((edit, index) => ({
363
+ id: `delete-${index + 1}`,
364
+ op: 'text.delete',
365
+ where: {
366
+ by: 'select',
367
+ select: {
368
+ type: 'text',
369
+ pattern: edit.find,
370
+ mode: 'contains',
371
+ caseSensitive: resolved.caseSensitive,
372
+ },
373
+ require: 'all',
374
+ },
375
+ args: {},
376
+ }));
377
+ }
378
+ function buildRewriteBlockSteps(resolved) {
379
+ const target = resolved.target;
380
+ if (target == null || target.entity.kind !== 'block' || resolved.rewriteText == null) {
381
+ return [];
382
+ }
383
+ return [
384
+ {
385
+ id: 'rewrite-block-1',
386
+ op: 'text.rewrite',
387
+ where: {
388
+ by: 'block',
389
+ nodeType: target.entity.nodeType,
390
+ nodeId: target.nodeId,
391
+ },
392
+ args: {
393
+ replacement: { text: resolved.rewriteText },
394
+ style: rewriteStyle(resolved.preserveStyle),
395
+ },
396
+ },
397
+ ];
398
+ }
399
+ async function buildFillPlaceholderSteps(context, resolved) {
400
+ if (resolved.placeholderValues.length === 0) {
401
+ return buildReplaceAllSteps(resolved);
402
+ }
403
+ const steps = [];
404
+ let valueIndex = 0;
405
+ let offset = 0;
406
+ while (true) {
407
+ const page = await context.documentHandle.blocks.list({ offset, limit: 250, includeText: true }, context.invokeOptions);
408
+ for (const block of page.blocks) {
409
+ if (block.nodeType !== 'paragraph' && block.nodeType !== 'heading' && block.nodeType !== 'listItem')
410
+ continue;
411
+ const text = typeof block.text === 'string' ? block.text : '';
412
+ if (!text.includes('[insert]'))
413
+ continue;
414
+ const rewritten = text.replace(/\[insert\]/g, () => {
415
+ const value = resolved.placeholderValues[valueIndex] ??
416
+ resolved.placeholderValues[resolved.placeholderValues.length - 1] ??
417
+ '';
418
+ valueIndex += 1;
419
+ return value;
420
+ });
421
+ steps.push({
422
+ id: `fill-placeholder-block-${steps.length + 1}`,
423
+ op: 'text.rewrite',
424
+ where: {
425
+ by: 'block',
426
+ nodeType: block.nodeType,
427
+ nodeId: block.nodeId,
428
+ },
429
+ args: {
430
+ replacement: { text: rewritten },
431
+ style: rewriteStyle(resolved.preserveStyle),
432
+ },
433
+ });
434
+ }
435
+ offset += page.blocks.length;
436
+ if (page.blocks.length === 0 || offset >= page.total)
437
+ break;
438
+ }
439
+ return steps;
440
+ }
441
+ async function planStep(context, resolved) {
442
+ let plannedResolved = resolved;
443
+ let editPreflight = resolved.edits.map((edit) => ({ ...edit, sourcePresent: true }));
444
+ if (resolved.action === 'replace_all' || resolved.action === 'delete_all') {
445
+ const currentText = await context.documentHandle.getText({}, context.invokeOptions);
446
+ editPreflight = resolved.edits.map((edit) => ({
447
+ ...edit,
448
+ sourcePresent: resolved.action === 'replace_all' && edit.replace === edit.find
449
+ ? false
450
+ : textIncludes(currentText, edit.find, resolved.caseSensitive),
451
+ }));
452
+ editPreflight = await resolveDescriptorBackedEdits(context, resolved, currentText, editPreflight);
453
+ plannedResolved = withPreflightedEdits(resolved, editPreflight);
454
+ }
455
+ const steps = plannedResolved.action === 'rewrite_block'
456
+ ? buildRewriteBlockSteps(plannedResolved)
457
+ : plannedResolved.action === 'delete_all'
458
+ ? buildDeleteAllSteps(plannedResolved)
459
+ : plannedResolved.action === 'fill_placeholders'
460
+ ? await buildFillPlaceholderSteps(context, plannedResolved)
461
+ : buildReplaceAllSteps(plannedResolved);
462
+ // fill_placeholders used to force direct mode (legacy workaround from when
463
+ // tracked rewrites misbehaved). Tracked rewrites are correct now, and
464
+ // silently downgrading a tracked request makes the receipt lie — lawyers
465
+ // filling a template AS TRACKED CHANGES is a core workflow.
466
+ const applyChangeMode = resolved.changeMode;
467
+ if (steps.length === 0 && (resolved.action === 'replace_all' || resolved.action === 'delete_all')) {
468
+ return engine.workflowStepSuccess({
469
+ action: resolved.action,
470
+ changeMode: applyChangeMode,
471
+ stepCount: 0,
472
+ noOp: true,
473
+ editPreflight,
474
+ applyParams: {
475
+ atomic: true,
476
+ changeMode: applyChangeMode,
477
+ steps,
478
+ },
479
+ });
480
+ }
481
+ if (steps.length === 0) {
482
+ return engine.workflowStepFailure({
483
+ status: 'failed',
484
+ phase: 'plan',
485
+ code: 'WORKFLOW_TEXT_TRANSFORM_STEP_BUILD_FAILED',
486
+ message: `No mutation steps were built for action "${resolved.action}".`,
487
+ });
488
+ }
489
+ return engine.workflowStepSuccess({
490
+ action: resolved.action,
491
+ changeMode: applyChangeMode,
492
+ stepCount: steps.length,
493
+ noOp: false,
494
+ editPreflight,
495
+ applyParams: {
496
+ atomic: true,
497
+ changeMode: applyChangeMode,
498
+ steps,
499
+ },
500
+ });
501
+ }
502
+ async function executeStep(context, _resolved, plan) {
503
+ if (plan.stepCount === 0) {
504
+ return engine.workflowStepSuccess({
505
+ action: plan.action,
506
+ changeMode: plan.changeMode,
507
+ stepCount: plan.stepCount,
508
+ revision: {
509
+ before: context.info.revision,
510
+ after: context.info.revision,
511
+ },
512
+ });
513
+ }
514
+ const applyResult = await context.documentHandle.mutations.apply(plan.applyParams, context.invokeOptions);
515
+ return engine.workflowStepSuccess({
516
+ action: plan.action,
517
+ changeMode: plan.changeMode,
518
+ stepCount: plan.stepCount,
519
+ revision: applyResult.revision,
520
+ });
521
+ }
522
+ function verifyReplace(resolved, plan, execution, currentText) {
523
+ const matchedEdits = plan.editPreflight.filter((edit) => edit.sourcePresent);
524
+ const replacementCandidates = matchedEdits.filter((edit) => (edit.replace ?? '').length > 0);
525
+ const replacementExpected = replacementCandidates.length;
526
+ const replacementsPresent = replacementCandidates.filter((edit) => textIncludes(currentText, edit.replace ?? '', resolved.caseSensitive)).length;
527
+ const sourceRemovalCandidates = plan.editPreflight.filter((edit) => {
528
+ const replacement = edit.replace ?? '';
529
+ return replacement !== edit.find && !replacement.includes(edit.find);
530
+ });
531
+ const sourceRemovalExpected = sourceRemovalCandidates.length;
532
+ const deletedPatternsGone = sourceRemovalCandidates.filter((edit) => !textIncludes(currentText, edit.find, resolved.caseSensitive)).length;
533
+ const passed = replacementsPresent === replacementExpected && deletedPatternsGone === sourceRemovalExpected;
534
+ const summary = `replacement checks ${replacementsPresent}/${replacementExpected}; source removal checks ${deletedPatternsGone}/${sourceRemovalExpected}; revision ${execution.revision.before} -> ${execution.revision.after}.`;
535
+ if (!passed) {
536
+ return engine.workflowStepFailure({
537
+ status: 'failed',
538
+ phase: 'verify',
539
+ code: 'WORKFLOW_TEXT_TRANSFORM_VERIFICATION_FAILED',
540
+ message: 'Text replacement verification failed.',
541
+ details: {
542
+ action: execution.action,
543
+ summary,
544
+ replacementsPresent,
545
+ replacementExpected,
546
+ deletedPatternsGone,
547
+ deletedPatternsExpected: sourceRemovalExpected,
548
+ },
549
+ });
550
+ }
551
+ return engine.workflowStepSuccess({
552
+ action: execution.action,
553
+ deterministicTarget: execution.action === 'rewrite_block',
554
+ passed,
555
+ summary,
556
+ checks: {
557
+ replacementsPresent,
558
+ replacementsExpected: replacementExpected,
559
+ deletedPatternsGone,
560
+ deletedPatternsExpected: sourceRemovalExpected,
561
+ },
562
+ });
563
+ }
564
+ function verifyDelete(resolved, execution, currentText) {
565
+ const deletedPatternsExpected = resolved.edits.length;
566
+ const deletedPatternsGone = resolved.edits.filter((edit) => !textIncludes(currentText, edit.find, resolved.caseSensitive)).length;
567
+ const passed = deletedPatternsGone === deletedPatternsExpected;
568
+ const summary = `delete checks ${deletedPatternsGone}/${deletedPatternsExpected}; revision ${execution.revision.before} -> ${execution.revision.after}.`;
569
+ if (!passed) {
570
+ return engine.workflowStepFailure({
571
+ status: 'failed',
572
+ phase: 'verify',
573
+ code: 'WORKFLOW_TEXT_TRANSFORM_VERIFICATION_FAILED',
574
+ message: 'Text deletion verification failed.',
575
+ details: {
576
+ action: execution.action,
577
+ summary,
578
+ deletedPatternsGone,
579
+ deletedPatternsExpected,
580
+ },
581
+ });
582
+ }
583
+ return engine.workflowStepSuccess({
584
+ action: execution.action,
585
+ deterministicTarget: false,
586
+ passed,
587
+ summary,
588
+ checks: {
589
+ deletedPatternsGone,
590
+ deletedPatternsExpected,
591
+ },
592
+ });
593
+ }
594
+ function significantRewriteTokens(text) {
595
+ return text
596
+ .split(/\s+/)
597
+ .map((token) => token.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu, ''))
598
+ .filter((token) => token.length >= 3);
599
+ }
600
+ function tokensPresentInOrder(haystack, tokens) {
601
+ let offset = 0;
602
+ const lowerHaystack = haystack.toLowerCase();
603
+ for (const token of tokens) {
604
+ const index = lowerHaystack.indexOf(token.toLowerCase(), offset);
605
+ if (index < 0) {
606
+ return false;
607
+ }
608
+ offset = index + token.length;
609
+ }
610
+ return true;
611
+ }
612
+ function verifyRewriteBlock(resolved, execution, currentText) {
613
+ const rewriteText = resolved.rewriteText ?? '';
614
+ const rewrittenTextPresent = execution.changeMode === 'tracked'
615
+ ? tokensPresentInOrder(currentText, significantRewriteTokens(rewriteText))
616
+ : currentText.includes(rewriteText);
617
+ const deterministicTarget = resolved.target != null;
618
+ const passed = rewrittenTextPresent && deterministicTarget;
619
+ const summary = `rewrite_block checks textPresent=${rewrittenTextPresent} deterministicTarget=${deterministicTarget}; revision ${execution.revision.before} -> ${execution.revision.after}.`;
620
+ if (!passed) {
621
+ return engine.workflowStepFailure({
622
+ status: 'failed',
623
+ phase: 'verify',
624
+ code: 'WORKFLOW_TEXT_TRANSFORM_VERIFICATION_FAILED',
625
+ message: 'rewrite_block verification failed.',
626
+ details: {
627
+ action: execution.action,
628
+ summary,
629
+ rewrittenTextPresent,
630
+ deterministicTarget,
631
+ },
632
+ });
633
+ }
634
+ return engine.workflowStepSuccess({
635
+ action: execution.action,
636
+ deterministicTarget,
637
+ passed,
638
+ summary,
639
+ checks: {
640
+ rewrittenTextPresent,
641
+ },
642
+ });
643
+ }
644
+ async function verifyStep(context, resolved, plan, execution) {
645
+ const currentText = await context.documentHandle.getText({}, context.invokeOptions);
646
+ if (execution.action === 'delete_all') {
647
+ return verifyDelete(resolved, execution, currentText);
648
+ }
649
+ if (execution.action === 'rewrite_block') {
650
+ return verifyRewriteBlock(resolved, execution, currentText);
651
+ }
652
+ return verifyReplace(resolved, plan, execution, currentText);
653
+ }
654
+ async function runSuperdocTextTransformWorkflow(input) {
655
+ return engine.runWorkflowEngine({
656
+ documentHandle: input.documentHandle,
657
+ toolName: 'superdoc_text_transform',
658
+ args: input.args,
659
+ invokeOptions: input.invokeOptions,
660
+ hooks: {
661
+ resolve: async (context) => resolveStep(context),
662
+ plan: async (context, resolved) => planStep(context, resolved),
663
+ execute: async (context, resolved, plan) => executeStep(context, resolved, plan),
664
+ verify: async (context, resolved, plan, execution) => verifyStep(context, resolved, plan, execution),
665
+ },
666
+ });
667
+ }
668
+
669
+ exports.runSuperdocTextTransformWorkflow = runSuperdocTextTransformWorkflow;