@superdoc/sdk 2.13.0-next.9 → 2.13.1-next.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/agent/actions.cjs +112 -3
- package/dist/agent/actions.d.ts +19 -0
- package/dist/agent/actions.js +110 -3
- package/dist/agent/catalog.cjs +52 -8
- package/dist/agent/catalog.d.ts +54 -1
- package/dist/agent/catalog.js +55 -8
- package/dist/embedded-tools.generated.cjs +5 -5
- package/dist/embedded-tools.generated.js +5 -5
- package/dist/generated/client.d.ts +3664 -905
- package/dist/generated/contract.cjs +1493 -4
- package/dist/generated/contract.js +1494 -4
- package/dist/index.cjs +2 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/presets/core.cjs +77 -15
- package/dist/presets/core.js +79 -17
- package/dist/presets.d.ts +23 -0
- package/dist/runtime/host.cjs +24 -15
- package/dist/runtime/host.d.ts +3 -1
- package/dist/runtime/host.js +24 -15
- 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/dist/tools.cjs +34 -8
- package/dist/tools.d.ts +28 -3
- package/dist/tools.js +34 -8
- package/package.json +7 -7
- package/tools/catalog.json +3517 -427
- package/tools/tools-policy.json +1 -1
- package/tools/tools.anthropic.json +3371 -281
- package/tools/tools.generic.json +4089 -999
- package/tools/tools.openai.json +4072 -982
- package/tools/tools.vercel.json +4072 -982
package/dist/agent/actions.cjs
CHANGED
|
@@ -226,6 +226,53 @@ const ACTION_ARGS = {
|
|
|
226
226
|
insert_page_break: ['selector'],
|
|
227
227
|
add_hyperlink: ['text', 'url', 'tooltip'],
|
|
228
228
|
};
|
|
229
|
+
const ACTION_DISPOSITIONS = {
|
|
230
|
+
insert_paragraphs: 'tracked',
|
|
231
|
+
insert_heading: 'tracked',
|
|
232
|
+
replace_text: 'tracked',
|
|
233
|
+
delete_text: 'tracked',
|
|
234
|
+
delete_blocks: 'tracked',
|
|
235
|
+
append_list: 'tracked',
|
|
236
|
+
create_table: 'tracked',
|
|
237
|
+
comment_paragraphs: 'direct',
|
|
238
|
+
add_comments: 'direct',
|
|
239
|
+
resolve_comments: 'direct',
|
|
240
|
+
reply_to_comment: 'direct',
|
|
241
|
+
rewrite_block: 'tracked',
|
|
242
|
+
accept_tracked_changes: 'direct',
|
|
243
|
+
reject_tracked_changes: 'direct',
|
|
244
|
+
normalize_body_font_size: 'tracked',
|
|
245
|
+
set_font_family: 'tracked',
|
|
246
|
+
apply_letter_spacing: 'tracked',
|
|
247
|
+
fill_placeholders: 'tracked',
|
|
248
|
+
move_range: 'direct',
|
|
249
|
+
insert_toc: 'tracked',
|
|
250
|
+
insert_table_row: 'tracked',
|
|
251
|
+
insert_table_column: 'tracked',
|
|
252
|
+
delete_table_row: 'tracked',
|
|
253
|
+
delete_table_column: 'tracked',
|
|
254
|
+
split_table: 'tracked',
|
|
255
|
+
convert_list: 'tracked',
|
|
256
|
+
split_list: 'direct',
|
|
257
|
+
undo_changes: 'direct',
|
|
258
|
+
redo_changes: 'direct',
|
|
259
|
+
attach_numbering: 'tracked',
|
|
260
|
+
add_list_items: 'tracked',
|
|
261
|
+
format_text: 'tracked',
|
|
262
|
+
apply_style: 'direct',
|
|
263
|
+
format_paragraph: 'tracked',
|
|
264
|
+
move_text: 'tracked',
|
|
265
|
+
style_table: 'direct',
|
|
266
|
+
move_table: 'direct',
|
|
267
|
+
delete_table: 'tracked',
|
|
268
|
+
set_paragraph_spacing: 'direct',
|
|
269
|
+
insert_page_break: 'direct',
|
|
270
|
+
add_hyperlink: 'direct',
|
|
271
|
+
};
|
|
272
|
+
/** Fresh copy of {@link ACTION_DISPOSITIONS} for integrators (safe to mutate). */
|
|
273
|
+
function getActionDispositions() {
|
|
274
|
+
return { ...ACTION_DISPOSITIONS };
|
|
275
|
+
}
|
|
229
276
|
function isActionName(value) {
|
|
230
277
|
return typeof value === 'string' && ACTION_NAMES.includes(value);
|
|
231
278
|
}
|
|
@@ -641,8 +688,8 @@ function evaluateChecks(pre, post, checks) {
|
|
|
641
688
|
else if (check.kind === 'tracked-change-count-delta') {
|
|
642
689
|
results.push({
|
|
643
690
|
check,
|
|
644
|
-
passed: post.trackedChanges
|
|
645
|
-
detail: `pre=${pre.trackedChanges
|
|
691
|
+
passed: post.counts.trackedChanges - pre.counts.trackedChanges === check.delta,
|
|
692
|
+
detail: `pre=${pre.counts.trackedChanges} post=${post.counts.trackedChanges}`,
|
|
646
693
|
});
|
|
647
694
|
}
|
|
648
695
|
else if (check.kind === 'revision-changed') {
|
|
@@ -1544,11 +1591,25 @@ async function runDeleteBlocks(doc, args) {
|
|
|
1544
1591
|
const changeMode = parseChangeMode(args.changeMode);
|
|
1545
1592
|
const executedOperations = [];
|
|
1546
1593
|
const deleted = [];
|
|
1594
|
+
const addressedTrackedChangeIds = new Set();
|
|
1595
|
+
let allTrackedEffectsAddressed = true;
|
|
1547
1596
|
for (const target of targets) {
|
|
1548
1597
|
try {
|
|
1549
1598
|
const result = await deleteFn({ target: { kind: 'block', nodeType: target.nodeType, nodeId: target.nodeId } }, changeMode ? { changeMode } : undefined);
|
|
1550
1599
|
executedOperations.push({ operationId: 'doc.blocks.delete', result: compactOpResult(result) });
|
|
1551
1600
|
deleted.push({ nodeId: target.nodeId, nodeType: target.nodeType });
|
|
1601
|
+
if (changeMode === 'tracked') {
|
|
1602
|
+
// One block transaction can create multiple logical review entities
|
|
1603
|
+
// (for example, text plus its section break). Exact receipt refs let
|
|
1604
|
+
// us fail closed on missing entities. Older handles omit refs, and
|
|
1605
|
+
// adjacent deletions can coalesce into one review entity, so their
|
|
1606
|
+
// truthful fallback is the positive observed inventory delta rather
|
|
1607
|
+
// than one assumed entity per operation.
|
|
1608
|
+
const refIds = trackedChangeRefIds(result);
|
|
1609
|
+
allTrackedEffectsAddressed &&= refIds.size > 0;
|
|
1610
|
+
for (const entityId of refIds)
|
|
1611
|
+
addressedTrackedChangeIds.add(entityId);
|
|
1612
|
+
}
|
|
1552
1613
|
}
|
|
1553
1614
|
catch (err) {
|
|
1554
1615
|
errors$1.push({
|
|
@@ -1562,14 +1623,37 @@ async function runDeleteBlocks(doc, args) {
|
|
|
1562
1623
|
// Tracked deletions leave the block in place until the revision is decided,
|
|
1563
1624
|
// so the block count cannot move — count the structural revisions instead.
|
|
1564
1625
|
// Direct deletions must show one fewer block of each deleted node type.
|
|
1626
|
+
const observedTrackedChangeDelta = post.counts.trackedChanges - pre.counts.trackedChanges;
|
|
1627
|
+
// A later deletion can coalesce the batch and replace entity identities
|
|
1628
|
+
// returned by earlier operation receipts. For a multi-operation batch the
|
|
1629
|
+
// final inventory is therefore the authoritative count. A single operation
|
|
1630
|
+
// keeps using its addressed refs so fan-out remains verifiable even when
|
|
1631
|
+
// the inventory listing is capped.
|
|
1632
|
+
const expectedTrackedChangeDelta = deleted.length > 1
|
|
1633
|
+
? observedTrackedChangeDelta
|
|
1634
|
+
: allTrackedEffectsAddressed
|
|
1635
|
+
? addressedTrackedChangeIds.size
|
|
1636
|
+
: Math.max(observedTrackedChangeDelta, addressedTrackedChangeIds.size);
|
|
1565
1637
|
const checks = changeMode === 'tracked'
|
|
1566
|
-
? [
|
|
1638
|
+
? [
|
|
1639
|
+
{
|
|
1640
|
+
kind: 'tracked-change-count-delta',
|
|
1641
|
+
delta: expectedTrackedChangeDelta,
|
|
1642
|
+
},
|
|
1643
|
+
]
|
|
1567
1644
|
: [...new Set(deleted.map((entry) => entry.nodeType))].map((nodeType) => ({
|
|
1568
1645
|
kind: 'block-count-delta',
|
|
1569
1646
|
nodeType,
|
|
1570
1647
|
delta: -deleted.filter((entry) => entry.nodeType === nodeType).length,
|
|
1571
1648
|
}));
|
|
1572
1649
|
const verification = evaluateChecks(pre, post, checks);
|
|
1650
|
+
if (changeMode === 'tracked' && observedTrackedChangeDelta <= 0) {
|
|
1651
|
+
verification[0] = {
|
|
1652
|
+
...verification[0],
|
|
1653
|
+
passed: false,
|
|
1654
|
+
detail: `pre=${pre.counts.trackedChanges} post=${post.counts.trackedChanges}; no tracked review entity was created`,
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1573
1657
|
// Compare against the DEDUPED target list: two selectors naming the same
|
|
1574
1658
|
// block are one deletion, and counting the raw selectors would report a
|
|
1575
1659
|
// fully applied call as `partial` with no error to explain it. Selectors
|
|
@@ -4513,6 +4597,15 @@ function compactOpResult(result) {
|
|
|
4513
4597
|
compact.stepCount = result.steps.length;
|
|
4514
4598
|
return compact;
|
|
4515
4599
|
}
|
|
4600
|
+
function trackedChangeRefIds(result) {
|
|
4601
|
+
const refs = asRecord(result)?.trackedChangeRefs;
|
|
4602
|
+
if (!Array.isArray(refs))
|
|
4603
|
+
return new Set();
|
|
4604
|
+
return new Set(refs.flatMap((entry) => {
|
|
4605
|
+
const entityId = asString(asRecord(entry)?.entityId);
|
|
4606
|
+
return entityId ? [entityId] : [];
|
|
4607
|
+
}));
|
|
4608
|
+
}
|
|
4516
4609
|
function parseScopedReplaceArgs(args, changeMode) {
|
|
4517
4610
|
if (args.action !== 'replace_text')
|
|
4518
4611
|
return null;
|
|
@@ -4817,10 +4910,24 @@ function leadInShapeOf(blockText, labelText, quote) {
|
|
|
4817
4910
|
if (occurrence > 0)
|
|
4818
4911
|
return { kind: 'inside', delimiter: inside, occurrence };
|
|
4819
4912
|
}
|
|
4913
|
+
// A single emphasised word followed by a space and plain prose: the token
|
|
4914
|
+
// itself is the label ("WHEREAS the Party…"). Multi-word runs stay
|
|
4915
|
+
// unrecognised — an emphasised opening clause is prose, not a label.
|
|
4916
|
+
if (/^\S+\s$/.test(labelText) && blockText.length > labelText.length && blockText.startsWith(labelText)) {
|
|
4917
|
+
return { kind: 'word', label: labelText.trimEnd() };
|
|
4918
|
+
}
|
|
4820
4919
|
return null;
|
|
4821
4920
|
}
|
|
4822
4921
|
/** The span of `text` that carries the label look, or null when it has none. */
|
|
4823
4922
|
function leadInSpanOf(text, shape) {
|
|
4923
|
+
if (shape.kind === 'word') {
|
|
4924
|
+
// Only the SAME token inherits the look — "WHEREAS the Parties…" does,
|
|
4925
|
+
// "The Parties further agree…" does not (no guessing a label there).
|
|
4926
|
+
const next = text.charAt(shape.label.length);
|
|
4927
|
+
return text.startsWith(shape.label) && (next === '' || /\s/.test(next))
|
|
4928
|
+
? { start: 0, end: shape.label.length }
|
|
4929
|
+
: null;
|
|
4930
|
+
}
|
|
4824
4931
|
if (shape.kind === 'quoted') {
|
|
4825
4932
|
const opener = text.slice(0, 2).indexOf(shape.quote);
|
|
4826
4933
|
if (opener < 0)
|
|
@@ -7303,8 +7410,10 @@ function parseSelector(value) {
|
|
|
7303
7410
|
const ACTION_NAMES_LIST = ACTION_NAMES;
|
|
7304
7411
|
|
|
7305
7412
|
exports.ACTION_ARGS = ACTION_ARGS;
|
|
7413
|
+
exports.ACTION_DISPOSITIONS = ACTION_DISPOSITIONS;
|
|
7306
7414
|
exports.ACTION_GROUPS = ACTION_GROUPS;
|
|
7307
7415
|
exports.ACTION_HINTS = ACTION_HINTS;
|
|
7308
7416
|
exports.ACTION_NAMES_LIST = ACTION_NAMES_LIST;
|
|
7417
|
+
exports.getActionDispositions = getActionDispositions;
|
|
7309
7418
|
exports.isActionName = isActionName;
|
|
7310
7419
|
exports.superdocPerformAction = superdocPerformAction;
|
package/dist/agent/actions.d.ts
CHANGED
|
@@ -433,6 +433,25 @@ export declare const ACTION_GROUPS: ReadonlyArray<{
|
|
|
433
433
|
* action at compile time.
|
|
434
434
|
*/
|
|
435
435
|
export declare const ACTION_ARGS: Record<ActionName, readonly string[]>;
|
|
436
|
+
/**
|
|
437
|
+
* What an action does with `changeMode`.
|
|
438
|
+
*
|
|
439
|
+
* - `tracked`: honors `changeMode: "tracked"` and records a reviewable tracked
|
|
440
|
+
* change (a multi-step action may run a bookkeeping step directly on purpose —
|
|
441
|
+
* insert_table_column fills the new cells directly so a column shows up as ONE
|
|
442
|
+
* Word-style tracked change, not one per cell).
|
|
443
|
+
* - `direct`: edits the document directly whatever `changeMode` says. Either the
|
|
444
|
+
* action never reads the argument (apply_style, insert_page_break, …), the
|
|
445
|
+
* engine refuses the tracked form (move_range), or the action is a review /
|
|
446
|
+
* comment operation that has no tracked form by nature.
|
|
447
|
+
*
|
|
448
|
+
* Source of truth for the tracked-only agent policy (`enforceChangeMode`):
|
|
449
|
+
* `direct` actions are hidden and refused unless a host allowlists them.
|
|
450
|
+
*/
|
|
451
|
+
export type ActionDisposition = 'tracked' | 'direct';
|
|
452
|
+
export declare const ACTION_DISPOSITIONS: Record<ActionName, ActionDisposition>;
|
|
453
|
+
/** Fresh copy of {@link ACTION_DISPOSITIONS} for integrators (safe to mutate). */
|
|
454
|
+
export declare function getActionDispositions(): Record<ActionName, ActionDisposition>;
|
|
436
455
|
export declare function isActionName(value: unknown): value is ActionName;
|
|
437
456
|
export type ResolveCommentsArgs = {
|
|
438
457
|
action: 'resolve_comments';
|
package/dist/agent/actions.js
CHANGED
|
@@ -223,6 +223,53 @@ export const ACTION_ARGS = {
|
|
|
223
223
|
insert_page_break: ['selector'],
|
|
224
224
|
add_hyperlink: ['text', 'url', 'tooltip'],
|
|
225
225
|
};
|
|
226
|
+
export const ACTION_DISPOSITIONS = {
|
|
227
|
+
insert_paragraphs: 'tracked',
|
|
228
|
+
insert_heading: 'tracked',
|
|
229
|
+
replace_text: 'tracked',
|
|
230
|
+
delete_text: 'tracked',
|
|
231
|
+
delete_blocks: 'tracked',
|
|
232
|
+
append_list: 'tracked',
|
|
233
|
+
create_table: 'tracked',
|
|
234
|
+
comment_paragraphs: 'direct',
|
|
235
|
+
add_comments: 'direct',
|
|
236
|
+
resolve_comments: 'direct',
|
|
237
|
+
reply_to_comment: 'direct',
|
|
238
|
+
rewrite_block: 'tracked',
|
|
239
|
+
accept_tracked_changes: 'direct',
|
|
240
|
+
reject_tracked_changes: 'direct',
|
|
241
|
+
normalize_body_font_size: 'tracked',
|
|
242
|
+
set_font_family: 'tracked',
|
|
243
|
+
apply_letter_spacing: 'tracked',
|
|
244
|
+
fill_placeholders: 'tracked',
|
|
245
|
+
move_range: 'direct',
|
|
246
|
+
insert_toc: 'tracked',
|
|
247
|
+
insert_table_row: 'tracked',
|
|
248
|
+
insert_table_column: 'tracked',
|
|
249
|
+
delete_table_row: 'tracked',
|
|
250
|
+
delete_table_column: 'tracked',
|
|
251
|
+
split_table: 'tracked',
|
|
252
|
+
convert_list: 'tracked',
|
|
253
|
+
split_list: 'direct',
|
|
254
|
+
undo_changes: 'direct',
|
|
255
|
+
redo_changes: 'direct',
|
|
256
|
+
attach_numbering: 'tracked',
|
|
257
|
+
add_list_items: 'tracked',
|
|
258
|
+
format_text: 'tracked',
|
|
259
|
+
apply_style: 'direct',
|
|
260
|
+
format_paragraph: 'tracked',
|
|
261
|
+
move_text: 'tracked',
|
|
262
|
+
style_table: 'direct',
|
|
263
|
+
move_table: 'direct',
|
|
264
|
+
delete_table: 'tracked',
|
|
265
|
+
set_paragraph_spacing: 'direct',
|
|
266
|
+
insert_page_break: 'direct',
|
|
267
|
+
add_hyperlink: 'direct',
|
|
268
|
+
};
|
|
269
|
+
/** Fresh copy of {@link ACTION_DISPOSITIONS} for integrators (safe to mutate). */
|
|
270
|
+
export function getActionDispositions() {
|
|
271
|
+
return { ...ACTION_DISPOSITIONS };
|
|
272
|
+
}
|
|
226
273
|
export function isActionName(value) {
|
|
227
274
|
return typeof value === 'string' && ACTION_NAMES.includes(value);
|
|
228
275
|
}
|
|
@@ -638,8 +685,8 @@ function evaluateChecks(pre, post, checks) {
|
|
|
638
685
|
else if (check.kind === 'tracked-change-count-delta') {
|
|
639
686
|
results.push({
|
|
640
687
|
check,
|
|
641
|
-
passed: post.trackedChanges
|
|
642
|
-
detail: `pre=${pre.trackedChanges
|
|
688
|
+
passed: post.counts.trackedChanges - pre.counts.trackedChanges === check.delta,
|
|
689
|
+
detail: `pre=${pre.counts.trackedChanges} post=${post.counts.trackedChanges}`,
|
|
643
690
|
});
|
|
644
691
|
}
|
|
645
692
|
else if (check.kind === 'revision-changed') {
|
|
@@ -1541,11 +1588,25 @@ async function runDeleteBlocks(doc, args) {
|
|
|
1541
1588
|
const changeMode = parseChangeMode(args.changeMode);
|
|
1542
1589
|
const executedOperations = [];
|
|
1543
1590
|
const deleted = [];
|
|
1591
|
+
const addressedTrackedChangeIds = new Set();
|
|
1592
|
+
let allTrackedEffectsAddressed = true;
|
|
1544
1593
|
for (const target of targets) {
|
|
1545
1594
|
try {
|
|
1546
1595
|
const result = await deleteFn({ target: { kind: 'block', nodeType: target.nodeType, nodeId: target.nodeId } }, changeMode ? { changeMode } : undefined);
|
|
1547
1596
|
executedOperations.push({ operationId: 'doc.blocks.delete', result: compactOpResult(result) });
|
|
1548
1597
|
deleted.push({ nodeId: target.nodeId, nodeType: target.nodeType });
|
|
1598
|
+
if (changeMode === 'tracked') {
|
|
1599
|
+
// One block transaction can create multiple logical review entities
|
|
1600
|
+
// (for example, text plus its section break). Exact receipt refs let
|
|
1601
|
+
// us fail closed on missing entities. Older handles omit refs, and
|
|
1602
|
+
// adjacent deletions can coalesce into one review entity, so their
|
|
1603
|
+
// truthful fallback is the positive observed inventory delta rather
|
|
1604
|
+
// than one assumed entity per operation.
|
|
1605
|
+
const refIds = trackedChangeRefIds(result);
|
|
1606
|
+
allTrackedEffectsAddressed &&= refIds.size > 0;
|
|
1607
|
+
for (const entityId of refIds)
|
|
1608
|
+
addressedTrackedChangeIds.add(entityId);
|
|
1609
|
+
}
|
|
1549
1610
|
}
|
|
1550
1611
|
catch (err) {
|
|
1551
1612
|
errors.push({
|
|
@@ -1559,14 +1620,37 @@ async function runDeleteBlocks(doc, args) {
|
|
|
1559
1620
|
// Tracked deletions leave the block in place until the revision is decided,
|
|
1560
1621
|
// so the block count cannot move — count the structural revisions instead.
|
|
1561
1622
|
// Direct deletions must show one fewer block of each deleted node type.
|
|
1623
|
+
const observedTrackedChangeDelta = post.counts.trackedChanges - pre.counts.trackedChanges;
|
|
1624
|
+
// A later deletion can coalesce the batch and replace entity identities
|
|
1625
|
+
// returned by earlier operation receipts. For a multi-operation batch the
|
|
1626
|
+
// final inventory is therefore the authoritative count. A single operation
|
|
1627
|
+
// keeps using its addressed refs so fan-out remains verifiable even when
|
|
1628
|
+
// the inventory listing is capped.
|
|
1629
|
+
const expectedTrackedChangeDelta = deleted.length > 1
|
|
1630
|
+
? observedTrackedChangeDelta
|
|
1631
|
+
: allTrackedEffectsAddressed
|
|
1632
|
+
? addressedTrackedChangeIds.size
|
|
1633
|
+
: Math.max(observedTrackedChangeDelta, addressedTrackedChangeIds.size);
|
|
1562
1634
|
const checks = changeMode === 'tracked'
|
|
1563
|
-
? [
|
|
1635
|
+
? [
|
|
1636
|
+
{
|
|
1637
|
+
kind: 'tracked-change-count-delta',
|
|
1638
|
+
delta: expectedTrackedChangeDelta,
|
|
1639
|
+
},
|
|
1640
|
+
]
|
|
1564
1641
|
: [...new Set(deleted.map((entry) => entry.nodeType))].map((nodeType) => ({
|
|
1565
1642
|
kind: 'block-count-delta',
|
|
1566
1643
|
nodeType,
|
|
1567
1644
|
delta: -deleted.filter((entry) => entry.nodeType === nodeType).length,
|
|
1568
1645
|
}));
|
|
1569
1646
|
const verification = evaluateChecks(pre, post, checks);
|
|
1647
|
+
if (changeMode === 'tracked' && observedTrackedChangeDelta <= 0) {
|
|
1648
|
+
verification[0] = {
|
|
1649
|
+
...verification[0],
|
|
1650
|
+
passed: false,
|
|
1651
|
+
detail: `pre=${pre.counts.trackedChanges} post=${post.counts.trackedChanges}; no tracked review entity was created`,
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1570
1654
|
// Compare against the DEDUPED target list: two selectors naming the same
|
|
1571
1655
|
// block are one deletion, and counting the raw selectors would report a
|
|
1572
1656
|
// fully applied call as `partial` with no error to explain it. Selectors
|
|
@@ -4510,6 +4594,15 @@ function compactOpResult(result) {
|
|
|
4510
4594
|
compact.stepCount = result.steps.length;
|
|
4511
4595
|
return compact;
|
|
4512
4596
|
}
|
|
4597
|
+
function trackedChangeRefIds(result) {
|
|
4598
|
+
const refs = asRecord(result)?.trackedChangeRefs;
|
|
4599
|
+
if (!Array.isArray(refs))
|
|
4600
|
+
return new Set();
|
|
4601
|
+
return new Set(refs.flatMap((entry) => {
|
|
4602
|
+
const entityId = asString(asRecord(entry)?.entityId);
|
|
4603
|
+
return entityId ? [entityId] : [];
|
|
4604
|
+
}));
|
|
4605
|
+
}
|
|
4513
4606
|
function parseScopedReplaceArgs(args, changeMode) {
|
|
4514
4607
|
if (args.action !== 'replace_text')
|
|
4515
4608
|
return null;
|
|
@@ -4814,10 +4907,24 @@ function leadInShapeOf(blockText, labelText, quote) {
|
|
|
4814
4907
|
if (occurrence > 0)
|
|
4815
4908
|
return { kind: 'inside', delimiter: inside, occurrence };
|
|
4816
4909
|
}
|
|
4910
|
+
// A single emphasised word followed by a space and plain prose: the token
|
|
4911
|
+
// itself is the label ("WHEREAS the Party…"). Multi-word runs stay
|
|
4912
|
+
// unrecognised — an emphasised opening clause is prose, not a label.
|
|
4913
|
+
if (/^\S+\s$/.test(labelText) && blockText.length > labelText.length && blockText.startsWith(labelText)) {
|
|
4914
|
+
return { kind: 'word', label: labelText.trimEnd() };
|
|
4915
|
+
}
|
|
4817
4916
|
return null;
|
|
4818
4917
|
}
|
|
4819
4918
|
/** The span of `text` that carries the label look, or null when it has none. */
|
|
4820
4919
|
function leadInSpanOf(text, shape) {
|
|
4920
|
+
if (shape.kind === 'word') {
|
|
4921
|
+
// Only the SAME token inherits the look — "WHEREAS the Parties…" does,
|
|
4922
|
+
// "The Parties further agree…" does not (no guessing a label there).
|
|
4923
|
+
const next = text.charAt(shape.label.length);
|
|
4924
|
+
return text.startsWith(shape.label) && (next === '' || /\s/.test(next))
|
|
4925
|
+
? { start: 0, end: shape.label.length }
|
|
4926
|
+
: null;
|
|
4927
|
+
}
|
|
4821
4928
|
if (shape.kind === 'quoted') {
|
|
4822
4929
|
const opener = text.slice(0, 2).indexOf(shape.quote);
|
|
4823
4930
|
if (opener < 0)
|
package/dist/agent/catalog.cjs
CHANGED
|
@@ -543,18 +543,61 @@ function toProviderTool(provider, definition) {
|
|
|
543
543
|
return toVercelTool(definition);
|
|
544
544
|
return toOpenAiTool(definition);
|
|
545
545
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
for (const name of
|
|
546
|
+
/** Wildcard entry for `allowDirectActions`: keep every direct-only action callable. */
|
|
547
|
+
const ALLOW_ALL_DIRECT_ACTIONS = '*';
|
|
548
|
+
function assertKnownActions(option, names, knownActions) {
|
|
549
|
+
for (const name of names) {
|
|
550
550
|
if (!knownActions.has(name)) {
|
|
551
|
-
throw new errors.SuperDocCliError(
|
|
551
|
+
throw new errors.SuperDocCliError(`${option}: unknown action "${name}".`, {
|
|
552
552
|
code: 'INVALID_ARGUMENT',
|
|
553
553
|
details: { unknownAction: name, knownActions: [...knownActions] },
|
|
554
554
|
});
|
|
555
555
|
}
|
|
556
556
|
}
|
|
557
|
-
|
|
557
|
+
}
|
|
558
|
+
function resolveActionPolicy(options, resolveOptions) {
|
|
559
|
+
const knownActions = new Set(actions.ACTION_NAMES_LIST);
|
|
560
|
+
const ignoreUnknown = resolveOptions?.ignoreUnknownActions === true;
|
|
561
|
+
const configuredExclusions = new Set(options?.excludeActions ?? []);
|
|
562
|
+
if (!ignoreUnknown)
|
|
563
|
+
assertKnownActions('excludeActions', configuredExclusions, knownActions);
|
|
564
|
+
const enforceChangeMode = options?.enforceChangeMode;
|
|
565
|
+
if (enforceChangeMode !== undefined && enforceChangeMode !== 'tracked') {
|
|
566
|
+
throw new errors.SuperDocCliError(`enforceChangeMode: only "tracked" is supported; got ${JSON.stringify(enforceChangeMode)}.`, {
|
|
567
|
+
code: 'INVALID_ARGUMENT',
|
|
568
|
+
details: { enforceChangeMode, supported: ['tracked'] },
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
if (options?.allowDirectActions !== undefined && enforceChangeMode === undefined) {
|
|
572
|
+
throw new errors.SuperDocCliError('allowDirectActions requires enforceChangeMode: "tracked"; without enforcement every action already runs as requested.', {
|
|
573
|
+
code: 'INVALID_ARGUMENT',
|
|
574
|
+
details: { allowDirectActions: [...options.allowDirectActions] },
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
// `'*'` is the "I accept every direct-only action" shortcut: tracked-capable
|
|
578
|
+
// actions are still forced tracked, the direct bucket just stays callable.
|
|
579
|
+
const requestedAllow = new Set(options?.allowDirectActions ?? []);
|
|
580
|
+
const allowAllDirect = requestedAllow.delete(ALLOW_ALL_DIRECT_ACTIONS);
|
|
581
|
+
if (!ignoreUnknown)
|
|
582
|
+
assertKnownActions('allowDirectActions', requestedAllow, knownActions);
|
|
583
|
+
const allowDirectActions = new Set((allowAllDirect ? [...actions.ACTION_NAMES_LIST] : [...requestedAllow]).filter((name) => actions.ACTION_DISPOSITIONS[name] === 'direct'));
|
|
584
|
+
const directOnlyExclusions = new Set();
|
|
585
|
+
if (enforceChangeMode === 'tracked') {
|
|
586
|
+
for (const name of actions.ACTION_NAMES_LIST) {
|
|
587
|
+
if (actions.ACTION_DISPOSITIONS[name] !== 'direct')
|
|
588
|
+
continue;
|
|
589
|
+
if (configuredExclusions.has(name) || allowDirectActions.has(name))
|
|
590
|
+
continue;
|
|
591
|
+
directOnlyExclusions.add(name);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return {
|
|
595
|
+
excludedActions: new Set([...configuredExclusions, ...directOnlyExclusions]),
|
|
596
|
+
configuredExclusions,
|
|
597
|
+
directOnlyExclusions,
|
|
598
|
+
enforceChangeMode,
|
|
599
|
+
allowDirectActions,
|
|
600
|
+
};
|
|
558
601
|
}
|
|
559
602
|
/**
|
|
560
603
|
* Tools advertised to the LLM. Only PUBLIC_AGENT_TOOL_NAMES (currently
|
|
@@ -566,7 +609,7 @@ function validateExclusions(options) {
|
|
|
566
609
|
* don't want the model to see certain capabilities.
|
|
567
610
|
*/
|
|
568
611
|
function listAgentTools(provider, options) {
|
|
569
|
-
const { excludedActions } =
|
|
612
|
+
const { excludedActions } = resolveActionPolicy(options);
|
|
570
613
|
const publicSet = new Set(PUBLIC_AGENT_TOOL_NAMES);
|
|
571
614
|
const includedActions = actions.ACTION_NAMES_LIST.filter((name) => !excludedActions.has(name));
|
|
572
615
|
return AGENT_TOOL_DEFINITIONS.filter((d) => publicSet.has(d.name))
|
|
@@ -587,6 +630,7 @@ function isAgentToolName(toolName) {
|
|
|
587
630
|
exports.ACTION_ARG_SCHEMA = ACTION_ARG_SCHEMA;
|
|
588
631
|
exports.AGENT_TOOL_DEFINITIONS = AGENT_TOOL_DEFINITIONS;
|
|
589
632
|
exports.AGENT_TOOL_NAMES = AGENT_TOOL_NAMES;
|
|
633
|
+
exports.ALLOW_ALL_DIRECT_ACTIONS = ALLOW_ALL_DIRECT_ACTIONS;
|
|
590
634
|
exports.CELL_SCHEMA = CELL_SCHEMA;
|
|
591
635
|
exports.LIST_ITEM_SCHEMA = LIST_ITEM_SCHEMA;
|
|
592
636
|
exports.PUBLIC_AGENT_TOOL_NAMES = PUBLIC_AGENT_TOOL_NAMES;
|
|
@@ -595,4 +639,4 @@ exports.SELECTOR_SCHEMA = SELECTOR_SCHEMA;
|
|
|
595
639
|
exports.buildPerformActionDefinition = buildPerformActionDefinition;
|
|
596
640
|
exports.isAgentToolName = isAgentToolName;
|
|
597
641
|
exports.listAgentTools = listAgentTools;
|
|
598
|
-
exports.
|
|
642
|
+
exports.resolveActionPolicy = resolveActionPolicy;
|
package/dist/agent/catalog.d.ts
CHANGED
|
@@ -326,7 +326,15 @@ export declare const ACTION_ARG_SCHEMA: Record<string, unknown>;
|
|
|
326
326
|
*/
|
|
327
327
|
export declare function buildPerformActionDefinition(includedActions: readonly string[]): AgentToolDefinition;
|
|
328
328
|
export declare const AGENT_TOOL_DEFINITIONS: readonly AgentToolDefinition[];
|
|
329
|
-
/**
|
|
329
|
+
/** The only change mode a host can enforce today. */
|
|
330
|
+
export type EnforceChangeMode = 'tracked';
|
|
331
|
+
/** Wildcard entry for `allowDirectActions`: keep every direct-only action callable. */
|
|
332
|
+
export declare const ALLOW_ALL_DIRECT_ACTIONS = "*";
|
|
333
|
+
/**
|
|
334
|
+
* Customer-facing narrowing of the advertised tool surface. The same options
|
|
335
|
+
* shape the tool schema, the system prompt, and the dispatch guard so the
|
|
336
|
+
* three can never disagree.
|
|
337
|
+
*/
|
|
330
338
|
export interface ListAgentToolsOptions {
|
|
331
339
|
/**
|
|
332
340
|
* Action names to REMOVE from `superdoc_perform_action` — the enum, the
|
|
@@ -335,7 +343,52 @@ export interface ListAgentToolsOptions {
|
|
|
335
343
|
* every action drops the tool entirely.
|
|
336
344
|
*/
|
|
337
345
|
excludeActions?: readonly string[];
|
|
346
|
+
/**
|
|
347
|
+
* Fail-closed tracked-changes-only policy. With `'tracked'`, every action
|
|
348
|
+
* whose disposition is `direct` (see ACTION_DISPOSITIONS) is removed from the
|
|
349
|
+
* surface exactly like an excluded action, and every tracked-capable action
|
|
350
|
+
* is upgraded to `changeMode: "tracked"` at dispatch whatever the model
|
|
351
|
+
* asked for. Only `'tracked'` is accepted.
|
|
352
|
+
*/
|
|
353
|
+
enforceChangeMode?: EnforceChangeMode;
|
|
354
|
+
/**
|
|
355
|
+
* Direct-only actions a host permits under `enforceChangeMode`. They stay in
|
|
356
|
+
* the enum and prompt and run directly at dispatch. `['*']` allows every
|
|
357
|
+
* direct-only action (tracked-capable ones are still forced tracked).
|
|
358
|
+
* Requires `enforceChangeMode`; unknown names throw; tracked-capable names
|
|
359
|
+
* are ignored so a list survives an action gaining tracked support.
|
|
360
|
+
*/
|
|
361
|
+
allowDirectActions?: readonly string[];
|
|
362
|
+
}
|
|
363
|
+
/** The exclusion set a policy resolves to, split by why each action is out. */
|
|
364
|
+
export interface ResolvedActionPolicy {
|
|
365
|
+
/** Every action removed from the enum, prompt, and dispatch. */
|
|
366
|
+
excludedActions: Set<string>;
|
|
367
|
+
/** Removed by `excludeActions`. Wins over `allowDirectActions`. */
|
|
368
|
+
configuredExclusions: Set<string>;
|
|
369
|
+
/** Removed because it is direct-only under `enforceChangeMode` and not allowlisted. */
|
|
370
|
+
directOnlyExclusions: Set<string>;
|
|
371
|
+
enforceChangeMode: EnforceChangeMode | undefined;
|
|
372
|
+
/** Direct-only actions the host allowlisted (validated, tracked names dropped). */
|
|
373
|
+
allowDirectActions: Set<string>;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Validate and resolve the surface-narrowing options into one exclusion set.
|
|
377
|
+
* Every layer (schema, prompt, dispatch) calls this so the policy is computed
|
|
378
|
+
* exactly one way.
|
|
379
|
+
*/
|
|
380
|
+
export interface ResolveActionPolicyOptions {
|
|
381
|
+
/**
|
|
382
|
+
* Skip the unknown-name check. A preset built on top of core (custom actions)
|
|
383
|
+
* forwards ONE exclusion list that mixes its own action names with core's, so
|
|
384
|
+
* at dispatch time a name core does not own is another layer's business, not
|
|
385
|
+
* a caller mistake. Configure-time entry points (getTools, getSystemPrompt)
|
|
386
|
+
* leave this off so a typo still fails loudly where it was written.
|
|
387
|
+
*/
|
|
388
|
+
ignoreUnknownActions?: boolean;
|
|
338
389
|
}
|
|
390
|
+
export declare function resolveActionPolicy(options?: ListAgentToolsOptions, resolveOptions?: ResolveActionPolicyOptions): ResolvedActionPolicy;
|
|
391
|
+
/** @deprecated Use {@link resolveActionPolicy}; kept for callers that only need the union. */
|
|
339
392
|
export declare function validateExclusions(options?: ListAgentToolsOptions): {
|
|
340
393
|
excludedActions: Set<string>;
|
|
341
394
|
};
|
package/dist/agent/catalog.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SuperDocCliError } from '../runtime/errors.js';
|
|
2
|
-
import { ACTION_NAMES_LIST, ACTION_HINTS, ACTION_GROUPS, ACTION_ARGS } from './actions.js';
|
|
2
|
+
import { ACTION_NAMES_LIST, ACTION_HINTS, ACTION_GROUPS, ACTION_ARGS, ACTION_DISPOSITIONS } from './actions.js';
|
|
3
3
|
export const AGENT_TOOL_NAMES = [
|
|
4
4
|
'superdoc_inspect',
|
|
5
5
|
'superdoc_perform_action',
|
|
@@ -540,18 +540,65 @@ function toProviderTool(provider, definition) {
|
|
|
540
540
|
return toVercelTool(definition);
|
|
541
541
|
return toOpenAiTool(definition);
|
|
542
542
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
for (const name of
|
|
543
|
+
/** Wildcard entry for `allowDirectActions`: keep every direct-only action callable. */
|
|
544
|
+
export const ALLOW_ALL_DIRECT_ACTIONS = '*';
|
|
545
|
+
function assertKnownActions(option, names, knownActions) {
|
|
546
|
+
for (const name of names) {
|
|
547
547
|
if (!knownActions.has(name)) {
|
|
548
|
-
throw new SuperDocCliError(
|
|
548
|
+
throw new SuperDocCliError(`${option}: unknown action "${name}".`, {
|
|
549
549
|
code: 'INVALID_ARGUMENT',
|
|
550
550
|
details: { unknownAction: name, knownActions: [...knownActions] },
|
|
551
551
|
});
|
|
552
552
|
}
|
|
553
553
|
}
|
|
554
|
-
|
|
554
|
+
}
|
|
555
|
+
export function resolveActionPolicy(options, resolveOptions) {
|
|
556
|
+
const knownActions = new Set(ACTION_NAMES_LIST);
|
|
557
|
+
const ignoreUnknown = resolveOptions?.ignoreUnknownActions === true;
|
|
558
|
+
const configuredExclusions = new Set(options?.excludeActions ?? []);
|
|
559
|
+
if (!ignoreUnknown)
|
|
560
|
+
assertKnownActions('excludeActions', configuredExclusions, knownActions);
|
|
561
|
+
const enforceChangeMode = options?.enforceChangeMode;
|
|
562
|
+
if (enforceChangeMode !== undefined && enforceChangeMode !== 'tracked') {
|
|
563
|
+
throw new SuperDocCliError(`enforceChangeMode: only "tracked" is supported; got ${JSON.stringify(enforceChangeMode)}.`, {
|
|
564
|
+
code: 'INVALID_ARGUMENT',
|
|
565
|
+
details: { enforceChangeMode, supported: ['tracked'] },
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
if (options?.allowDirectActions !== undefined && enforceChangeMode === undefined) {
|
|
569
|
+
throw new SuperDocCliError('allowDirectActions requires enforceChangeMode: "tracked"; without enforcement every action already runs as requested.', {
|
|
570
|
+
code: 'INVALID_ARGUMENT',
|
|
571
|
+
details: { allowDirectActions: [...options.allowDirectActions] },
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
// `'*'` is the "I accept every direct-only action" shortcut: tracked-capable
|
|
575
|
+
// actions are still forced tracked, the direct bucket just stays callable.
|
|
576
|
+
const requestedAllow = new Set(options?.allowDirectActions ?? []);
|
|
577
|
+
const allowAllDirect = requestedAllow.delete(ALLOW_ALL_DIRECT_ACTIONS);
|
|
578
|
+
if (!ignoreUnknown)
|
|
579
|
+
assertKnownActions('allowDirectActions', requestedAllow, knownActions);
|
|
580
|
+
const allowDirectActions = new Set((allowAllDirect ? [...ACTION_NAMES_LIST] : [...requestedAllow]).filter((name) => ACTION_DISPOSITIONS[name] === 'direct'));
|
|
581
|
+
const directOnlyExclusions = new Set();
|
|
582
|
+
if (enforceChangeMode === 'tracked') {
|
|
583
|
+
for (const name of ACTION_NAMES_LIST) {
|
|
584
|
+
if (ACTION_DISPOSITIONS[name] !== 'direct')
|
|
585
|
+
continue;
|
|
586
|
+
if (configuredExclusions.has(name) || allowDirectActions.has(name))
|
|
587
|
+
continue;
|
|
588
|
+
directOnlyExclusions.add(name);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return {
|
|
592
|
+
excludedActions: new Set([...configuredExclusions, ...directOnlyExclusions]),
|
|
593
|
+
configuredExclusions,
|
|
594
|
+
directOnlyExclusions,
|
|
595
|
+
enforceChangeMode,
|
|
596
|
+
allowDirectActions,
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
/** @deprecated Use {@link resolveActionPolicy}; kept for callers that only need the union. */
|
|
600
|
+
export function validateExclusions(options) {
|
|
601
|
+
return { excludedActions: resolveActionPolicy(options).excludedActions };
|
|
555
602
|
}
|
|
556
603
|
/**
|
|
557
604
|
* Tools advertised to the LLM. Only PUBLIC_AGENT_TOOL_NAMES (currently
|
|
@@ -563,7 +610,7 @@ export function validateExclusions(options) {
|
|
|
563
610
|
* don't want the model to see certain capabilities.
|
|
564
611
|
*/
|
|
565
612
|
export function listAgentTools(provider, options) {
|
|
566
|
-
const { excludedActions } =
|
|
613
|
+
const { excludedActions } = resolveActionPolicy(options);
|
|
567
614
|
const publicSet = new Set(PUBLIC_AGENT_TOOL_NAMES);
|
|
568
615
|
const includedActions = ACTION_NAMES_LIST.filter((name) => !excludedActions.has(name));
|
|
569
616
|
return AGENT_TOOL_DEFINITIONS.filter((d) => publicSet.has(d.name))
|