@superdoc/sdk 2.10.0-next.7 → 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.
Files changed (53) hide show
  1. package/README.md +45 -0
  2. package/dist/action-primitives/doc-index.cjs +6 -4
  3. package/dist/action-primitives/doc-index.js +6 -4
  4. package/dist/action-primitives/engine.cjs +6 -2
  5. package/dist/action-primitives/engine.js +6 -2
  6. package/dist/action-primitives/receipt.d.ts +4 -0
  7. package/dist/action-primitives/tools/structure-insert.d.ts +1 -1
  8. package/dist/agent/actions.cjs +203 -223
  9. package/dist/agent/actions.d.ts +5 -0
  10. package/dist/agent/actions.js +203 -223
  11. package/dist/agent/catalog.cjs +15 -0
  12. package/dist/agent/catalog.js +15 -0
  13. package/dist/agent/doc-snapshot.cjs +22 -46
  14. package/dist/agent/doc-snapshot.js +22 -46
  15. package/dist/agent/execution-context.cjs +385 -0
  16. package/dist/agent/execution-context.d.ts +97 -0
  17. package/dist/agent/execution-context.js +376 -0
  18. package/dist/agent/runtime.cjs +116 -158
  19. package/dist/agent/runtime.d.ts +3 -0
  20. package/dist/agent/runtime.js +117 -159
  21. package/dist/agent/v2-preset-compat.cjs +5 -1
  22. package/dist/agent/v2-preset-compat.js +4 -1
  23. package/dist/embedded-tools.generated.cjs +5 -5
  24. package/dist/embedded-tools.generated.js +5 -5
  25. package/dist/generated/client.cjs +2 -0
  26. package/dist/generated/client.d.ts +47 -0
  27. package/dist/generated/client.js +2 -0
  28. package/dist/generated/contract.cjs +1600 -1296
  29. package/dist/generated/contract.js +1600 -1296
  30. package/dist/index.cjs +11 -0
  31. package/dist/index.d.ts +3 -1
  32. package/dist/index.js +11 -0
  33. package/dist/presets/core.cjs +1 -1
  34. package/dist/presets/core.js +1 -1
  35. package/dist/runtime/document-evidence.cjs +40 -0
  36. package/dist/runtime/document-evidence.d.ts +13 -0
  37. package/dist/runtime/document-evidence.js +30 -0
  38. package/dist/runtime/host.cjs +30 -2
  39. package/dist/runtime/host.d.ts +2 -0
  40. package/dist/runtime/host.js +30 -2
  41. package/dist/runtime/process.cjs +6 -0
  42. package/dist/runtime/process.d.ts +2 -0
  43. package/dist/runtime/process.js +6 -0
  44. package/dist/runtime/sdk-version.generated.cjs +1 -1
  45. package/dist/runtime/sdk-version.generated.d.ts +1 -1
  46. package/dist/runtime/sdk-version.generated.js +1 -1
  47. package/package.json +10 -9
  48. package/tools/catalog.json +35 -0
  49. package/tools/tools-policy.json +1 -1
  50. package/tools/tools.anthropic.json +35 -0
  51. package/tools/tools.generic.json +35 -0
  52. package/tools/tools.openai.json +35 -0
  53. package/tools/tools.vercel.json +35 -0
@@ -0,0 +1,97 @@
1
+ import type { BoundDocApi, DocBlocksListBoundParams } from '../generated/client.js';
2
+ import { type DocumentSnapshot, type SnapshotBlock } from './doc-snapshot.js';
3
+ import type { AgentSelector, AgentVerificationCheck } from './ir.js';
4
+ import type { AgentReceipt, VerificationResult } from './runtime.js';
5
+ export type EvidencePolicy = 'required' | 'full';
6
+ export type EvidenceRequirements = {
7
+ evidence?: EvidencePolicy;
8
+ selectors?: readonly AgentSelector[];
9
+ checks?: readonly AgentVerificationCheck[];
10
+ completeReason?: string;
11
+ revisionOnly?: boolean;
12
+ };
13
+ export declare function supportsLocalSelector(selector: AgentSelector): boolean;
14
+ export declare function readPublicRevision(doc: BoundDocApi): Promise<string>;
15
+ export declare class DocumentFacts {
16
+ readonly doc: BoundDocApi;
17
+ readonly revision: string;
18
+ readonly complete?: DocumentSnapshot | undefined;
19
+ readonly summaryCounts: {
20
+ blocks: number;
21
+ paragraphs: number;
22
+ headings: number;
23
+ tables: number;
24
+ lists: number;
25
+ images: number;
26
+ comments: number;
27
+ trackedChanges: number;
28
+ sections: number;
29
+ fields: number;
30
+ hyperlinks: number;
31
+ bookmarks: number;
32
+ contentControls: number;
33
+ permissionRanges: number;
34
+ styles: number;
35
+ headers: number;
36
+ footers: number;
37
+ } | undefined;
38
+ readonly blocks: Map<string, SnapshotBlock>;
39
+ private readonly counts;
40
+ private readonly selections;
41
+ constructor(doc: BoundDocApi, revision: string, complete?: DocumentSnapshot | undefined, summaryCounts?: {
42
+ blocks: number;
43
+ paragraphs: number;
44
+ headings: number;
45
+ tables: number;
46
+ lists: number;
47
+ images: number;
48
+ comments: number;
49
+ trackedChanges: number;
50
+ sections: number;
51
+ fields: number;
52
+ hyperlinks: number;
53
+ bookmarks: number;
54
+ contentControls: number;
55
+ permissionRanges: number;
56
+ styles: number;
57
+ headers: number;
58
+ footers: number;
59
+ } | undefined);
60
+ get summary(): NonNullable<AgentReceipt['preSnapshot']>;
61
+ fence(): Promise<void>;
62
+ page(input: DocBlocksListBoundParams): Promise<SnapshotBlock[]>;
63
+ count(nodeType?: string): Promise<number>;
64
+ block(nodeId: string): Promise<SnapshotBlock | undefined>;
65
+ select(selector: AgentSelector): Promise<readonly string[]>;
66
+ target(selector: AgentSelector): Promise<SnapshotBlock | undefined>;
67
+ tableShape(nodeId: string): Promise<{
68
+ rows: number;
69
+ columns: number;
70
+ } | undefined>;
71
+ completeIndex<T>(reason: string, acquire: () => Promise<T>): Promise<{
72
+ value: T;
73
+ coverage: 'complete';
74
+ reason: string;
75
+ }>;
76
+ prepare(checks: readonly AgentVerificationCheck[]): Promise<void>;
77
+ }
78
+ export declare class ExecutionContext {
79
+ readonly source: BoundDocApi;
80
+ readonly requirements: EvidenceRequirements;
81
+ readonly document: BoundDocApi;
82
+ readonly executedOperations: NonNullable<AgentReceipt['executedOperations']>[number][];
83
+ readonly selectedTargets: NonNullable<AgentReceipt['selectedTargets']>[number][];
84
+ pre?: DocumentFacts;
85
+ post?: DocumentFacts;
86
+ fallbackReason?: string;
87
+ private currentRevision?;
88
+ private legacyRevisionOnly;
89
+ constructor(source: BoundDocApi, requirements?: EvidenceRequirements);
90
+ start(): Promise<DocumentFacts>;
91
+ finishRevision(): Promise<DocumentFacts>;
92
+ finish(): Promise<DocumentFacts>;
93
+ private assertExecutionRevision;
94
+ receipt(intent: string, verification: readonly VerificationResult[], extra?: Partial<AgentReceipt>): AgentReceipt;
95
+ failure(intent: string, error: unknown): AgentReceipt;
96
+ }
97
+ export declare function evaluateFactChecks(pre: DocumentFacts | undefined, post: DocumentFacts, checks: readonly AgentVerificationCheck[]): Promise<VerificationResult[]>;
@@ -0,0 +1,376 @@
1
+ import { getDocumentRevisionReader, supportsDocumentFacts, forwardDocumentEvidence, getMutationRevision, } from '../runtime/document-evidence.js';
2
+ import { SuperDocCliError } from '../runtime/errors.js';
3
+ import { buildDocumentSnapshot, buildMutationSnapshot, resolveSnapshotSelector, } from './doc-snapshot.js';
4
+ import { getOperationCatalogEntry } from './operation-catalog.js';
5
+ export function supportsLocalSelector(selector) {
6
+ return (selector.kind === 'nodeId' ||
7
+ selector.kind === 'textSearch' ||
8
+ selector.kind === 'placement' ||
9
+ (selector.kind === 'ordinal' &&
10
+ ['blockOrdinal', 'headingOrdinal', 'tableOrdinal'].includes(selector.ordinalKind)) ||
11
+ (selector.kind === 'relative' && supportsLocalSelector(selector.target)));
12
+ }
13
+ function localCheck(check) {
14
+ return (['revision-changed', 'revision-unchanged', 'block-text-contains', 'block-text-equals', 'table-shape'].includes(check.kind) ||
15
+ (check.kind === 'block-count-delta' && ['paragraph', 'heading', 'listItem', 'table'].includes(check.nodeType)));
16
+ }
17
+ export async function readPublicRevision(doc) {
18
+ const revision = await getDocumentRevisionReader(doc)?.();
19
+ if (revision !== undefined)
20
+ return revision;
21
+ const page = await doc.blocks.list({ offset: Number.MAX_SAFE_INTEGER, limit: 1 });
22
+ if (!page.revision || page.revision === 'unknown')
23
+ throw new SuperDocCliError('Required document revision is unavailable.', { code: 'EVIDENCE_UNAVAILABLE' });
24
+ return page.revision;
25
+ }
26
+ export class DocumentFacts {
27
+ doc;
28
+ revision;
29
+ complete;
30
+ summaryCounts;
31
+ blocks = new Map();
32
+ counts = new Map();
33
+ selections = new Map();
34
+ constructor(doc, revision, complete, summaryCounts = complete?.counts) {
35
+ this.doc = doc;
36
+ this.revision = revision;
37
+ this.complete = complete;
38
+ this.summaryCounts = summaryCounts;
39
+ }
40
+ get summary() {
41
+ return { revision: this.revision, ...(this.summaryCounts ? { counts: this.summaryCounts } : {}) };
42
+ }
43
+ async fence() {
44
+ if ((await readPublicRevision(this.doc)) !== this.revision)
45
+ throw new SuperDocCliError('Document changed while acquiring execution evidence.', { code: 'REVISION_CONFLICT' });
46
+ }
47
+ async page(input) {
48
+ const result = await this.doc.blocks.list({ ...input, includeText: true });
49
+ if (result.revision !== this.revision)
50
+ throw new SuperDocCliError('Document changed while resolving a target.', { code: 'REVISION_CONFLICT' });
51
+ const blocks = result.blocks.map((block) => ({
52
+ ...block,
53
+ ordinal: block.ordinal + 1,
54
+ text: block.text ?? '',
55
+ textPreview: block.textPreview ?? null,
56
+ styleId: block.styleId ?? null,
57
+ }));
58
+ for (const block of blocks)
59
+ this.blocks.set(block.nodeId, block);
60
+ return blocks;
61
+ }
62
+ async count(nodeType) {
63
+ const key = nodeType ?? '*';
64
+ if (this.counts.has(key))
65
+ return this.counts.get(key);
66
+ if (this.complete)
67
+ return nodeType
68
+ ? this.complete.blocks.filter((block) => block.nodeType === nodeType).length
69
+ : this.complete.blocks.length;
70
+ const result = await this.doc.blocks.list({
71
+ offset: Number.MAX_SAFE_INTEGER,
72
+ limit: 1,
73
+ ...(nodeType ? { nodeTypes: [nodeType] } : {}),
74
+ });
75
+ if (result.revision !== this.revision || !Number.isSafeInteger(result.total))
76
+ throw new SuperDocCliError('Required count is unavailable at the execution revision.', {
77
+ code: 'EVIDENCE_UNAVAILABLE',
78
+ });
79
+ this.counts.set(key, result.total);
80
+ return result.total;
81
+ }
82
+ async block(nodeId) {
83
+ if (this.complete)
84
+ return this.complete.blocks.find((block) => block.nodeId === nodeId);
85
+ if (!this.blocks.has(nodeId))
86
+ await this.page({ nodeIds: [nodeId], limit: 1 });
87
+ return this.blocks.get(nodeId);
88
+ }
89
+ async select(selector) {
90
+ if (this.complete)
91
+ return resolveSnapshotSelector(this.complete, selector);
92
+ const key = JSON.stringify(selector);
93
+ if (this.selections.has(key))
94
+ return this.selections.get(key);
95
+ let blocks = [];
96
+ if (selector.kind === 'nodeId') {
97
+ const block = await this.block(selector.nodeId);
98
+ if (block)
99
+ blocks = [block];
100
+ }
101
+ else if (selector.kind === 'textSearch') {
102
+ const terms = selector.terms.filter((term) => term.trim());
103
+ if (terms.length && selector.nodeTypes?.length !== 0)
104
+ blocks = await this.page({
105
+ textSearch: { terms: [...terms], match: selector.match, caseSensitive: selector.caseSensitive },
106
+ nodeTypes: [...(selector.nodeTypes ?? ['paragraph', 'heading', 'listItem'])],
107
+ offset: (selector.occurrence ?? 1) - 1,
108
+ limit: 1,
109
+ });
110
+ }
111
+ else if (selector.kind === 'placement') {
112
+ const offset = selector.at === 'document_start' ? 0 : (await this.count()) - 1;
113
+ if (offset >= 0)
114
+ blocks = await this.page({ offset, limit: 1 });
115
+ }
116
+ else if (selector.kind === 'ordinal') {
117
+ blocks = await this.page({
118
+ offset: selector.value - 1,
119
+ limit: 1,
120
+ ...(selector.ordinalKind === 'headingOrdinal'
121
+ ? { nodeTypes: ['heading'] }
122
+ : selector.ordinalKind === 'tableOrdinal'
123
+ ? { nodeTypes: ['table'] }
124
+ : {}),
125
+ });
126
+ }
127
+ else if (selector.kind === 'relative') {
128
+ for (const id of await this.select(selector.target)) {
129
+ const target = await this.block(id);
130
+ const offset = target ? target.ordinal - 1 + (selector.position === 'before' ? -1 : 1) : -1;
131
+ if (offset >= 0)
132
+ blocks.push(...(await this.page({ offset, limit: 1 })));
133
+ }
134
+ }
135
+ else
136
+ throw new SuperDocCliError('Selector requires complete evidence.', { code: 'EVIDENCE_UNAVAILABLE' });
137
+ const ids = [...new Set(blocks.map((block) => block.nodeId))];
138
+ this.selections.set(key, ids);
139
+ return ids;
140
+ }
141
+ async target(selector) {
142
+ const ids = await this.select(selector);
143
+ if (this.complete && selector.kind === 'tableCell' && ids.length === 1) {
144
+ const cell = this.complete.tables.flatMap((table) => table.cells).find((cell) => cell.nodeId === ids[0]);
145
+ if (cell?.nodeId)
146
+ return {
147
+ nodeId: cell.nodeId,
148
+ nodeType: 'paragraph',
149
+ text: cell.text,
150
+ textPreview: cell.text,
151
+ styleId: null,
152
+ ordinal: 0,
153
+ };
154
+ }
155
+ return ids.length === 1 ? this.block(ids[0]) : undefined;
156
+ }
157
+ async tableShape(nodeId) {
158
+ if (this.complete)
159
+ return this.complete.tables.find((table) => table.nodeId === nodeId);
160
+ return this.doc.tables.get({ nodeId });
161
+ }
162
+ async completeIndex(reason, acquire) {
163
+ const value = await acquire();
164
+ await this.fence();
165
+ return { value, coverage: 'complete', reason };
166
+ }
167
+ async prepare(checks) {
168
+ for (const check of checks)
169
+ if (check.kind === 'block-count-delta')
170
+ await this.count(check.nodeType);
171
+ }
172
+ }
173
+ export class ExecutionContext {
174
+ source;
175
+ requirements;
176
+ document;
177
+ executedOperations = [];
178
+ selectedTargets = [];
179
+ pre;
180
+ post;
181
+ fallbackReason;
182
+ currentRevision;
183
+ legacyRevisionOnly = false;
184
+ constructor(source, requirements = {}) {
185
+ this.source = source;
186
+ this.requirements = requirements;
187
+ if (requirements.evidence !== undefined && !['required', 'full'].includes(requirements.evidence))
188
+ throw new SuperDocCliError('evidence must be required or full.', { code: 'INVALID_ARGUMENT' });
189
+ const wrap = (value, path) => new Proxy(value, {
190
+ get: (target, property, receiver) => {
191
+ const member = Reflect.get(target, property, receiver);
192
+ if (typeof property !== 'string')
193
+ return member;
194
+ const memberPath = [...path, property];
195
+ const operationId = `doc.${memberPath.join('.')}`;
196
+ if (typeof member === 'function' && getOperationCatalogEntry(operationId)?.isMutating) {
197
+ return async (args = {}, options) => {
198
+ if (operationId === 'doc.format.apply' &&
199
+ supportsDocumentFacts(this.source) &&
200
+ typeof args.blockId === 'string') {
201
+ const { blockId, start, end, ...rest } = args;
202
+ args = {
203
+ ...rest,
204
+ target: {
205
+ kind: 'selection',
206
+ start: { kind: 'text', blockId, offset: start },
207
+ end: { kind: 'text', blockId, offset: end },
208
+ },
209
+ };
210
+ }
211
+ if (this.legacyRevisionOnly && this.executedOperations.length)
212
+ throw new SuperDocCliError('This host cannot bind revisions across multiple writes.', {
213
+ code: 'EVIDENCE_UNAVAILABLE',
214
+ });
215
+ const guarded = (supportsDocumentFacts(this.source) || this.legacyRevisionOnly) &&
216
+ !['doc.history.undo', 'doc.history.redo', 'doc.plan.execute'].includes(operationId);
217
+ const revision = guarded ? (this.currentRevision ?? (await readPublicRevision(this.source))) : undefined;
218
+ const result = await Reflect.apply(member, target, guarded
219
+ ? [
220
+ { ...args, expectedRevision: args.expectedRevision ?? revision },
221
+ { ...options, expectedRevision: options?.expectedRevision ?? revision },
222
+ ]
223
+ : [args, options]);
224
+ this.executedOperations.push({ operationId, result });
225
+ if (result?.success === false)
226
+ throw new SuperDocCliError('Document operation did not apply.', {
227
+ code: result.failure?.code ?? 'APPLY_FAILED',
228
+ details: result,
229
+ });
230
+ if (supportsDocumentFacts(this.source)) {
231
+ const applied = getMutationRevision(result);
232
+ if (!applied ||
233
+ applied.before !== this.currentRevision ||
234
+ !applied.after ||
235
+ applied.after === 'unknown')
236
+ throw new SuperDocCliError('Committed operation revision is unavailable or changed.', {
237
+ code: 'EVIDENCE_UNAVAILABLE',
238
+ });
239
+ this.currentRevision = applied.after;
240
+ }
241
+ return result;
242
+ };
243
+ }
244
+ if (member && typeof member === 'object')
245
+ return wrap(member, memberPath);
246
+ return member;
247
+ },
248
+ });
249
+ this.document = wrap(source, []);
250
+ forwardDocumentEvidence(source, this.document);
251
+ }
252
+ async start() {
253
+ const { evidence, selectors = [], checks = [], completeReason } = this.requirements;
254
+ const compactRevision = this.requirements.revisionOnly && evidence !== 'full' && !completeReason
255
+ ? await getDocumentRevisionReader(this.source)?.()
256
+ : undefined;
257
+ this.legacyRevisionOnly = compactRevision !== undefined && !supportsDocumentFacts(this.source);
258
+ this.fallbackReason =
259
+ completeReason ??
260
+ (!supportsDocumentFacts(this.source) && !this.legacyRevisionOnly
261
+ ? 'host lacks typed execution facts'
262
+ : selectors.some((selector) => !supportsLocalSelector(selector))
263
+ ? 'selector requires complete index'
264
+ : checks.some((check) => !localCheck(check))
265
+ ? 'verification requires complete evidence'
266
+ : undefined);
267
+ const complete = evidence === 'full' || this.fallbackReason ? await buildMutationSnapshot(this.source) : undefined;
268
+ if (complete && (complete.revision === 'unknown' || complete.diagnostics.some((item) => item.section === 'info')))
269
+ throw new SuperDocCliError('Required complete evidence is unavailable.', { code: 'EVIDENCE_UNAVAILABLE' });
270
+ this.pre = new DocumentFacts(this.source, complete?.revision ?? compactRevision ?? (await readPublicRevision(this.source)), complete);
271
+ this.currentRevision = this.pre.revision;
272
+ await this.pre.prepare(checks);
273
+ return this.pre;
274
+ }
275
+ async finishRevision() {
276
+ if (this.requirements.evidence === 'full')
277
+ return this.finish();
278
+ const summary = this.pre?.complete ? await buildDocumentSnapshot(this.source, { countsOnly: true }) : undefined;
279
+ if (summary && (summary.revision === 'unknown' || summary.diagnostics.some((item) => item.section === 'info')))
280
+ throw new SuperDocCliError('Required summary is unavailable.', { code: 'EVIDENCE_UNAVAILABLE' });
281
+ const revision = summary?.revision ?? (await readPublicRevision(this.source));
282
+ this.assertExecutionRevision(revision);
283
+ this.post = new DocumentFacts(this.source, revision, undefined, summary?.counts);
284
+ this.currentRevision = this.post.revision;
285
+ return this.post;
286
+ }
287
+ async finish() {
288
+ const complete = this.pre?.complete ? await buildMutationSnapshot(this.source) : undefined;
289
+ if (complete && (complete.revision === 'unknown' || complete.diagnostics.some((item) => item.section === 'info')))
290
+ throw new SuperDocCliError('Required complete evidence is unavailable.', { code: 'EVIDENCE_UNAVAILABLE' });
291
+ const revision = complete?.revision ?? (await readPublicRevision(this.source));
292
+ this.assertExecutionRevision(revision);
293
+ this.post = new DocumentFacts(this.source, revision, complete);
294
+ this.currentRevision = this.post.revision;
295
+ return this.post;
296
+ }
297
+ assertExecutionRevision(revision) {
298
+ if (supportsDocumentFacts(this.source) && this.currentRevision !== revision)
299
+ throw new SuperDocCliError('Document changed outside this execution.', { code: 'REVISION_CONFLICT' });
300
+ }
301
+ receipt(intent, verification, extra = {}) {
302
+ return {
303
+ status: verification.every((check) => check.passed) ? 'ok' : 'failed',
304
+ intent,
305
+ preSnapshot: this.pre?.summary,
306
+ postSnapshot: this.post?.summary,
307
+ selectedTargets: this.selectedTargets,
308
+ executedOperations: this.executedOperations,
309
+ verification,
310
+ ...(this.fallbackReason ? { evidenceFallback: this.fallbackReason } : {}),
311
+ ...extra,
312
+ };
313
+ }
314
+ failure(intent, error) {
315
+ const err = error;
316
+ return this.receipt(intent, [], {
317
+ status: err?.code === 'AMBIGUOUS_SELECTOR' ? 'aborted' : 'failed',
318
+ errors: [
319
+ { code: err?.code ?? 'ACTION_FAILED', message: err?.message ?? String(error), recovery: { kind: 'reinspect' } },
320
+ ],
321
+ });
322
+ }
323
+ }
324
+ export async function evaluateFactChecks(pre, post, checks) {
325
+ const results = [];
326
+ for (const check of checks) {
327
+ if ([
328
+ 'revision-changed',
329
+ 'revision-unchanged',
330
+ 'block-count-delta',
331
+ 'comment-count-delta',
332
+ 'tracked-change-count-delta',
333
+ ].includes(check.kind) &&
334
+ !pre) {
335
+ results.push({ check, passed: false, detail: `${check.kind} requires a baseline snapshot` });
336
+ continue;
337
+ }
338
+ if (check.kind === 'revision-changed' || check.kind === 'revision-unchanged')
339
+ results.push({
340
+ check,
341
+ passed: check.kind === 'revision-changed' ? pre.revision !== post.revision : pre.revision === post.revision,
342
+ detail: `pre=${pre.revision} post=${post.revision}`,
343
+ });
344
+ else if (check.kind === 'block-count-delta') {
345
+ const before = await pre.count(check.nodeType), after = await post.count(check.nodeType);
346
+ results.push({
347
+ check,
348
+ passed: after - before === check.delta,
349
+ detail: `pre=${before} post=${after} delta=${after - before}`,
350
+ });
351
+ }
352
+ else if (check.kind === 'block-text-contains' || check.kind === 'block-text-equals') {
353
+ const block = await post.block(check.nodeId);
354
+ results.push({
355
+ check,
356
+ passed: !!block && (check.kind === 'block-text-equals' ? block.text === check.text : block.text.includes(check.text)),
357
+ });
358
+ }
359
+ else if (check.kind === 'table-shape') {
360
+ const table = await post.tableShape(check.nodeId);
361
+ results.push({ check, passed: !!table && table.rows === check.rows && table.columns === check.columns });
362
+ }
363
+ else if (check.kind === 'comment-count-delta' || check.kind === 'tracked-change-count-delta') {
364
+ const key = check.kind === 'comment-count-delta' ? 'comments' : 'trackedChanges';
365
+ const before = pre?.complete?.counts[key], after = post.complete?.counts[key];
366
+ results.push({
367
+ check,
368
+ passed: before !== undefined && after !== undefined && after - before === check.delta,
369
+ detail: `pre=${before} post=${after}`,
370
+ });
371
+ }
372
+ else
373
+ results.push({ check, passed: false, detail: 'Check requires domain-specific evidence.' });
374
+ }
375
+ return results;
376
+ }