@superdoc/sdk 2.23.0-next.1 → 2.23.0-next.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action-primitives/tools/structure-insert.cjs +3 -5
- package/dist/action-primitives/tools/structure-insert.js +3 -5
- package/dist/action-primitives/tools/text-transform.cjs +3 -5
- package/dist/action-primitives/tools/text-transform.js +3 -5
- package/dist/actions/define.cjs +15 -17
- package/dist/actions/define.js +1 -3
- package/dist/agent/actions.cjs +42 -44
- package/dist/agent/actions.js +1 -3
- package/dist/agent/operation-catalog.js +0 -4
- package/dist/agent/runtime.cjs +7 -9
- package/dist/agent/runtime.js +1 -3
- package/dist/agent/v2-preset-compat.cjs +5 -7
- package/dist/agent/v2-preset-compat.js +1 -3
- package/dist/embedded-tools.generated.cjs +5 -5
- package/dist/embedded-tools.generated.js +5 -5
- package/dist/generated/client.cjs +10 -0
- package/dist/generated/client.d.ts +16343 -919
- package/dist/generated/client.js +10 -0
- package/dist/generated/contract.cjs +1 -1
- package/dist/generated/contract.js +1 -1
- package/dist/guards.cjs +7 -0
- package/dist/guards.d.ts +1 -0
- package/dist/guards.js +3 -0
- package/dist/presets/core.cjs +10 -12
- package/dist/presets/core.js +1 -3
- package/dist/presets/legacy.cjs +6 -8
- package/dist/presets/legacy.js +1 -3
- 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 +6 -6
- package/tools/catalog.json +77 -0
- package/tools/generation-receipt.json +17 -17
- package/tools/tools-policy.json +1 -1
- package/tools/tools.anthropic.json +77 -0
- package/tools/tools.generic.json +77 -0
- package/tools/tools.openai.json +77 -0
- package/tools/tools.vercel.json +77 -0
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var docIndex = require('../doc-index.cjs');
|
|
4
4
|
var engine = require('../engine.cjs');
|
|
5
5
|
var resolve = require('../resolve.cjs');
|
|
6
|
+
var guards = require('../../guards.cjs');
|
|
6
7
|
|
|
7
8
|
const STRUCTURE_INSERT_ACTIONS = [
|
|
8
9
|
'insert_toc',
|
|
@@ -22,9 +23,6 @@ const DEFAULT_TOC_CONFIG = {
|
|
|
22
23
|
rightAlignPageNumbers: true,
|
|
23
24
|
tabLeader: 'dot',
|
|
24
25
|
};
|
|
25
|
-
function isObjectRecord(value) {
|
|
26
|
-
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
27
|
-
}
|
|
28
26
|
function parseAction(raw) {
|
|
29
27
|
if (typeof raw !== 'string') {
|
|
30
28
|
return undefined;
|
|
@@ -166,7 +164,7 @@ function resolvePlacement(context, action) {
|
|
|
166
164
|
source: 'provided',
|
|
167
165
|
});
|
|
168
166
|
}
|
|
169
|
-
if (!
|
|
167
|
+
if (!guards.isRecord(rawPlacement)) {
|
|
170
168
|
return engine.workflowStepFailure({
|
|
171
169
|
status: 'failed',
|
|
172
170
|
phase: 'resolve',
|
|
@@ -960,7 +958,7 @@ async function executeStep(context, _resolved, plan) {
|
|
|
960
958
|
sectionId: 'section-break-fallback',
|
|
961
959
|
});
|
|
962
960
|
}
|
|
963
|
-
const sectionId =
|
|
961
|
+
const sectionId = guards.isRecord(sectionResult.section) && typeof sectionResult.section.sectionId === 'string'
|
|
964
962
|
? sectionResult.section.sectionId
|
|
965
963
|
: undefined;
|
|
966
964
|
const afterInfo = await context.documentHandle.info({}, context.invokeOptions);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { buildWorkflowDocIndex } from '../doc-index.js';
|
|
2
2
|
import { runWorkflowEngine, workflowStepFailure, workflowStepSuccess, } from '../engine.js';
|
|
3
3
|
import { resolveWorkflowTargetFromUnknown, } from '../resolve.js';
|
|
4
|
+
import { isRecord } from '../../guards.js';
|
|
4
5
|
const STRUCTURE_INSERT_ACTIONS = [
|
|
5
6
|
'insert_toc',
|
|
6
7
|
'insert_section_break',
|
|
@@ -19,9 +20,6 @@ const DEFAULT_TOC_CONFIG = {
|
|
|
19
20
|
rightAlignPageNumbers: true,
|
|
20
21
|
tabLeader: 'dot',
|
|
21
22
|
};
|
|
22
|
-
function isObjectRecord(value) {
|
|
23
|
-
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
24
|
-
}
|
|
25
23
|
function parseAction(raw) {
|
|
26
24
|
if (typeof raw !== 'string') {
|
|
27
25
|
return undefined;
|
|
@@ -163,7 +161,7 @@ function resolvePlacement(context, action) {
|
|
|
163
161
|
source: 'provided',
|
|
164
162
|
});
|
|
165
163
|
}
|
|
166
|
-
if (!
|
|
164
|
+
if (!isRecord(rawPlacement)) {
|
|
167
165
|
return workflowStepFailure({
|
|
168
166
|
status: 'failed',
|
|
169
167
|
phase: 'resolve',
|
|
@@ -957,7 +955,7 @@ async function executeStep(context, _resolved, plan) {
|
|
|
957
955
|
sectionId: 'section-break-fallback',
|
|
958
956
|
});
|
|
959
957
|
}
|
|
960
|
-
const sectionId =
|
|
958
|
+
const sectionId = isRecord(sectionResult.section) && typeof sectionResult.section.sectionId === 'string'
|
|
961
959
|
? sectionResult.section.sectionId
|
|
962
960
|
: undefined;
|
|
963
961
|
const afterInfo = await context.documentHandle.info({}, context.invokeOptions);
|
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
var engine = require('../engine.cjs');
|
|
4
4
|
var resolve = require('../resolve.cjs');
|
|
5
|
+
var guards = require('../../guards.cjs');
|
|
5
6
|
|
|
6
7
|
const TEXT_TRANSFORM_ACTIONS = ['replace_all', 'delete_all', 'rewrite_block', 'fill_placeholders'];
|
|
7
8
|
const CHANGE_MODES = ['direct', 'tracked'];
|
|
8
9
|
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
10
|
function coerceBoolean(raw, fallback) {
|
|
13
11
|
if (typeof raw === 'boolean') {
|
|
14
12
|
return raw;
|
|
@@ -155,7 +153,7 @@ function parseEdits(raw) {
|
|
|
155
153
|
}
|
|
156
154
|
const edits = [];
|
|
157
155
|
for (const entry of raw) {
|
|
158
|
-
if (!
|
|
156
|
+
if (!guards.isRecord(entry)) {
|
|
159
157
|
return undefined;
|
|
160
158
|
}
|
|
161
159
|
const find = entry.find;
|
|
@@ -184,7 +182,7 @@ function parsePlaceholderValues(raw) {
|
|
|
184
182
|
values.push(entry);
|
|
185
183
|
continue;
|
|
186
184
|
}
|
|
187
|
-
if (
|
|
185
|
+
if (guards.isRecord(entry) && typeof entry.value === 'string' && entry.value.length > 0) {
|
|
188
186
|
values.push(entry.value);
|
|
189
187
|
continue;
|
|
190
188
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { runWorkflowEngine, workflowStepFailure, workflowStepSuccess, } from '../engine.js';
|
|
2
2
|
import { resolveWorkflowTargetFromUnknown, } from '../resolve.js';
|
|
3
|
+
import { isRecord } from '../../guards.js';
|
|
3
4
|
const TEXT_TRANSFORM_ACTIONS = ['replace_all', 'delete_all', 'rewrite_block', 'fill_placeholders'];
|
|
4
5
|
const CHANGE_MODES = ['direct', 'tracked'];
|
|
5
6
|
const REWRITE_BLOCK_SUPPORTED_NODE_TYPES = ['paragraph', 'heading', 'listItem'];
|
|
6
|
-
function isObjectRecord(value) {
|
|
7
|
-
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
8
|
-
}
|
|
9
7
|
function coerceBoolean(raw, fallback) {
|
|
10
8
|
if (typeof raw === 'boolean') {
|
|
11
9
|
return raw;
|
|
@@ -152,7 +150,7 @@ function parseEdits(raw) {
|
|
|
152
150
|
}
|
|
153
151
|
const edits = [];
|
|
154
152
|
for (const entry of raw) {
|
|
155
|
-
if (!
|
|
153
|
+
if (!isRecord(entry)) {
|
|
156
154
|
return undefined;
|
|
157
155
|
}
|
|
158
156
|
const find = entry.find;
|
|
@@ -181,7 +179,7 @@ function parsePlaceholderValues(raw) {
|
|
|
181
179
|
values.push(entry);
|
|
182
180
|
continue;
|
|
183
181
|
}
|
|
184
|
-
if (
|
|
182
|
+
if (isRecord(entry) && typeof entry.value === 'string' && entry.value.length > 0) {
|
|
185
183
|
values.push(entry.value);
|
|
186
184
|
continue;
|
|
187
185
|
}
|
package/dist/actions/define.cjs
CHANGED
|
@@ -4,6 +4,7 @@ var errors = require('../runtime/errors.cjs');
|
|
|
4
4
|
var presets = require('../presets.cjs');
|
|
5
5
|
var actions = require('../agent/actions.cjs');
|
|
6
6
|
var catalog = require('../agent/catalog.cjs');
|
|
7
|
+
var guards = require('../guards.cjs');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Customer-extensible **custom actions** for the SuperDoc LLM-tools SDK. The
|
|
@@ -71,7 +72,7 @@ function coerceInputSchema(input) {
|
|
|
71
72
|
// `{ type: 'object', additionalProperties: true }`). The declared type makes
|
|
72
73
|
// `properties` required and every consumer (and the Python mirror) assumes
|
|
73
74
|
// it exists, so normalize a missing/non-object bag to {}.
|
|
74
|
-
return isRecord(input.properties) ? input : { ...input, properties: {} };
|
|
75
|
+
return guards.isRecord(input.properties) ? input : { ...input, properties: {} };
|
|
75
76
|
}
|
|
76
77
|
// A bare properties bag — wrap it.
|
|
77
78
|
return { type: 'object', properties: input, additionalProperties: true };
|
|
@@ -121,7 +122,7 @@ function defineAction(input) {
|
|
|
121
122
|
if (!BUILTIN_ACTION_NAMES.has(step.action)) {
|
|
122
123
|
throw new errors.SuperDocCliError(`defineAction "${input.name}": steps[${index}].action "${step.action}" is not a built-in core action. Steps compose built-in actions only; use the \`run\` tier for anything else.`, { code: 'INVALID_ARGUMENT', details: { name: input.name, index, action: step.action } });
|
|
123
124
|
}
|
|
124
|
-
if (step.args != null && !isRecord(step.args)) {
|
|
125
|
+
if (step.args != null && !guards.isRecord(step.args)) {
|
|
125
126
|
throw new errors.SuperDocCliError(`defineAction "${input.name}": steps[${index}].args must be an object.`, {
|
|
126
127
|
code: 'INVALID_ARGUMENT',
|
|
127
128
|
details: { name: input.name, index },
|
|
@@ -240,9 +241,6 @@ function assertActionsValid(actions, presetId, standalone = false) {
|
|
|
240
241
|
// ---------------------------------------------------------------------------
|
|
241
242
|
// runCustomAction — validate, codegen, dispatch via superdoc_execute_code, map receipt
|
|
242
243
|
// ---------------------------------------------------------------------------
|
|
243
|
-
function isRecord(value) {
|
|
244
|
-
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
245
|
-
}
|
|
246
244
|
/** Kit-level args every custom action accepts without declaring them. */
|
|
247
245
|
const IMPLICIT_ACTION_ARGS = new Set(['changeMode', 'rationale']);
|
|
248
246
|
function validateAgainstSchema(action, args) {
|
|
@@ -254,7 +252,7 @@ function validateAgainstSchema(action, args) {
|
|
|
254
252
|
details: { action: action.name, missingKeys: missing },
|
|
255
253
|
});
|
|
256
254
|
}
|
|
257
|
-
const properties = isRecord(action.inputSchema.properties) ? action.inputSchema.properties : {};
|
|
255
|
+
const properties = guards.isRecord(action.inputSchema.properties) ? action.inputSchema.properties : {};
|
|
258
256
|
if (action.inputSchema.additionalProperties === false) {
|
|
259
257
|
const unknown = Object.keys(args).filter((key) => !(key in properties) && !IMPLICIT_ACTION_ARGS.has(key));
|
|
260
258
|
if (unknown.length > 0) {
|
|
@@ -266,7 +264,7 @@ function validateAgainstSchema(action, args) {
|
|
|
266
264
|
}
|
|
267
265
|
for (const [key, prop] of Object.entries(properties)) {
|
|
268
266
|
const value = args[key];
|
|
269
|
-
if (value !== undefined && isRecord(prop) && Array.isArray(prop.enum) && !prop.enum.includes(value)) {
|
|
267
|
+
if (value !== undefined && guards.isRecord(prop) && Array.isArray(prop.enum) && !prop.enum.includes(value)) {
|
|
270
268
|
throw new errors.SuperDocCliError(`Invalid value for ${action.name}.${key}: ${JSON.stringify(value)} (allowed: ${prop.enum.map((entry) => JSON.stringify(entry)).join(', ')})`, { code: 'INVALID_ARGUMENT', details: { action: action.name, key, allowed: prop.enum } });
|
|
271
269
|
}
|
|
272
270
|
}
|
|
@@ -275,7 +273,7 @@ function validateAgainstSchema(action, args) {
|
|
|
275
273
|
function applyInputDefaults(action, args) {
|
|
276
274
|
const out = { ...args };
|
|
277
275
|
for (const [key, prop] of Object.entries(action.inputSchema.properties ?? {})) {
|
|
278
|
-
if (out[key] === undefined && isRecord(prop) && 'default' in prop)
|
|
276
|
+
if (out[key] === undefined && guards.isRecord(prop) && 'default' in prop)
|
|
279
277
|
out[key] = prop.default;
|
|
280
278
|
}
|
|
281
279
|
return out;
|
|
@@ -309,7 +307,7 @@ function substituteTemplates(node, vars) {
|
|
|
309
307
|
// would crash the transport).
|
|
310
308
|
return node.map((item) => substituteTemplates(item, vars)).filter((item) => item !== undefined);
|
|
311
309
|
}
|
|
312
|
-
if (isRecord(node)) {
|
|
310
|
+
if (guards.isRecord(node)) {
|
|
313
311
|
const out = {};
|
|
314
312
|
for (const [key, value] of Object.entries(node)) {
|
|
315
313
|
const substituted = substituteTemplates(value, vars);
|
|
@@ -341,7 +339,7 @@ async function runStepsAction(base, action, documentHandle, args, invokeOptions)
|
|
|
341
339
|
let receipt;
|
|
342
340
|
try {
|
|
343
341
|
const dispatched = await base.dispatch(documentHandle, 'superdoc_perform_action', { action: step.action, ...stepArgs }, stepOptions);
|
|
344
|
-
receipt = isRecord(dispatched) ? dispatched : { status: 'ok', result: dispatched };
|
|
342
|
+
receipt = guards.isRecord(dispatched) ? dispatched : { status: 'ok', result: dispatched };
|
|
345
343
|
}
|
|
346
344
|
catch (error) {
|
|
347
345
|
// Validation errors THROW from dispatch; runtime failures come back as
|
|
@@ -458,13 +456,13 @@ function renormalizeAnthropicCacheMarker(tools, provider, cacheRequested) {
|
|
|
458
456
|
if (provider !== 'anthropic' || !cacheRequested || tools.length === 0)
|
|
459
457
|
return tools;
|
|
460
458
|
const stripped = tools.map((tool) => {
|
|
461
|
-
if (!isRecord(tool) || !('cache_control' in tool))
|
|
459
|
+
if (!guards.isRecord(tool) || !('cache_control' in tool))
|
|
462
460
|
return tool;
|
|
463
461
|
const { cache_control: _drop, ...rest } = tool;
|
|
464
462
|
return rest;
|
|
465
463
|
});
|
|
466
464
|
const last = stripped[stripped.length - 1];
|
|
467
|
-
stripped[stripped.length - 1] = isRecord(last) ? { ...last, cache_control: { type: 'ephemeral' } } : last;
|
|
465
|
+
stripped[stripped.length - 1] = guards.isRecord(last) ? { ...last, cache_control: { type: 'ephemeral' } } : last;
|
|
468
466
|
return stripped;
|
|
469
467
|
}
|
|
470
468
|
function customActionsDescription(actions) {
|
|
@@ -535,16 +533,16 @@ function mergeIntoAgentAction(tools, actions) {
|
|
|
535
533
|
// Provider dialects: openai nests under `function` (`parameters`);
|
|
536
534
|
// anthropic is flat with `input_schema`; the core agent dialect for
|
|
537
535
|
// vercel is flat with `inputSchema`; generic is flat with `parameters`.
|
|
538
|
-
const fn = isRecord(t.function) ? t.function : null;
|
|
536
|
+
const fn = guards.isRecord(t.function) ? t.function : null;
|
|
539
537
|
const schemaContainer = fn ?? t;
|
|
540
538
|
const schemaKey = 'input_schema' in schemaContainer
|
|
541
539
|
? 'input_schema'
|
|
542
540
|
: 'inputSchema' in schemaContainer
|
|
543
541
|
? 'inputSchema'
|
|
544
542
|
: 'parameters';
|
|
545
|
-
const schema = isRecord(schemaContainer[schemaKey]) ? schemaContainer[schemaKey] : {};
|
|
546
|
-
const properties = isRecord(schema.properties) ? { ...schema.properties } : {};
|
|
547
|
-
const actionProp = isRecord(properties.action) ? { ...properties.action } : {};
|
|
543
|
+
const schema = guards.isRecord(schemaContainer[schemaKey]) ? schemaContainer[schemaKey] : {};
|
|
544
|
+
const properties = guards.isRecord(schema.properties) ? { ...schema.properties } : {};
|
|
545
|
+
const actionProp = guards.isRecord(properties.action) ? { ...properties.action } : {};
|
|
548
546
|
const enumValues = Array.isArray(actionProp.enum) ? [...actionProp.enum] : [];
|
|
549
547
|
for (const action of actions) {
|
|
550
548
|
if (!enumValues.includes(action.name))
|
|
@@ -748,7 +746,7 @@ function extendPreset(baseId, options) {
|
|
|
748
746
|
// must be refused before it runs (the base can only refuse built-ins).
|
|
749
747
|
const exclusionOptions = invokeOptions;
|
|
750
748
|
const excluded = new Set(exclusionOptions?.excludeActions ?? []);
|
|
751
|
-
if (toolName === 'superdoc_perform_action' && isRecord(args) && typeof args.action === 'string') {
|
|
749
|
+
if (toolName === 'superdoc_perform_action' && guards.isRecord(args) && typeof args.action === 'string') {
|
|
752
750
|
// Advertised surface == dispatchable surface: in standalone mode custom
|
|
753
751
|
// actions are advertised as their own tools, so the (unadvertised)
|
|
754
752
|
// perform_action route must not execute them — it falls through to the
|
package/dist/actions/define.js
CHANGED
|
@@ -32,6 +32,7 @@ import { SuperDocCliError } from '../runtime/errors.js';
|
|
|
32
32
|
import { getPreset, } from '../presets.js';
|
|
33
33
|
import { ACTION_NAMES_LIST } from '../agent/actions.js';
|
|
34
34
|
import { AGENT_TOOL_NAMES, buildPerformActionDefinition } from '../agent/catalog.js';
|
|
35
|
+
import { isRecord } from '../guards.js';
|
|
35
36
|
/** Which execution tier a spec uses. */
|
|
36
37
|
export function executionKindOf(spec) {
|
|
37
38
|
return Array.isArray(spec.steps) ? 'steps' : 'run';
|
|
@@ -237,9 +238,6 @@ function assertActionsValid(actions, presetId, standalone = false) {
|
|
|
237
238
|
// ---------------------------------------------------------------------------
|
|
238
239
|
// runCustomAction — validate, codegen, dispatch via superdoc_execute_code, map receipt
|
|
239
240
|
// ---------------------------------------------------------------------------
|
|
240
|
-
function isRecord(value) {
|
|
241
|
-
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
242
|
-
}
|
|
243
241
|
/** Kit-level args every custom action accepts without declaring them. */
|
|
244
242
|
const IMPLICIT_ACTION_ARGS = new Set(['changeMode', 'rationale']);
|
|
245
243
|
function validateAgainstSchema(action, args) {
|
package/dist/agent/actions.cjs
CHANGED
|
@@ -9,6 +9,7 @@ var listTransform = require('../action-primitives/tools/list-transform.cjs');
|
|
|
9
9
|
var structureInsert = require('../action-primitives/tools/structure-insert.cjs');
|
|
10
10
|
var textTransform = require('../action-primitives/tools/text-transform.cjs');
|
|
11
11
|
var docSnapshot = require('./doc-snapshot.cjs');
|
|
12
|
+
var guards = require('../guards.cjs');
|
|
12
13
|
|
|
13
14
|
const ACTION_DEFINITIONS = actionDescriptors;
|
|
14
15
|
function projectActionDefinitions(project) {
|
|
@@ -72,9 +73,6 @@ function getActionDispositions() {
|
|
|
72
73
|
function isActionName(value) {
|
|
73
74
|
return typeof value === 'string' && ACTION_NAMES.includes(value);
|
|
74
75
|
}
|
|
75
|
-
function isRecord(value) {
|
|
76
|
-
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
77
|
-
}
|
|
78
76
|
/** The message to put in a receipt when something best-effort did not happen. */
|
|
79
77
|
function reasonOf(err) {
|
|
80
78
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -100,7 +98,7 @@ function isNoOp(err) {
|
|
|
100
98
|
return err?.code === 'NO_OP';
|
|
101
99
|
}
|
|
102
100
|
function asRecord(value) {
|
|
103
|
-
return isRecord(value) ? value : null;
|
|
101
|
+
return guards.isRecord(value) ? value : null;
|
|
104
102
|
}
|
|
105
103
|
function asString(value, fallback) {
|
|
106
104
|
return typeof value === 'string' ? value : fallback;
|
|
@@ -657,7 +655,7 @@ async function previewMutations(doc, steps, changeMode, expectedRevision) {
|
|
|
657
655
|
});
|
|
658
656
|
}
|
|
659
657
|
const result = await fn({ atomic: true, changeMode: changeMode ?? 'direct', expectedRevision, steps });
|
|
660
|
-
return isRecord(result) ? result : {};
|
|
658
|
+
return guards.isRecord(result) ? result : {};
|
|
661
659
|
}
|
|
662
660
|
async function executeCreateTable(doc, args, placement) {
|
|
663
661
|
const fn = maybeMethod(doc, ['create', 'table']);
|
|
@@ -1052,7 +1050,7 @@ async function runReplaceTextUnchecked(doc, args) {
|
|
|
1052
1050
|
const preview = await previewMutations(doc, candidates.map((candidate) => candidate.step), args.changeMode, pre.revision);
|
|
1053
1051
|
if (preview.valid === true)
|
|
1054
1052
|
break;
|
|
1055
|
-
const failures = Array.isArray(preview.failures) ? preview.failures.filter(isRecord) : [];
|
|
1053
|
+
const failures = Array.isArray(preview.failures) ? preview.failures.filter(guards.isRecord) : [];
|
|
1056
1054
|
const absent = failures.find((failure) => failure.code === 'MATCH_NOT_FOUND' && typeof failure.stepId === 'string');
|
|
1057
1055
|
if (!absent) {
|
|
1058
1056
|
throw new Error(typeof failures[0]?.message === 'string' ? failures[0].message : 'replacement plan could not be prepared');
|
|
@@ -2713,7 +2711,7 @@ async function listAllTrackedChanges(listFn) {
|
|
|
2713
2711
|
const page = asRecord(await listFn({ offset, limit: PAGE, in: 'all' }));
|
|
2714
2712
|
const items = Array.isArray(page?.items) ? page.items : [];
|
|
2715
2713
|
for (const item of items) {
|
|
2716
|
-
if (isRecord(item))
|
|
2714
|
+
if (guards.isRecord(item))
|
|
2717
2715
|
out.push(item);
|
|
2718
2716
|
}
|
|
2719
2717
|
const total = asNumber(page?.total, out.length) ?? out.length;
|
|
@@ -4290,12 +4288,12 @@ function findTextTarget(snapshot, nodeId) {
|
|
|
4290
4288
|
return null;
|
|
4291
4289
|
}
|
|
4292
4290
|
function compactOpResult(result) {
|
|
4293
|
-
if (!isRecord(result))
|
|
4291
|
+
if (!guards.isRecord(result))
|
|
4294
4292
|
return result;
|
|
4295
4293
|
const compact = {};
|
|
4296
4294
|
if (typeof result.success === 'boolean')
|
|
4297
4295
|
compact.success = result.success;
|
|
4298
|
-
if (isRecord(result.revision))
|
|
4296
|
+
if (guards.isRecord(result.revision))
|
|
4299
4297
|
compact.revision = result.revision;
|
|
4300
4298
|
if (Array.isArray(result.steps))
|
|
4301
4299
|
compact.stepCount = result.steps.length;
|
|
@@ -4311,7 +4309,7 @@ function trackedChangeRefIds(result) {
|
|
|
4311
4309
|
}));
|
|
4312
4310
|
}
|
|
4313
4311
|
function parseScopedReplaceArgs(args, edits, changeMode, expectedRevision) {
|
|
4314
|
-
if (!isRecord(args.selector))
|
|
4312
|
+
if (!guards.isRecord(args.selector))
|
|
4315
4313
|
return null;
|
|
4316
4314
|
return {
|
|
4317
4315
|
edits,
|
|
@@ -4341,9 +4339,9 @@ async function fieldResultRanges(doc, nodeId, blockText) {
|
|
|
4341
4339
|
const limit = 500;
|
|
4342
4340
|
for (let offset = 0; offset < 100_000; offset += limit) {
|
|
4343
4341
|
const page = await fn({ limit, offset });
|
|
4344
|
-
const items = isRecord(page) && Array.isArray(page.items) ? page.items.filter(isRecord) : [];
|
|
4342
|
+
const items = guards.isRecord(page) && Array.isArray(page.items) ? page.items.filter(guards.isRecord) : [];
|
|
4345
4343
|
for (const item of items) {
|
|
4346
|
-
const address = isRecord(item.address) ? item.address : null;
|
|
4344
|
+
const address = guards.isRecord(item.address) ? item.address : null;
|
|
4347
4345
|
const inBody = typeof address?.storyId !== 'string' || address.storyId.startsWith('main:');
|
|
4348
4346
|
if (address?.blockId !== nodeId || !inBody)
|
|
4349
4347
|
continue;
|
|
@@ -4352,7 +4350,7 @@ async function fieldResultRanges(doc, nodeId, blockText) {
|
|
|
4352
4350
|
}
|
|
4353
4351
|
results.push(item.resolvedText);
|
|
4354
4352
|
}
|
|
4355
|
-
const total = isRecord(page) && typeof page.total === 'number' ? page.total : null;
|
|
4353
|
+
const total = guards.isRecord(page) && typeof page.total === 'number' ? page.total : null;
|
|
4356
4354
|
if (items.length < limit || (total != null && offset + limit >= total))
|
|
4357
4355
|
break;
|
|
4358
4356
|
}
|
|
@@ -4493,11 +4491,11 @@ async function runScopedReplaceUnchecked(doc, args) {
|
|
|
4493
4491
|
// that each one still covers the text matched in the snapshot, so a
|
|
4494
4492
|
// projection mismatch never rewrites other text.
|
|
4495
4493
|
const preview = await previewMutations(doc, steps, args.changeMode, pre.revision);
|
|
4496
|
-
const previewSteps = Array.isArray(preview.steps) ? preview.steps.filter(isRecord) : [];
|
|
4494
|
+
const previewSteps = Array.isArray(preview.steps) ? preview.steps.filter(guards.isRecord) : [];
|
|
4497
4495
|
const aligned = preview.valid === true &&
|
|
4498
4496
|
steps.every((step, index) => {
|
|
4499
4497
|
const resolutions = previewSteps.find((candidate) => candidate.stepId === step.id)?.resolutions;
|
|
4500
|
-
const resolved = Array.isArray(resolutions) && isRecord(resolutions[0]) ? resolutions[0].text : undefined;
|
|
4498
|
+
const resolved = Array.isArray(resolutions) && guards.isRecord(resolutions[0]) ? resolutions[0].text : undefined;
|
|
4501
4499
|
const span = targetedSpans[index];
|
|
4502
4500
|
return resolved === block.text.slice(span.start, span.end);
|
|
4503
4501
|
});
|
|
@@ -4530,7 +4528,7 @@ async function runScopedReplaceUnchecked(doc, args) {
|
|
|
4530
4528
|
// atomic history step. Only do so while the document is still at the
|
|
4531
4529
|
// revision this plan produced, so the undo cannot reach any other change.
|
|
4532
4530
|
let rolledBack = false;
|
|
4533
|
-
const appliedRevision = isRecord(result) && isRecord(result.revision) ? result.revision.after : undefined;
|
|
4531
|
+
const appliedRevision = guards.isRecord(result) && guards.isRecord(result.revision) ? result.revision.after : undefined;
|
|
4534
4532
|
if (wroteExactTargets &&
|
|
4535
4533
|
postBlock != null &&
|
|
4536
4534
|
postBlock.text !== expected &&
|
|
@@ -4645,13 +4643,13 @@ function markRunPropsFromMarks(marks) {
|
|
|
4645
4643
|
* paragraphIndentOf. Returns null when the paragraph mark carries nothing.
|
|
4646
4644
|
*/
|
|
4647
4645
|
function paragraphMarkRunPropsOf(payload) {
|
|
4648
|
-
const node = isRecord(payload) ? payload.node : null;
|
|
4649
|
-
if (!isRecord(node))
|
|
4646
|
+
const node = guards.isRecord(payload) ? payload.node : null;
|
|
4647
|
+
if (!guards.isRecord(node))
|
|
4650
4648
|
return null;
|
|
4651
|
-
const candidates = [isRecord(node.paragraph) ? node.paragraph.props : null, node.props];
|
|
4649
|
+
const candidates = [guards.isRecord(node.paragraph) ? node.paragraph.props : null, node.props];
|
|
4652
4650
|
for (const props of candidates) {
|
|
4653
|
-
const mark = isRecord(props) ? props.markRunProps : null;
|
|
4654
|
-
if (isRecord(mark) && Object.keys(mark).length > 0)
|
|
4651
|
+
const mark = guards.isRecord(props) ? props.markRunProps : null;
|
|
4652
|
+
if (guards.isRecord(mark) && Object.keys(mark).length > 0)
|
|
4655
4653
|
return mark;
|
|
4656
4654
|
}
|
|
4657
4655
|
return null;
|
|
@@ -4896,13 +4894,13 @@ async function listItemRunPattern(doc, text, blockId) {
|
|
|
4896
4894
|
* indent copied" rather than a thrown read.
|
|
4897
4895
|
*/
|
|
4898
4896
|
function paragraphIndentOf(payload) {
|
|
4899
|
-
const node = isRecord(payload) ? payload.node : null;
|
|
4900
|
-
if (!isRecord(node))
|
|
4897
|
+
const node = guards.isRecord(payload) ? payload.node : null;
|
|
4898
|
+
if (!guards.isRecord(node))
|
|
4901
4899
|
return null;
|
|
4902
|
-
const candidates = [isRecord(node.paragraph) ? node.paragraph.props : null, node.props];
|
|
4900
|
+
const candidates = [guards.isRecord(node.paragraph) ? node.paragraph.props : null, node.props];
|
|
4903
4901
|
for (const props of candidates) {
|
|
4904
|
-
const indent = isRecord(props) ? props.indent : null;
|
|
4905
|
-
if (!isRecord(indent))
|
|
4902
|
+
const indent = guards.isRecord(props) ? props.indent : null;
|
|
4903
|
+
if (!guards.isRecord(indent))
|
|
4906
4904
|
continue;
|
|
4907
4905
|
const twips = {};
|
|
4908
4906
|
for (const key of ['left', 'right', 'firstLine', 'hanging']) {
|
|
@@ -4979,7 +4977,7 @@ function marksToInline(marks) {
|
|
|
4979
4977
|
}
|
|
4980
4978
|
/** Parse a raw agent mark bag, keeping only recognized keys with valid types. */
|
|
4981
4979
|
function normalizeMarks(raw) {
|
|
4982
|
-
if (!isRecord(raw))
|
|
4980
|
+
if (!guards.isRecord(raw))
|
|
4983
4981
|
return undefined;
|
|
4984
4982
|
const m = {};
|
|
4985
4983
|
if (typeof raw.bold === 'boolean')
|
|
@@ -5009,7 +5007,7 @@ function normalizeMarks(raw) {
|
|
|
5009
5007
|
function normalizeItemSpec(raw) {
|
|
5010
5008
|
if (typeof raw === 'string')
|
|
5011
5009
|
return { text: raw, level: 0 };
|
|
5012
|
-
if (!isRecord(raw))
|
|
5010
|
+
if (!guards.isRecord(raw))
|
|
5013
5011
|
return null;
|
|
5014
5012
|
const spec = {};
|
|
5015
5013
|
const lvl = asNumber(raw.level);
|
|
@@ -5023,7 +5021,7 @@ function normalizeItemSpec(raw) {
|
|
|
5023
5021
|
if (typeof r === 'string') {
|
|
5024
5022
|
runs.push({ text: r });
|
|
5025
5023
|
}
|
|
5026
|
-
else if (isRecord(r) && typeof r.text === 'string') {
|
|
5024
|
+
else if (guards.isRecord(r) && typeof r.text === 'string') {
|
|
5027
5025
|
const rm = normalizeMarks(r.marks);
|
|
5028
5026
|
runs.push(rm ? { text: r.text, marks: rm } : { text: r.text });
|
|
5029
5027
|
}
|
|
@@ -5642,9 +5640,9 @@ async function appendListAtPlacement(doc, args) {
|
|
|
5642
5640
|
const items = Array.isArray(args.items)
|
|
5643
5641
|
? args.items.filter((t) => typeof t === 'string' && t.length > 0)
|
|
5644
5642
|
: [];
|
|
5645
|
-
const placement = isRecord(args.placement) ? args.placement : null;
|
|
5643
|
+
const placement = guards.isRecord(args.placement) ? args.placement : null;
|
|
5646
5644
|
const at = placement && typeof placement.at === 'string' ? placement.at : null;
|
|
5647
|
-
const selector = placement && isRecord(placement.selector) ? placement.selector : null;
|
|
5645
|
+
const selector = placement && guards.isRecord(placement.selector) ? placement.selector : null;
|
|
5648
5646
|
if (items.length === 0 || !placement || (at !== 'after' && at !== 'before') || !selector)
|
|
5649
5647
|
return null;
|
|
5650
5648
|
const pre = await docSnapshot.buildMutationSnapshot(doc, { includeDomains: ['blocks'] });
|
|
@@ -6042,15 +6040,15 @@ async function matchInsertedBlockFormatting(doc, preBlocks, receipt, args, avail
|
|
|
6042
6040
|
return receipt;
|
|
6043
6041
|
// The placement anchor, when the action targeted one, is the user's own
|
|
6044
6042
|
// statement of "blend in with THIS" — prefer it over type-matched siblings.
|
|
6045
|
-
const placement = isRecord(args?.placement) ? args.placement : null;
|
|
6046
|
-
const anchorSelector = placement && isRecord(placement.selector) ? placement.selector : null;
|
|
6043
|
+
const placement = guards.isRecord(args?.placement) ? args.placement : null;
|
|
6044
|
+
const anchorSelector = placement && guards.isRecord(placement.selector) ? placement.selector : null;
|
|
6047
6045
|
const anchorId = anchorSelector && typeof anchorSelector.nodeId === 'string' ? anchorSelector.nodeId : null;
|
|
6048
6046
|
const anchorRow = anchorId ? (postBlocks.find((b) => b.nodeId === anchorId) ?? null) : null;
|
|
6049
6047
|
// When the caller styled any entry explicitly (runs/marks), it took
|
|
6050
6048
|
// ownership of intra-paragraph formatting — the automatic lead-marker
|
|
6051
6049
|
// pattern pass must not fight it.
|
|
6052
6050
|
const rawTexts = Array.isArray(args?.texts) ? args.texts : [];
|
|
6053
|
-
const hasExplicitStyling = rawTexts.some((entry) => isRecord(entry) && (Array.isArray(entry.runs) || entry.marks != null));
|
|
6051
|
+
const hasExplicitStyling = rawTexts.some((entry) => guards.isRecord(entry) && (Array.isArray(entry.runs) || entry.marks != null));
|
|
6054
6052
|
const insertedTexts = rawTexts.map((entry) => {
|
|
6055
6053
|
if (typeof entry === 'string')
|
|
6056
6054
|
return entry;
|
|
@@ -6075,7 +6073,7 @@ async function matchInsertedBlockFormatting(doc, preBlocks, receipt, args, avail
|
|
|
6075
6073
|
// the previous NAME line, not like the "Name:" line directly above it.
|
|
6076
6074
|
// Only tried when matching against the neighbour found nothing, so this
|
|
6077
6075
|
// cannot change a case that already had an answer.
|
|
6078
|
-
const applied = isRecord(result?.applied) ? result.applied : null;
|
|
6076
|
+
const applied = guards.isRecord(result?.applied) ? result.applied : null;
|
|
6079
6077
|
if (createdRows.length > 1 && (!applied || Object.keys(applied).length === 0)) {
|
|
6080
6078
|
const parallel = postBlocks[createdIndex - createdRows.length];
|
|
6081
6079
|
if (parallel && preIds.has(parallel.nodeId) && !parallel.isEmpty) {
|
|
@@ -6291,7 +6289,7 @@ async function runAttachNumbering(doc, args) {
|
|
|
6291
6289
|
};
|
|
6292
6290
|
}
|
|
6293
6291
|
async function dispatchSuperdocPerformAction(doc, args) {
|
|
6294
|
-
if (!isRecord(args)) {
|
|
6292
|
+
if (!guards.isRecord(args)) {
|
|
6295
6293
|
throw new errors.SuperDocCliError('superdoc_perform_action arguments must be an object', {
|
|
6296
6294
|
code: 'INVALID_ARGUMENT',
|
|
6297
6295
|
});
|
|
@@ -6365,7 +6363,7 @@ async function dispatchSuperdocPerformAction(doc, args) {
|
|
|
6365
6363
|
if (expectedRevision !== undefined && (typeof expectedRevision !== 'string' || expectedRevision.length === 0)) {
|
|
6366
6364
|
throw new errors.SuperDocCliError('replace_text: expectedRevision must be a non-empty string from this live document handle', { code: 'INVALID_ARGUMENT' });
|
|
6367
6365
|
}
|
|
6368
|
-
if (edits.some((edit) => edit.occurrence != null) && !isRecord(args.selector)) {
|
|
6366
|
+
if (edits.some((edit) => edit.occurrence != null) && !guards.isRecord(args.selector)) {
|
|
6369
6367
|
throw new errors.SuperDocCliError('replace_text: explicit occurrence requires a selector resolving to one block', {
|
|
6370
6368
|
code: 'INVALID_ARGUMENT',
|
|
6371
6369
|
});
|
|
@@ -6995,13 +6993,13 @@ function parseExactTrackedChangeIdList(value) {
|
|
|
6995
6993
|
}
|
|
6996
6994
|
async function superdocPerformAction(doc, args) {
|
|
6997
6995
|
try {
|
|
6998
|
-
const changeMode = isRecord(args) ? parseChangeMode(args.changeMode) : undefined;
|
|
6996
|
+
const changeMode = guards.isRecord(args) ? parseChangeMode(args.changeMode) : undefined;
|
|
6999
6997
|
return await dispatchSuperdocPerformAction(changeMode ? actionChangeMode.bindActionChangeMode(doc, changeMode) : doc, args);
|
|
7000
6998
|
}
|
|
7001
6999
|
catch (error) {
|
|
7002
7000
|
if (!(error instanceof docSnapshot.MutationSnapshotError))
|
|
7003
7001
|
throw error;
|
|
7004
|
-
const action = isRecord(args) && isActionName(args.action) ? args.action : 'superdoc_perform_action';
|
|
7002
|
+
const action = guards.isRecord(args) && isActionName(args.action) ? args.action : 'superdoc_perform_action';
|
|
7005
7003
|
return {
|
|
7006
7004
|
status: 'failed',
|
|
7007
7005
|
intent: action,
|
|
@@ -7062,7 +7060,7 @@ function parseEdits(value) {
|
|
|
7062
7060
|
return undefined;
|
|
7063
7061
|
const result = [];
|
|
7064
7062
|
for (const entry of value) {
|
|
7065
|
-
if (!isRecord(entry))
|
|
7063
|
+
if (!guards.isRecord(entry))
|
|
7066
7064
|
return undefined;
|
|
7067
7065
|
const find = asString(entry.find);
|
|
7068
7066
|
const replace = asString(entry.replace) ?? '';
|
|
@@ -7082,7 +7080,7 @@ function parseEdits(value) {
|
|
|
7082
7080
|
function parseCellCandidate(cell) {
|
|
7083
7081
|
if (typeof cell === 'string')
|
|
7084
7082
|
return cell;
|
|
7085
|
-
if (isRecord(cell)) {
|
|
7083
|
+
if (guards.isRecord(cell)) {
|
|
7086
7084
|
const spec = normalizeItemSpec(cell);
|
|
7087
7085
|
if (spec)
|
|
7088
7086
|
return { text: spec.text, runs: spec.runs, marks: spec.marks };
|
|
@@ -7110,7 +7108,7 @@ function parsePlaceholderFields(value) {
|
|
|
7110
7108
|
return undefined;
|
|
7111
7109
|
const result = [];
|
|
7112
7110
|
for (const entry of value) {
|
|
7113
|
-
if (!isRecord(entry) || typeof entry.value !== 'string')
|
|
7111
|
+
if (!guards.isRecord(entry) || typeof entry.value !== 'string')
|
|
7114
7112
|
return undefined;
|
|
7115
7113
|
const label = asString(entry.label);
|
|
7116
7114
|
result.push(label ? { label, value: entry.value } : { value: entry.value });
|
|
@@ -7118,7 +7116,7 @@ function parsePlaceholderFields(value) {
|
|
|
7118
7116
|
return result;
|
|
7119
7117
|
}
|
|
7120
7118
|
function parsePlacement(value) {
|
|
7121
|
-
if (!isRecord(value))
|
|
7119
|
+
if (!guards.isRecord(value))
|
|
7122
7120
|
return undefined;
|
|
7123
7121
|
const at = asString(value.at);
|
|
7124
7122
|
if (at === 'document_end' || at === 'document_start') {
|
|
@@ -7133,7 +7131,7 @@ function parsePlacement(value) {
|
|
|
7133
7131
|
return undefined;
|
|
7134
7132
|
}
|
|
7135
7133
|
function parseSelector(value) {
|
|
7136
|
-
if (!isRecord(value))
|
|
7134
|
+
if (!guards.isRecord(value))
|
|
7137
7135
|
return undefined;
|
|
7138
7136
|
const kind = asString(value.kind);
|
|
7139
7137
|
if (kind === 'nodeId') {
|
package/dist/agent/actions.js
CHANGED
|
@@ -7,6 +7,7 @@ import { runSuperdocListTransformWorkflow } from '../action-primitives/tools/lis
|
|
|
7
7
|
import { runSuperdocStructureInsertWorkflow } from '../action-primitives/tools/structure-insert.js';
|
|
8
8
|
import { runSuperdocTextTransformWorkflow } from '../action-primitives/tools/text-transform.js';
|
|
9
9
|
import { buildDocumentSnapshot, buildMutationSnapshot, matchRunsForBlock, MutationSnapshotError, resolveSnapshotSelector, } from './doc-snapshot.js';
|
|
10
|
+
import { isRecord } from '../guards.js';
|
|
10
11
|
export const ACTION_DEFINITIONS = actionDescriptors;
|
|
11
12
|
function projectActionDefinitions(project) {
|
|
12
13
|
return Object.fromEntries(Object.entries(ACTION_DEFINITIONS).map(([name, definition]) => [name, project(definition)]));
|
|
@@ -69,9 +70,6 @@ export function getActionDispositions() {
|
|
|
69
70
|
export function isActionName(value) {
|
|
70
71
|
return typeof value === 'string' && ACTION_NAMES.includes(value);
|
|
71
72
|
}
|
|
72
|
-
function isRecord(value) {
|
|
73
|
-
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
74
|
-
}
|
|
75
73
|
/** The message to put in a receipt when something best-effort did not happen. */
|
|
76
74
|
function reasonOf(err) {
|
|
77
75
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -26,10 +26,6 @@ const BENCHMARK_ONLY_HINTS = [
|
|
|
26
26
|
function tokensFromOperationId(operationId) {
|
|
27
27
|
return operationId.split('.').slice(1);
|
|
28
28
|
}
|
|
29
|
-
function lastToken(operationId) {
|
|
30
|
-
const tokens = tokensFromOperationId(operationId);
|
|
31
|
-
return (tokens[tokens.length - 1] ?? '').toLowerCase();
|
|
32
|
-
}
|
|
33
29
|
function deriveDomain(operationId) {
|
|
34
30
|
const tokens = tokensFromOperationId(operationId);
|
|
35
31
|
const first = (tokens[0] ?? '').toLowerCase();
|