@superdoc/sdk 2.10.0-next.6 → 2.10.0-next.8
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/README.md +45 -0
- package/dist/action-primitives/doc-index.cjs +6 -4
- package/dist/action-primitives/doc-index.js +6 -4
- package/dist/action-primitives/engine.cjs +6 -2
- package/dist/action-primitives/engine.js +6 -2
- package/dist/action-primitives/receipt.d.ts +4 -0
- package/dist/action-primitives/tools/structure-insert.d.ts +1 -1
- package/dist/agent/actions.cjs +203 -223
- package/dist/agent/actions.d.ts +5 -0
- package/dist/agent/actions.js +203 -223
- package/dist/agent/catalog.cjs +15 -0
- package/dist/agent/catalog.js +15 -0
- package/dist/agent/doc-snapshot.cjs +22 -46
- package/dist/agent/doc-snapshot.js +22 -46
- package/dist/agent/execution-context.cjs +385 -0
- package/dist/agent/execution-context.d.ts +97 -0
- package/dist/agent/execution-context.js +376 -0
- package/dist/agent/runtime.cjs +116 -158
- package/dist/agent/runtime.d.ts +3 -0
- package/dist/agent/runtime.js +117 -159
- package/dist/agent/v2-preset-compat.cjs +5 -1
- package/dist/agent/v2-preset-compat.js +4 -1
- package/dist/embedded-tools.generated.cjs +5 -5
- package/dist/embedded-tools.generated.js +5 -5
- package/dist/generated/client.cjs +2 -0
- package/dist/generated/client.d.ts +47 -0
- package/dist/generated/client.js +2 -0
- package/dist/generated/contract.cjs +1600 -1296
- package/dist/generated/contract.js +1600 -1296
- package/dist/index.cjs +11 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +11 -0
- package/dist/presets/core.cjs +1 -1
- package/dist/presets/core.js +1 -1
- package/dist/runtime/document-evidence.cjs +40 -0
- package/dist/runtime/document-evidence.d.ts +13 -0
- package/dist/runtime/document-evidence.js +30 -0
- package/dist/runtime/host.cjs +30 -2
- package/dist/runtime/host.d.ts +2 -0
- package/dist/runtime/host.js +30 -2
- package/dist/runtime/process.cjs +6 -0
- package/dist/runtime/process.d.ts +2 -0
- package/dist/runtime/process.js +6 -0
- package/dist/runtime/sdk-version.generated.cjs +1 -1
- package/dist/runtime/sdk-version.generated.d.ts +1 -1
- package/dist/runtime/sdk-version.generated.js +1 -1
- package/package.json +10 -9
- package/tools/catalog.json +35 -0
- package/tools/tools-policy.json +1 -1
- package/tools/tools.anthropic.json +35 -0
- package/tools/tools.generic.json +35 -0
- package/tools/tools.openai.json +35 -0
- package/tools/tools.vercel.json +35 -0
package/dist/agent/actions.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ExecutionContext, evaluateFactChecks } from './execution-context.js';
|
|
1
2
|
import { SuperDocCliError } from '../runtime/errors.js';
|
|
2
3
|
import { runSuperdocListTransformWorkflow } from '../action-primitives/tools/list-transform.js';
|
|
3
4
|
import { runSuperdocStructureInsertWorkflow } from '../action-primitives/tools/structure-insert.js';
|
|
@@ -167,18 +168,18 @@ export const ACTION_GROUPS = [
|
|
|
167
168
|
* action at compile time.
|
|
168
169
|
*/
|
|
169
170
|
export const ACTION_ARGS = {
|
|
170
|
-
insert_paragraphs: ['text', 'texts', 'headingLevel', 'placement', 'changeMode'],
|
|
171
|
+
insert_paragraphs: ['text', 'texts', 'headingLevel', 'placement', 'changeMode', 'evidence'],
|
|
171
172
|
insert_heading: ['text', 'level', 'placement', 'changeMode'],
|
|
172
173
|
replace_text: ['edits', 'selector', 'caseSensitive', 'changeMode'],
|
|
173
174
|
delete_text: ['finds', 'selector', 'caseSensitive', 'changeMode'],
|
|
174
175
|
delete_blocks: ['selectors', 'selector', 'changeMode'],
|
|
175
176
|
append_list: ['items', 'kind', 'headingText', 'headingLevel', 'placement', 'changeMode'],
|
|
176
|
-
create_table: ['rows', 'columns', 'cellTexts', 'placement', 'changeMode'],
|
|
177
|
+
create_table: ['rows', 'columns', 'cellTexts', 'placement', 'changeMode', 'evidence'],
|
|
177
178
|
comment_paragraphs: ['commentText', 'scope', 'excludeBlockQuotes'],
|
|
178
179
|
add_comments: ['commentText', 'selector', 'selectors'],
|
|
179
180
|
resolve_comments: ['anchorText', 'reopen'],
|
|
180
181
|
reply_to_comment: ['commentText', 'anchorText', 'commentId'],
|
|
181
|
-
rewrite_block: ['text', 'selector', 'changeMode'],
|
|
182
|
+
rewrite_block: ['text', 'selector', 'changeMode', 'evidence'],
|
|
182
183
|
accept_tracked_changes: ['author', 'changeType'],
|
|
183
184
|
reject_tracked_changes: ['author', 'changeType'],
|
|
184
185
|
normalize_body_font_size: ['fontSize', 'changeMode'],
|
|
@@ -213,7 +214,7 @@ export const ACTION_ARGS = {
|
|
|
213
214
|
'changeMode',
|
|
214
215
|
],
|
|
215
216
|
apply_style: ['selector', 'styleId', 'headingLevel', 'likeText'],
|
|
216
|
-
format_paragraph: ['selector', 'alignment', 'changeMode'],
|
|
217
|
+
format_paragraph: ['selector', 'alignment', 'changeMode', 'evidence'],
|
|
217
218
|
move_text: ['text', 'afterText', 'changeMode'],
|
|
218
219
|
style_table: ['tableOrdinal', 'accentColor'],
|
|
219
220
|
move_table: ['tableOrdinal', 'placement'],
|
|
@@ -316,10 +317,6 @@ function findSnapshotTextByNodeId(snapshot, nodeId) {
|
|
|
316
317
|
}
|
|
317
318
|
return null;
|
|
318
319
|
}
|
|
319
|
-
function lastBlock(snapshot) {
|
|
320
|
-
const block = snapshot.blocks[snapshot.blocks.length - 1];
|
|
321
|
-
return block ? { nodeId: block.nodeId, nodeType: block.nodeType } : null;
|
|
322
|
-
}
|
|
323
320
|
function createdBlockTarget(result) {
|
|
324
321
|
const rec = asRecord(result);
|
|
325
322
|
const paragraph = asRecord(rec?.paragraph);
|
|
@@ -857,7 +854,7 @@ forceDirectFill = false) {
|
|
|
857
854
|
});
|
|
858
855
|
}
|
|
859
856
|
const extracted = asRecord(await extractFn({}));
|
|
860
|
-
const blocks = (Array.isArray(extracted?.blocks) ? extracted
|
|
857
|
+
const blocks = (Array.isArray(extracted?.blocks) ? extracted.blocks : [])
|
|
861
858
|
.map((block) => asRecord(block))
|
|
862
859
|
.filter((block) => block != null)
|
|
863
860
|
.flatMap((block) => {
|
|
@@ -1046,95 +1043,68 @@ async function styleTableCells(doc, cells, changeMode) {
|
|
|
1046
1043
|
await applyItemStyling(doc, nodeIds, specs, changeMode);
|
|
1047
1044
|
return styled.length;
|
|
1048
1045
|
}
|
|
1049
|
-
async function runInsertParagraphs(doc, args) {
|
|
1050
|
-
const
|
|
1051
|
-
? await buildMutationSnapshot(doc, { includeDomains: ['blocks'] })
|
|
1052
|
-
: await buildDocumentSnapshot(doc, { includeDomains: ['blocks'] });
|
|
1046
|
+
async function runInsertParagraphs(doc, args, formatArgs) {
|
|
1047
|
+
const context = new ExecutionContext(doc, { evidence: args.evidence, selectors: placementSelectors(args.placement) });
|
|
1053
1048
|
try {
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
const
|
|
1057
|
-
const
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
// For subsequent items: append after the previously inserted block by
|
|
1065
|
-
// using `documentEnd` (the SDK keeps blocks contiguous) so order is
|
|
1066
|
-
// preserved.
|
|
1049
|
+
const pre = await context.start();
|
|
1050
|
+
const placement = await resolveFactPlacement(args.placement, pre);
|
|
1051
|
+
const isNewDocument = (await pre.count()) <= 1;
|
|
1052
|
+
const anchor = placement.kind === 'before' || placement.kind === 'after'
|
|
1053
|
+
? await pre.block(placement.target.nodeId)
|
|
1054
|
+
: await pre.target({
|
|
1055
|
+
kind: 'placement',
|
|
1056
|
+
at: placement.kind === 'documentStart' ? 'document_start' : 'document_end',
|
|
1057
|
+
});
|
|
1058
|
+
const preRows = pre.complete ? await listBlockRows(doc) : anchor ? [anchor] : [];
|
|
1067
1059
|
let currentPlacement = placement;
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
return
|
|
1060
|
+
const nodeIds = [];
|
|
1061
|
+
const specs = [];
|
|
1062
|
+
const advance = async (result) => {
|
|
1063
|
+
const target = createdBlockTarget(result);
|
|
1064
|
+
if (target) {
|
|
1065
|
+
currentPlacement = {
|
|
1066
|
+
kind: 'after',
|
|
1067
|
+
target: { kind: 'block', nodeType: target.nodeType, nodeId: target.nodeId },
|
|
1068
|
+
};
|
|
1069
|
+
return target.nodeId;
|
|
1078
1070
|
}
|
|
1079
|
-
const
|
|
1080
|
-
const last =
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1071
|
+
const current = await context.finishRevision();
|
|
1072
|
+
const last = await current.target({ kind: 'placement', at: 'document_end' });
|
|
1073
|
+
if (!last)
|
|
1074
|
+
throw new Error('Created paragraph identity is unavailable.');
|
|
1075
|
+
currentPlacement = { kind: 'after', target: { kind: 'block', nodeType: last.nodeType, nodeId: last.nodeId } };
|
|
1076
|
+
return last.nodeId;
|
|
1084
1077
|
};
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
currentPlacement = await advanceAfter(result);
|
|
1108
|
-
// New-document drafts get a blank paragraph after each item for spacing;
|
|
1109
|
-
// inserts into an existing document do not (would scatter blank lines).
|
|
1110
|
-
if (isNewDocument) {
|
|
1111
|
-
const spacer = await executeCreateParagraph(doc, '', currentPlacement, args.changeMode);
|
|
1112
|
-
executedOperations.push({ operationId: 'doc.create.paragraph', result: spacer, rationale: 'spacing' });
|
|
1113
|
-
currentPlacement = await advanceAfter(spacer);
|
|
1078
|
+
const texts = args.texts ?? (args.text ? [args.text] : []);
|
|
1079
|
+
for (let i = 0; i < texts.length; i++) {
|
|
1080
|
+
const result = i === 0 && args.headingLevel
|
|
1081
|
+
? await executeCreateHeading(context.document, texts[i], args.headingLevel, currentPlacement, args.changeMode)
|
|
1082
|
+
: await executeCreateParagraph(context.document, texts[i], currentPlacement, args.changeMode);
|
|
1083
|
+
const id = await advance(result);
|
|
1084
|
+
nodeIds.push(id);
|
|
1085
|
+
specs.push({ ...args.textSpecs?.[i], text: texts[i] });
|
|
1086
|
+
if (isNewDocument)
|
|
1087
|
+
await advance(await executeCreateParagraph(context.document, '', currentPlacement, args.changeMode));
|
|
1088
|
+
}
|
|
1089
|
+
if (specs.some((spec) => spec.runs?.length || spec.marks))
|
|
1090
|
+
await applyItemStyling(context.document, nodeIds, specs, args.changeMode);
|
|
1091
|
+
let post = await context.finishRevision();
|
|
1092
|
+
let formatted;
|
|
1093
|
+
if (formatArgs) {
|
|
1094
|
+
let localRows;
|
|
1095
|
+
if (!pre.complete) {
|
|
1096
|
+
const created = await post.page({ nodeIds, limit: Math.max(nodeIds.length, 1) });
|
|
1097
|
+
localRows = [...(anchor ? [anchor] : []), ...created].sort((a, b) => a.ordinal - b.ordinal);
|
|
1114
1098
|
}
|
|
1099
|
+
const count = context.executedOperations.length;
|
|
1100
|
+
formatted = await matchInsertedBlockFormatting(context.document, preRows, context.receipt('insert_paragraphs', []), formatArgs, localRows);
|
|
1101
|
+
if (context.executedOperations.length !== count)
|
|
1102
|
+
post = await context.finishRevision();
|
|
1115
1103
|
}
|
|
1116
|
-
|
|
1117
|
-
const styling = await applyItemStyling(doc, styledNodeIds, styledSpecs, args.changeMode);
|
|
1118
|
-
executedOperations.push({
|
|
1119
|
-
operationId: 'doc.format.apply',
|
|
1120
|
-
result: styling,
|
|
1121
|
-
rationale: `Styled ${styledNodeIds.length} inserted paragraph(s)`,
|
|
1122
|
-
});
|
|
1123
|
-
}
|
|
1124
|
-
const postIdentity = await readDocumentIdentity(doc);
|
|
1125
|
-
const verification = [revisionVerification(pre.revision, postIdentity.revision, true)];
|
|
1126
|
-
return {
|
|
1127
|
-
status: verification.every((v) => v.passed) ? 'ok' : 'failed',
|
|
1128
|
-
intent: 'insert_paragraphs',
|
|
1129
|
-
preSnapshot: { revision: pre.revision, counts: pre.counts },
|
|
1130
|
-
postSnapshot: postIdentity,
|
|
1131
|
-
selectedTargets: [],
|
|
1132
|
-
executedOperations,
|
|
1133
|
-
verification,
|
|
1134
|
-
};
|
|
1104
|
+
return context.receipt('insert_paragraphs', await evaluateFactChecks(pre, post, [{ kind: 'revision-changed' }]), formatted?.contextualFormatting ? { contextualFormatting: formatted.contextualFormatting } : {});
|
|
1135
1105
|
}
|
|
1136
|
-
catch (
|
|
1137
|
-
return
|
|
1106
|
+
catch (error) {
|
|
1107
|
+
return context.failure('insert_paragraphs', error);
|
|
1138
1108
|
}
|
|
1139
1109
|
}
|
|
1140
1110
|
async function runInsertHeading(doc, args) {
|
|
@@ -2251,52 +2221,75 @@ async function runSplitList(doc, args) {
|
|
|
2251
2221
|
return failedReceipt('split_list', err, pre);
|
|
2252
2222
|
}
|
|
2253
2223
|
}
|
|
2224
|
+
async function resolveFactPlacement(placement, facts) {
|
|
2225
|
+
if (!placement || placement.at === 'document_end')
|
|
2226
|
+
return { kind: 'documentEnd' };
|
|
2227
|
+
if (placement.at === 'document_start')
|
|
2228
|
+
return { kind: 'documentStart' };
|
|
2229
|
+
const target = await facts.target(placement.selector);
|
|
2230
|
+
if (!target)
|
|
2231
|
+
throw new SuperDocCliError('placement selector did not resolve to a unique body block', {
|
|
2232
|
+
code: 'INVALID_ARGUMENT',
|
|
2233
|
+
});
|
|
2234
|
+
return { kind: placement.at, target: { kind: 'block', nodeType: target.nodeType, nodeId: target.nodeId } };
|
|
2235
|
+
}
|
|
2236
|
+
function placementSelectors(placement) {
|
|
2237
|
+
return placement?.at === 'before' || placement?.at === 'after' ? [placement.selector] : [];
|
|
2238
|
+
}
|
|
2254
2239
|
async function runCreateTable(doc, args) {
|
|
2255
|
-
const
|
|
2240
|
+
const checks = [
|
|
2241
|
+
{ kind: 'revision-changed' },
|
|
2242
|
+
{ kind: 'block-count-delta', nodeType: 'table', delta: 1 },
|
|
2243
|
+
];
|
|
2244
|
+
const context = new ExecutionContext(doc, {
|
|
2245
|
+
evidence: args.evidence,
|
|
2246
|
+
selectors: placementSelectors(args.placement),
|
|
2247
|
+
checks,
|
|
2248
|
+
});
|
|
2256
2249
|
try {
|
|
2257
|
-
if (!Number.isInteger(args.rows) || args.rows < 1 || !Number.isInteger(args.columns) || args.columns < 1)
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
const placement =
|
|
2261
|
-
const
|
|
2262
|
-
const
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
const appliedCells = await applyTableCellTexts(doc, createdTableNodeId, insertedTableOrdinal, flattenCellTexts(args.cellTexts), args.changeMode);
|
|
2269
|
-
if (appliedCells.length > 0) {
|
|
2270
|
-
executedOperations.push({
|
|
2271
|
-
operationId: 'doc.mutations.apply',
|
|
2272
|
-
rationale: `Populated ${appliedCells.length} table cells.`,
|
|
2273
|
-
});
|
|
2250
|
+
if (!Number.isInteger(args.rows) || args.rows < 1 || !Number.isInteger(args.columns) || args.columns < 1)
|
|
2251
|
+
throw new Error('rows and columns must be positive integers');
|
|
2252
|
+
const pre = await context.start();
|
|
2253
|
+
const placement = await resolveFactPlacement(args.placement, pre);
|
|
2254
|
+
const result = await executeCreateTable(context.document, args, placement);
|
|
2255
|
+
const tableNodeId = asString(asRecord(asRecord(result)?.table)?.nodeId);
|
|
2256
|
+
if (args.cellTexts && tableNodeId) {
|
|
2257
|
+
const cells = flattenCellTexts(args.cellTexts).filter((cell) => cell.text.trim());
|
|
2258
|
+
if (pre.complete) {
|
|
2259
|
+
const applied = await applyTableCellTexts(context.document, tableNodeId, estimateInsertedTableOrdinal(pre.complete, placement), cells, args.changeMode);
|
|
2260
|
+
await styleTableCells(context.document, applied, args.changeMode);
|
|
2274
2261
|
}
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2262
|
+
else {
|
|
2263
|
+
for (const cell of cells)
|
|
2264
|
+
await context.document.tables.setCellText({
|
|
2265
|
+
target: { kind: 'block', nodeType: 'table', nodeId: tableNodeId },
|
|
2266
|
+
rowIndex: cell.rowIndex,
|
|
2267
|
+
columnIndex: cell.columnIndex,
|
|
2268
|
+
text: cell.text,
|
|
2269
|
+
changeMode: args.changeMode,
|
|
2270
|
+
});
|
|
2271
|
+
const styled = cells.filter((cell) => cell.runs?.length || cell.marks);
|
|
2272
|
+
if (styled.length) {
|
|
2273
|
+
const identities = await doc.tables.getCells({ nodeId: tableNodeId });
|
|
2274
|
+
const resolved = styled.map((cell) => {
|
|
2275
|
+
const identity = identities.cells.find((item) => item.rowIndex === cell.rowIndex &&
|
|
2276
|
+
item.columnIndex <= cell.columnIndex &&
|
|
2277
|
+
item.columnIndex + item.colspan > cell.columnIndex);
|
|
2278
|
+
if (!identity?.firstParagraphNodeId)
|
|
2279
|
+
throw new Error('Required cell paragraph identity is unavailable.');
|
|
2280
|
+
return { ...cell, nodeId: identity.firstParagraphNodeId };
|
|
2281
|
+
});
|
|
2282
|
+
const styling = await applyItemStyling(context.document, resolved.map((cell) => cell.nodeId), resolved, args.changeMode);
|
|
2283
|
+
if (styling.skipped)
|
|
2284
|
+
throw new Error(`Requested cell styling failed: ${styling.skipped}`);
|
|
2285
|
+
}
|
|
2281
2286
|
}
|
|
2282
2287
|
}
|
|
2283
|
-
const post = await
|
|
2284
|
-
|
|
2285
|
-
{ kind: 'revision-changed' },
|
|
2286
|
-
{ kind: 'block-count-delta', nodeType: 'table', delta: 1 },
|
|
2287
|
-
]);
|
|
2288
|
-
return {
|
|
2289
|
-
status: verification.every((v) => v.passed) ? 'ok' : 'failed',
|
|
2290
|
-
intent: 'create_table',
|
|
2291
|
-
preSnapshot: { revision: pre.revision, counts: pre.counts },
|
|
2292
|
-
postSnapshot: { revision: post.revision, counts: post.counts },
|
|
2293
|
-
selectedTargets: [],
|
|
2294
|
-
executedOperations,
|
|
2295
|
-
verification,
|
|
2296
|
-
};
|
|
2288
|
+
const post = await context.finish();
|
|
2289
|
+
return context.receipt('create_table', await evaluateFactChecks(pre, post, checks));
|
|
2297
2290
|
}
|
|
2298
|
-
catch (
|
|
2299
|
-
return
|
|
2291
|
+
catch (error) {
|
|
2292
|
+
return context.failure('create_table', error);
|
|
2300
2293
|
}
|
|
2301
2294
|
}
|
|
2302
2295
|
async function runCommentParagraphs(doc, args) {
|
|
@@ -2579,67 +2572,39 @@ async function runReplyToComment(doc, args) {
|
|
|
2579
2572
|
}
|
|
2580
2573
|
}
|
|
2581
2574
|
async function runRewriteBlock(doc, args) {
|
|
2582
|
-
const
|
|
2575
|
+
const context = new ExecutionContext(doc, { evidence: args.evidence, selectors: [args.selector] });
|
|
2583
2576
|
try {
|
|
2584
|
-
const
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
// it can be re-applied over the unchanged prefix/suffix afterward.
|
|
2593
|
-
const preRuns = await captureBlockRuns(doc, target.nodeId, target.text);
|
|
2594
|
-
const steps = [
|
|
2577
|
+
const pre = await context.start();
|
|
2578
|
+
const target = await pre.target(args.selector);
|
|
2579
|
+
if (!target)
|
|
2580
|
+
throw new Error('selector did not resolve to a body block');
|
|
2581
|
+
context.selectedTargets.push({ selector: args.selector, matched: [target.nodeId] });
|
|
2582
|
+
const text = normalizeTitleLikeRewriteText(target.text, args.text);
|
|
2583
|
+
const runs = await captureBlockRuns(doc, target.nodeId, target.text);
|
|
2584
|
+
await executeMutations(context.document, [
|
|
2595
2585
|
{
|
|
2596
2586
|
id: 'rewrite-block-1',
|
|
2597
2587
|
op: 'text.rewrite',
|
|
2598
2588
|
where: { by: 'block', nodeType: target.nodeType, nodeId: target.nodeId },
|
|
2599
|
-
args: {
|
|
2600
|
-
replacement: { text: normalizedText },
|
|
2601
|
-
style: preserveRewriteStyle(),
|
|
2602
|
-
},
|
|
2589
|
+
args: { replacement: { text }, style: preserveRewriteStyle() },
|
|
2603
2590
|
},
|
|
2604
|
-
];
|
|
2605
|
-
|
|
2606
|
-
const
|
|
2607
|
-
const
|
|
2608
|
-
const rewrittenBlock = findSnapshotTextByNodeId(post, target.nodeId);
|
|
2591
|
+
], args.changeMode, pre.revision);
|
|
2592
|
+
await preserveRunPatternAfterRewrite(context.document, target.nodeId, target.text, runs, text);
|
|
2593
|
+
const post = await context.finish();
|
|
2594
|
+
const rewritten = await post.block(target.nodeId);
|
|
2609
2595
|
const verification = [
|
|
2610
2596
|
revisionVerification(pre.revision, post.revision, true),
|
|
2611
2597
|
{
|
|
2612
|
-
check: { kind: 'block-text-contains', nodeId: target.nodeId, text
|
|
2613
|
-
passed: !!
|
|
2598
|
+
check: { kind: 'block-text-contains', nodeId: target.nodeId, text },
|
|
2599
|
+
passed: !!rewritten && verifyRewrittenBlockText(rewritten.text, text, args.changeMode),
|
|
2614
2600
|
},
|
|
2615
2601
|
];
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
status: changed && rewritten ? 'ok' : 'failed',
|
|
2620
|
-
intent: 'rewrite_block',
|
|
2621
|
-
preSnapshot: { revision: pre.revision, counts: pre.counts },
|
|
2622
|
-
postSnapshot: { revision: post.revision, counts: post.counts },
|
|
2623
|
-
selectedTargets: [{ selector: args.selector, matched: [target.nodeId] }],
|
|
2624
|
-
executedOperations: [
|
|
2625
|
-
{ operationId: 'doc.mutations.apply', result },
|
|
2626
|
-
...(preservation ? [{ operationId: 'doc.format.apply', result: preservation }] : []),
|
|
2627
|
-
],
|
|
2628
|
-
verification,
|
|
2629
|
-
errors: changed && rewritten
|
|
2630
|
-
? undefined
|
|
2631
|
-
: [
|
|
2632
|
-
{
|
|
2633
|
-
code: 'ACTION_FAILED',
|
|
2634
|
-
message: changed
|
|
2635
|
-
? 'rewrite_block did not produce the requested rewritten text for the selected block'
|
|
2636
|
-
: 'rewrite_block produced no change for the selected block; keep the same target and provide a changed rewrite',
|
|
2637
|
-
},
|
|
2638
|
-
],
|
|
2639
|
-
};
|
|
2602
|
+
return context.receipt('rewrite_block', verification, verification.every((check) => check.passed)
|
|
2603
|
+
? {}
|
|
2604
|
+
: { errors: [{ code: 'ACTION_FAILED', message: 'rewrite_block did not produce the requested changed text' }] });
|
|
2640
2605
|
}
|
|
2641
|
-
catch (
|
|
2642
|
-
return
|
|
2606
|
+
catch (error) {
|
|
2607
|
+
return context.failure('rewrite_block', error);
|
|
2643
2608
|
}
|
|
2644
2609
|
}
|
|
2645
2610
|
const RUN_BOOL_MARKS = ['bold', 'italic', 'underline', 'strike'];
|
|
@@ -3290,43 +3255,30 @@ async function runApplyStyle(doc, args) {
|
|
|
3290
3255
|
* escape to superdoc_execute_code and apply the alignment untracked (no pPrChange).
|
|
3291
3256
|
*/
|
|
3292
3257
|
async function runFormatParagraph(doc, args) {
|
|
3293
|
-
const
|
|
3258
|
+
const context = new ExecutionContext(doc, { evidence: args.evidence, selectors: [args.selector] });
|
|
3294
3259
|
try {
|
|
3295
|
-
const target = selectorToBlockTarget(args.selector, pre);
|
|
3296
|
-
if (!target) {
|
|
3297
|
-
return failedReceipt('format_paragraph', new Error('selector did not resolve to a unique body block'), pre);
|
|
3298
|
-
}
|
|
3299
3260
|
const alignment = typeof args.alignment === 'string' ? args.alignment.trim().toLowerCase() : '';
|
|
3300
|
-
const
|
|
3301
|
-
if (!['left', 'center', 'right', 'justify'].includes(
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
const
|
|
3261
|
+
const normalized = alignment === 'both' ? 'justify' : alignment;
|
|
3262
|
+
if (!['left', 'center', 'right', 'justify'].includes(normalized))
|
|
3263
|
+
throw new Error('format_paragraph requires alignment: left | center | right | justify');
|
|
3264
|
+
const pre = await context.start();
|
|
3265
|
+
const target = await pre.target(args.selector);
|
|
3266
|
+
if (!target)
|
|
3267
|
+
throw new Error('selector did not resolve to a unique body block');
|
|
3268
|
+
context.selectedTargets.push({ selector: args.selector, matched: [target.nodeId] });
|
|
3269
|
+
await executeMutations(context.document, [
|
|
3305
3270
|
{
|
|
3306
3271
|
id: 'format-paragraph-1',
|
|
3307
3272
|
op: 'format.apply',
|
|
3308
3273
|
where: { by: 'block', nodeType: target.nodeType, nodeId: target.nodeId },
|
|
3309
|
-
args: { alignment:
|
|
3274
|
+
args: { alignment: normalized, scope: 'block' },
|
|
3310
3275
|
},
|
|
3311
|
-
];
|
|
3312
|
-
const
|
|
3313
|
-
|
|
3314
|
-
const verification = evaluateChecks(pre, post, [{ kind: 'revision-changed' }]);
|
|
3315
|
-
return {
|
|
3316
|
-
status: verification.every((v) => v.passed) ? 'ok' : 'failed',
|
|
3317
|
-
intent: `format_paragraph: ${normalizedAlignment}`,
|
|
3318
|
-
preSnapshot: { revision: pre.revision, counts: pre.counts },
|
|
3319
|
-
postSnapshot: { revision: post.revision, counts: post.counts },
|
|
3320
|
-
selectedTargets: [{ selector: args.selector, matched: [target.nodeId] }],
|
|
3321
|
-
applied: { alignment: normalizedAlignment },
|
|
3322
|
-
executedOperations: [
|
|
3323
|
-
{ operationId: 'doc.mutations.apply', result: compactOpResult(result), rationale: `align ${target.nodeId}` },
|
|
3324
|
-
],
|
|
3325
|
-
verification,
|
|
3326
|
-
};
|
|
3276
|
+
], args.changeMode, pre.revision);
|
|
3277
|
+
const post = await context.finish();
|
|
3278
|
+
return context.receipt(`format_paragraph: ${normalized}`, await evaluateFactChecks(pre, post, [{ kind: 'revision-changed' }]), { applied: { alignment: normalized } });
|
|
3327
3279
|
}
|
|
3328
|
-
catch (
|
|
3329
|
-
return
|
|
3280
|
+
catch (error) {
|
|
3281
|
+
return context.failure('format_paragraph', error);
|
|
3330
3282
|
}
|
|
3331
3283
|
}
|
|
3332
3284
|
/**
|
|
@@ -5982,8 +5934,9 @@ async function matchOneBlock(doc, postBlocks, preIds, created, createdIndex, anc
|
|
|
5982
5934
|
const formatApplyFn = maybeMethod(doc, ['format', 'apply']);
|
|
5983
5935
|
if (formatApplyFn) {
|
|
5984
5936
|
const textLength = (created.textPreview ?? '').length;
|
|
5985
|
-
const
|
|
5986
|
-
|
|
5937
|
+
const full = createdText ||
|
|
5938
|
+
(await doc.blocks.list({ nodeIds: [created.nodeId], limit: 1, includeText: true })).blocks.find((block) => block.nodeId === created.nodeId)?.text ||
|
|
5939
|
+
'';
|
|
5987
5940
|
await formatApplyFn({
|
|
5988
5941
|
blockId: created.nodeId,
|
|
5989
5942
|
start: 0,
|
|
@@ -6180,11 +6133,11 @@ async function matchBlockRunPattern(doc, reference, created, createdText) {
|
|
|
6180
6133
|
}
|
|
6181
6134
|
return Object.keys(applied).length > 0 ? { matchedSibling: reference.nodeId, ...applied } : null;
|
|
6182
6135
|
}
|
|
6183
|
-
async function matchInsertedBlockFormatting(doc, preBlocks, receipt, args) {
|
|
6136
|
+
async function matchInsertedBlockFormatting(doc, preBlocks, receipt, args, availableRows) {
|
|
6184
6137
|
if (receipt.status !== 'ok')
|
|
6185
6138
|
return receipt;
|
|
6186
6139
|
try {
|
|
6187
|
-
const postBlocks = await listBlockRows(doc);
|
|
6140
|
+
const postBlocks = availableRows ?? (await listBlockRows(doc));
|
|
6188
6141
|
const preIds = new Set(preBlocks.map((b) => b.nodeId));
|
|
6189
6142
|
const createdRows = postBlocks
|
|
6190
6143
|
.map((b, i) => ({ row: b, index: i }))
|
|
@@ -6475,9 +6428,9 @@ async function dispatchSuperdocPerformAction(doc, args) {
|
|
|
6475
6428
|
});
|
|
6476
6429
|
}
|
|
6477
6430
|
const headingLevel = asNumber(args.headingLevel);
|
|
6478
|
-
|
|
6479
|
-
const receipt = await runInsertParagraphs(doc, {
|
|
6431
|
+
return runInsertParagraphs(doc, {
|
|
6480
6432
|
action,
|
|
6433
|
+
evidence: args.evidence,
|
|
6481
6434
|
texts,
|
|
6482
6435
|
textSpecs,
|
|
6483
6436
|
placement: parsePlacement(args.placement),
|
|
@@ -6485,8 +6438,7 @@ async function dispatchSuperdocPerformAction(doc, args) {
|
|
|
6485
6438
|
headingLevel: headingLevel != null && Number.isInteger(headingLevel) && headingLevel >= 1 && headingLevel <= 6
|
|
6486
6439
|
? headingLevel
|
|
6487
6440
|
: undefined,
|
|
6488
|
-
});
|
|
6489
|
-
return matchInsertedBlockFormatting(doc, preRows, receipt, args);
|
|
6441
|
+
}, args);
|
|
6490
6442
|
}
|
|
6491
6443
|
case 'insert_heading': {
|
|
6492
6444
|
const text = asString(args.text);
|
|
@@ -6610,6 +6562,7 @@ async function dispatchSuperdocPerformAction(doc, args) {
|
|
|
6610
6562
|
}
|
|
6611
6563
|
return runCreateTable(doc, {
|
|
6612
6564
|
action,
|
|
6565
|
+
evidence: args.evidence,
|
|
6613
6566
|
rows,
|
|
6614
6567
|
columns,
|
|
6615
6568
|
cellTexts: parseCellTexts(args.cellTexts),
|
|
@@ -6633,16 +6586,41 @@ async function dispatchSuperdocPerformAction(doc, args) {
|
|
|
6633
6586
|
}
|
|
6634
6587
|
case 'add_comments': {
|
|
6635
6588
|
const commentText = asString(args.commentText);
|
|
6636
|
-
|
|
6637
|
-
const selectors = Array.isArray(args.selectors)
|
|
6638
|
-
? args.selectors.map((s) => parseSelector(s)).filter((s) => Boolean(s))
|
|
6639
|
-
: undefined;
|
|
6640
|
-
if (!commentText || (!selector && !(selectors && selectors.length))) {
|
|
6589
|
+
if (!commentText) {
|
|
6641
6590
|
throw new SuperDocCliError('add_comments requires "commentText" and a "selector" or non-empty "selectors" array', {
|
|
6642
6591
|
code: 'INVALID_ARGUMENT',
|
|
6643
6592
|
});
|
|
6644
6593
|
}
|
|
6645
|
-
|
|
6594
|
+
const hasSelector = args.selector != null;
|
|
6595
|
+
const hasSelectors = args.selectors != null;
|
|
6596
|
+
if (hasSelector && hasSelectors) {
|
|
6597
|
+
throw new SuperDocCliError('add_comments requires either "selector" or "selectors", not both', {
|
|
6598
|
+
code: 'INVALID_ARGUMENT',
|
|
6599
|
+
});
|
|
6600
|
+
}
|
|
6601
|
+
if (hasSelectors) {
|
|
6602
|
+
if (!Array.isArray(args.selectors) || args.selectors.length === 0) {
|
|
6603
|
+
throw new SuperDocCliError('add_comments requires "selectors" to be a non-empty array', {
|
|
6604
|
+
code: 'INVALID_ARGUMENT',
|
|
6605
|
+
});
|
|
6606
|
+
}
|
|
6607
|
+
// Parse the complete batch before mutation. Filtering an invalid entry
|
|
6608
|
+
// would make the receipt report success for only part of the request.
|
|
6609
|
+
const selectors = [];
|
|
6610
|
+
for (const [index, raw] of args.selectors.entries()) {
|
|
6611
|
+
const parsed = parseSelector(raw);
|
|
6612
|
+
if (!parsed) {
|
|
6613
|
+
throw new SuperDocCliError(`add_comments selectors[${index}] is not a valid selector; no comments were added`, { code: 'INVALID_ARGUMENT' });
|
|
6614
|
+
}
|
|
6615
|
+
selectors.push(parsed);
|
|
6616
|
+
}
|
|
6617
|
+
return runAddComments(doc, { action, commentText, selectors });
|
|
6618
|
+
}
|
|
6619
|
+
const selector = parseSelector(args.selector);
|
|
6620
|
+
if (!selector) {
|
|
6621
|
+
throw new SuperDocCliError('add_comments requires "selector" to be valid or "selectors" to be a non-empty array', { code: 'INVALID_ARGUMENT' });
|
|
6622
|
+
}
|
|
6623
|
+
return runAddComments(doc, { action, commentText, selector });
|
|
6646
6624
|
}
|
|
6647
6625
|
case 'resolve_comments':
|
|
6648
6626
|
return runResolveComments(doc, {
|
|
@@ -6671,6 +6649,7 @@ async function dispatchSuperdocPerformAction(doc, args) {
|
|
|
6671
6649
|
}
|
|
6672
6650
|
return runRewriteBlock(doc, {
|
|
6673
6651
|
action,
|
|
6652
|
+
evidence: args.evidence,
|
|
6674
6653
|
text,
|
|
6675
6654
|
selector,
|
|
6676
6655
|
changeMode: parseChangeMode(args.changeMode),
|
|
@@ -6924,6 +6903,7 @@ async function dispatchSuperdocPerformAction(doc, args) {
|
|
|
6924
6903
|
}
|
|
6925
6904
|
return runFormatParagraph(doc, {
|
|
6926
6905
|
action,
|
|
6906
|
+
evidence: args.evidence,
|
|
6927
6907
|
selector,
|
|
6928
6908
|
alignment: asString(args.alignment) ?? '',
|
|
6929
6909
|
changeMode: parseChangeMode(args.changeMode),
|
package/dist/agent/catalog.cjs
CHANGED
|
@@ -146,6 +146,11 @@ const ACTION_ARG_SCHEMA = {
|
|
|
146
146
|
},
|
|
147
147
|
finds: { type: 'array', items: { type: 'string' } },
|
|
148
148
|
caseSensitive: { type: 'boolean' },
|
|
149
|
+
evidence: {
|
|
150
|
+
type: 'string',
|
|
151
|
+
enum: ['required', 'full'],
|
|
152
|
+
description: 'Required evidence by default; full includes complete count summaries.',
|
|
153
|
+
},
|
|
149
154
|
changeMode: { type: 'string', enum: ['direct', 'tracked'] },
|
|
150
155
|
placement: { type: 'object', additionalProperties: true, properties: {} },
|
|
151
156
|
selector: SELECTOR_SCHEMA,
|
|
@@ -390,6 +395,11 @@ const AGENT_TOOL_DEFINITIONS = [
|
|
|
390
395
|
additionalProperties: false,
|
|
391
396
|
required: ['plan'],
|
|
392
397
|
properties: {
|
|
398
|
+
evidence: {
|
|
399
|
+
type: 'string',
|
|
400
|
+
enum: ['required', 'full'],
|
|
401
|
+
description: 'Required evidence by default; full includes complete count summaries.',
|
|
402
|
+
},
|
|
393
403
|
plan: {
|
|
394
404
|
type: 'object',
|
|
395
405
|
additionalProperties: false,
|
|
@@ -428,6 +438,11 @@ const AGENT_TOOL_DEFINITIONS = [
|
|
|
428
438
|
additionalProperties: false,
|
|
429
439
|
required: ['checks'],
|
|
430
440
|
properties: {
|
|
441
|
+
evidence: {
|
|
442
|
+
type: 'string',
|
|
443
|
+
enum: ['required', 'full'],
|
|
444
|
+
description: 'Required evidence by default; full includes complete count summaries.',
|
|
445
|
+
},
|
|
431
446
|
checks: {
|
|
432
447
|
type: 'array',
|
|
433
448
|
minItems: 1,
|