@superdoc/sdk 2.10.0-next.1 → 2.10.0-next.10

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 (57) 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 +384 -373
  9. package/dist/agent/actions.d.ts +5 -0
  10. package/dist/agent/actions.js +385 -374
  11. package/dist/agent/catalog.cjs +15 -0
  12. package/dist/agent/catalog.js +15 -0
  13. package/dist/agent/doc-snapshot.cjs +205 -86
  14. package/dist/agent/doc-snapshot.d.ts +11 -0
  15. package/dist/agent/doc-snapshot.js +203 -86
  16. package/dist/agent/execution-context.cjs +385 -0
  17. package/dist/agent/execution-context.d.ts +97 -0
  18. package/dist/agent/execution-context.js +376 -0
  19. package/dist/agent/runtime.cjs +123 -117
  20. package/dist/agent/runtime.d.ts +3 -0
  21. package/dist/agent/runtime.js +124 -118
  22. package/dist/agent/v2-preset-compat.cjs +5 -1
  23. package/dist/agent/v2-preset-compat.js +4 -1
  24. package/dist/embedded-tools.generated.cjs +5 -5
  25. package/dist/embedded-tools.generated.js +5 -5
  26. package/dist/generated/client.cjs +2 -0
  27. package/dist/generated/client.d.ts +85 -0
  28. package/dist/generated/client.js +2 -0
  29. package/dist/generated/contract.cjs +1796 -1297
  30. package/dist/generated/contract.js +1797 -1297
  31. package/dist/index.cjs +23 -0
  32. package/dist/index.d.ts +7 -2
  33. package/dist/index.js +23 -0
  34. package/dist/presets/core.cjs +1 -1
  35. package/dist/presets/core.js +1 -1
  36. package/dist/runtime/document-evidence.cjs +40 -0
  37. package/dist/runtime/document-evidence.d.ts +13 -0
  38. package/dist/runtime/document-evidence.js +30 -0
  39. package/dist/runtime/document-rpc.cjs +27 -0
  40. package/dist/runtime/document-rpc.d.ts +2 -0
  41. package/dist/runtime/document-rpc.js +25 -0
  42. package/dist/runtime/host.cjs +65 -4
  43. package/dist/runtime/host.d.ts +3 -0
  44. package/dist/runtime/host.js +66 -5
  45. package/dist/runtime/process.cjs +38 -0
  46. package/dist/runtime/process.d.ts +16 -0
  47. package/dist/runtime/process.js +38 -0
  48. package/dist/runtime/sdk-version.generated.cjs +1 -1
  49. package/dist/runtime/sdk-version.generated.d.ts +1 -1
  50. package/dist/runtime/sdk-version.generated.js +1 -1
  51. package/package.json +10 -8
  52. package/tools/catalog.json +89 -0
  53. package/tools/tools-policy.json +1 -1
  54. package/tools/tools.anthropic.json +89 -0
  55. package/tools/tools.generic.json +89 -0
  56. package/tools/tools.openai.json +89 -0
  57. package/tools/tools.vercel.json +89 -0
@@ -1,3 +1,4 @@
1
+ import { type EvidencePolicy } from './execution-context.js';
1
2
  /**
2
3
  * Clean agent runtime.
3
4
  *
@@ -43,9 +44,11 @@ export type AgentInspectArgs = {
43
44
  blockRunsLimit?: number;
44
45
  };
45
46
  export type AgentApplyArgs = {
47
+ evidence?: EvidencePolicy;
46
48
  plan: AgentPlan;
47
49
  };
48
50
  export type AgentVerifyArgs = {
51
+ evidence?: EvidencePolicy;
49
52
  checks: readonly AgentVerificationCheck[];
50
53
  saveReopen?: boolean;
51
54
  };
@@ -1,7 +1,8 @@
1
+ import { ExecutionContext, evaluateFactChecks } from './execution-context.js';
1
2
  import { CONTRACT } from '../generated/contract.js';
2
3
  import { SuperDocCliError } from '../runtime/errors.js';
3
4
  import { validatePlan } from './ir.js';
4
- import { buildDocumentSnapshot, resolveSnapshotSelector, AmbiguousSelectorError, } from './doc-snapshot.js';
5
+ import { buildDocumentSnapshot, buildMutationSnapshot, resolveSnapshotSelector, AmbiguousSelectorError, } from './doc-snapshot.js';
5
6
  import { getOperationCatalogEntry } from './operation-catalog.js';
6
7
  const RESERVED_ARG_KEYS = new Set(['sessionId', 'doc']);
7
8
  function ensureKnownOperation(operationId) {
@@ -224,8 +225,8 @@ function computeDeltaChecks(pre, post, checks, saveReopen) {
224
225
  else if (check.kind === 'comment-count-delta') {
225
226
  results.push({
226
227
  check,
227
- passed: post.comments.length - pre.comments.length === check.delta,
228
- detail: `pre=${pre.comments.length} post=${post.comments.length}`,
228
+ passed: post.counts.comments - pre.counts.comments === check.delta,
229
+ detail: `pre=${pre.counts.comments} post=${post.counts.comments}`,
229
230
  });
230
231
  }
231
232
  else if (check.kind === 'tracked-change-count-delta') {
@@ -310,7 +311,7 @@ async function trySaveReopen(doc, checks) {
310
311
  await saveAny.call(doc, {});
311
312
  // Rebuild a fresh snapshot after save. Host-level true reopen still needs
312
313
  // a new document handle, which this runtime cannot force on its own.
313
- const fresh = await buildDocumentSnapshot(doc);
314
+ const fresh = await buildMutationSnapshot(doc);
314
315
  for (const check of checks) {
315
316
  if (check.kind === 'save-reopen-text-contains') {
316
317
  const found = fresh.blocks.some((b) => b.text.includes(check.text));
@@ -333,124 +334,150 @@ async function trySaveReopen(doc, checks) {
333
334
  function verificationNeedsSaveReopen(checks) {
334
335
  return checks.some((check) => check.kind === 'document-saves-cleanly' || check.kind === 'save-reopen-text-contains');
335
336
  }
337
+ function revisionOnlyParagraphStep(plan) {
338
+ if (plan.atomic || plan.preconditions || plan.postconditions || plan.expectedDiff || plan.steps.length !== 2) {
339
+ return undefined;
340
+ }
341
+ const [apply, verify] = plan.steps;
342
+ if (apply?.kind !== 'apply' ||
343
+ apply.operationId !== 'doc.create.paragraph' ||
344
+ apply.atomic ||
345
+ verify?.kind !== 'verify' ||
346
+ verify.saveReopen ||
347
+ verify.checks.length === 0 ||
348
+ !verify.checks.every((check) => check.kind === 'revision-changed' || check.kind === 'revision-unchanged')) {
349
+ return undefined;
350
+ }
351
+ // Only literal body inputs are eligible. Binding tokens, story targets and
352
+ // additional operation options must retain the complete evidence path.
353
+ if (Object.keys(apply.args).some((key) => key !== 'text' && key !== 'at'))
354
+ return undefined;
355
+ if (apply.args.text !== undefined && typeof apply.args.text !== 'string')
356
+ return undefined;
357
+ const at = apply.args.at;
358
+ if (at !== undefined) {
359
+ if (!isRecord(at))
360
+ return undefined;
361
+ if (at.kind === 'documentStart' || at.kind === 'documentEnd') {
362
+ if (Object.keys(at).some((key) => key !== 'kind'))
363
+ return undefined;
364
+ }
365
+ else if (at.kind === 'before' || at.kind === 'after') {
366
+ if (Object.keys(at).some((key) => key !== 'kind' && key !== 'target') ||
367
+ !isRecord(at.target) ||
368
+ at.target.kind !== 'block' ||
369
+ typeof at.target.nodeId !== 'string' ||
370
+ typeof at.target.nodeType !== 'string' ||
371
+ Object.keys(at.target).some((key) => !['kind', 'nodeId', 'nodeType'].includes(key))) {
372
+ return undefined;
373
+ }
374
+ }
375
+ else
376
+ return undefined;
377
+ }
378
+ return apply;
379
+ }
336
380
  export async function agentApply(doc, args) {
337
381
  const plan = args.plan;
338
382
  const validation = validatePlan(plan);
339
- if (!validation.ok) {
383
+ if (!validation.ok)
340
384
  return {
341
385
  status: 'failed',
342
386
  intent: plan.intent,
343
- preSnapshot: { revision: 'unknown', counts: emptyCounts() },
344
387
  selectedTargets: [],
345
388
  executedOperations: [],
346
389
  verification: [],
347
- errors: validation.errors.map((e) => ({ code: e.code, message: e.message })),
390
+ errors: validation.errors.map((error) => ({ code: error.code, message: error.message })),
348
391
  };
349
- }
350
- const preSnapshot = await buildDocumentSnapshot(doc);
351
- const selectedTargets = [];
352
- const executedOperations = [];
392
+ const verifyStep = plan.steps.find((step) => step.kind === 'verify');
393
+ const checks = verifyStep?.kind === 'verify' ? verifyStep.checks : [];
394
+ let written = false;
395
+ const baselineAfterWrite = plan.steps.some((step) => {
396
+ if (step.kind === 'apply')
397
+ written = true;
398
+ return written && step.kind === 'select' && step.selector.kind !== 'ref';
399
+ });
400
+ const context = new ExecutionContext(doc, {
401
+ evidence: args.evidence,
402
+ revisionOnly: !!revisionOnlyParagraphStep(plan),
403
+ checks,
404
+ selectors: plan.steps.flatMap((step) => step.kind === 'select' && step.selector.kind !== 'ref' ? [step.selector] : []),
405
+ completeReason: plan.atomic ||
406
+ plan.preconditions ||
407
+ plan.postconditions ||
408
+ plan.expectedDiff ||
409
+ plan.steps.some((step) => step.kind === 'inspect') ||
410
+ plan.steps.filter((step) => step.kind === 'apply').length > 1
411
+ ? 'plan requires complete baseline evidence'
412
+ : baselineAfterWrite
413
+ ? 'plan selects baseline targets after a write'
414
+ : planTouchesRiskyDomain(plan)
415
+ ? 'plan requires document-wide save/reopen evidence'
416
+ : plan.steps.some((step) => step.kind === 'apply' &&
417
+ ['doc.history.undo', 'doc.history.redo', 'doc.plan.execute'].includes(step.operationId))
418
+ ? 'operation requires complete execution evidence'
419
+ : undefined,
420
+ });
353
421
  const bindings = new Map();
354
422
  try {
423
+ const pre = await context.start();
355
424
  for (const step of plan.steps) {
356
425
  if (step.kind === 'select') {
357
- const matched = resolveSelectorWithBindings(preSnapshot, step.selector, bindings);
358
- if (step.requireUnique && matched.length !== 1) {
426
+ const matched = pre.complete
427
+ ? resolveSelectorWithBindings(pre.complete, step.selector, bindings)
428
+ : step.selector.kind === 'ref'
429
+ ? extractBoundNodeIds(resolveBindingRef(bindings, step.selector.ref))
430
+ : await pre.select(step.selector);
431
+ if (step.requireUnique && matched.length !== 1)
359
432
  throw new AmbiguousSelectorError(`Selector did not resolve uniquely (matched ${matched.length}).`, matched.map((nodeId) => ({ nodeId, description: nodeId })));
360
- }
361
- selectedTargets.push({ selector: step.selector, matched });
362
- if (step.bind) {
433
+ context.selectedTargets.push({ selector: step.selector, matched });
434
+ if (step.bind)
363
435
  bindings.set(step.bind, matched.length === 1 ? matched[0] : [...matched]);
364
- }
365
- continue;
366
436
  }
367
- if (step.kind === 'inspect') {
368
- const inspectArgs = ensureClean(resolveBindingTokens(step.args, bindings));
369
- const method = resolveDocMethod(doc, step.operationId);
370
- const result = await method(inspectArgs);
371
- if (step.bind)
437
+ else if (step.kind === 'inspect' || step.kind === 'apply') {
438
+ const input = ensureClean(resolveBindingTokens(step.args, bindings));
439
+ const method = resolveDocMethod(context.document, step.operationId);
440
+ const result = await method(step.kind === 'apply' && step.changeMode && getOperationCatalogEntry(step.operationId)?.supportsChangeMode
441
+ ? { ...input, changeMode: step.changeMode }
442
+ : input);
443
+ if (step.kind === 'inspect' && step.bind)
372
444
  bindings.set(step.bind, result);
373
- continue;
374
- }
375
- if (step.kind === 'apply') {
376
- ensureKnownOperation(step.operationId);
377
- const applyArgs = ensureClean(resolveBindingTokens(step.args, bindings));
378
- const method = resolveDocMethod(doc, step.operationId);
379
- const argsWithMode = step.changeMode != null && getOperationCatalogEntry(step.operationId)?.supportsChangeMode
380
- ? { ...applyArgs, changeMode: step.changeMode }
381
- : applyArgs;
382
- const result = await method(argsWithMode);
383
- executedOperations.push({ operationId: step.operationId, rationale: step.rationale, result });
445
+ if (step.kind === 'apply' && context.executedOperations.length)
446
+ context.executedOperations.at(-1).rationale = step.rationale;
384
447
  }
385
448
  }
449
+ const post = await context.finish();
450
+ const saveReopen = (verifyStep?.kind === 'verify' && (verifyStep.saveReopen || verificationNeedsSaveReopen(checks))) ||
451
+ planTouchesRiskyDomain(plan)
452
+ ? await trySaveReopen(doc, checks)
453
+ : undefined;
454
+ const verification = pre.complete && post.complete
455
+ ? computeDeltaChecks(pre.complete, post.complete, checks, saveReopen)
456
+ : await evaluateFactChecks(pre, post, checks);
457
+ if (!post.complete && checks.some((check) => !['revision-changed', 'revision-unchanged'].includes(check.kind)))
458
+ await post.fence();
459
+ return context.receipt(plan.intent, verification, { saveReopen });
386
460
  }
387
- catch (err) {
388
- if (err instanceof AmbiguousSelectorError) {
389
- return {
390
- status: 'aborted',
391
- intent: plan.intent,
392
- preSnapshot: { revision: preSnapshot.revision, counts: preSnapshot.counts },
393
- selectedTargets,
394
- executedOperations,
395
- verification: [],
396
- errors: [
397
- {
398
- code: err.code,
399
- message: err.message,
400
- },
401
- ],
402
- };
403
- }
404
- const message = err instanceof Error ? err.message : String(err);
405
- return {
406
- status: 'failed',
407
- intent: plan.intent,
408
- preSnapshot: { revision: preSnapshot.revision, counts: preSnapshot.counts },
409
- selectedTargets,
410
- executedOperations,
411
- verification: [],
412
- errors: [{ code: 'APPLY_FAILED', message }],
413
- };
461
+ catch (error) {
462
+ return context.failure(plan.intent, error);
414
463
  }
415
- const postSnapshot = await buildDocumentSnapshot(doc);
416
- const verifyStep = plan.steps.find((s) => s.kind === 'verify');
417
- let saveReopen;
418
- const shouldSaveReopen = (verifyStep?.kind === 'verify' && (verifyStep.saveReopen || verificationNeedsSaveReopen(verifyStep.checks))) ||
419
- planTouchesRiskyDomain(plan);
420
- if (shouldSaveReopen) {
421
- saveReopen = await trySaveReopen(doc, verifyStep?.kind === 'verify' ? verifyStep.checks : []);
422
- }
423
- const verification = verifyStep?.kind === 'verify' ? computeDeltaChecks(preSnapshot, postSnapshot, verifyStep.checks, saveReopen) : [];
424
- const allVerified = verification.every((v) => v.passed);
425
- return {
426
- status: allVerified ? 'ok' : 'failed',
427
- intent: plan.intent,
428
- preSnapshot: { revision: preSnapshot.revision, counts: preSnapshot.counts },
429
- postSnapshot: { revision: postSnapshot.revision, counts: postSnapshot.counts },
430
- selectedTargets,
431
- executedOperations,
432
- verification,
433
- saveReopen,
434
- };
435
464
  }
436
465
  export async function agentVerify(doc, args) {
437
- const snapshot = await buildDocumentSnapshot(doc);
438
- let saveReopen;
439
- if (args.saveReopen || verificationNeedsSaveReopen(args.checks)) {
440
- saveReopen = await trySaveReopen(doc, args.checks);
466
+ const context = new ExecutionContext(doc, { evidence: args.evidence, checks: args.checks });
467
+ try {
468
+ const current = await context.start();
469
+ context.post = current;
470
+ const saveReopen = args.saveReopen || verificationNeedsSaveReopen(args.checks) ? await trySaveReopen(doc, args.checks) : undefined;
471
+ const verification = current.complete
472
+ ? computeCurrentChecks(current.complete, args.checks, saveReopen)
473
+ : await evaluateFactChecks(undefined, current, args.checks);
474
+ if (!current.complete)
475
+ await current.fence();
476
+ return context.receipt('verify', verification, { saveReopen });
477
+ }
478
+ catch (error) {
479
+ return context.failure('verify', error);
441
480
  }
442
- const verification = computeCurrentChecks(snapshot, args.checks, saveReopen);
443
- const allPassed = verification.every((v) => v.passed);
444
- return {
445
- status: allPassed ? 'ok' : 'failed',
446
- intent: 'verify',
447
- preSnapshot: { revision: snapshot.revision, counts: snapshot.counts },
448
- postSnapshot: { revision: snapshot.revision, counts: snapshot.counts },
449
- selectedTargets: [],
450
- executedOperations: [],
451
- verification,
452
- saveReopen,
453
- };
454
481
  }
455
482
  /**
456
483
  * Controlled escape hatch — dispatches a single generated operation by id.
@@ -478,24 +505,3 @@ export async function agentOperation(doc, args) {
478
505
  const method = resolveDocMethod(doc, args.operationId);
479
506
  return method(callArgs);
480
507
  }
481
- function emptyCounts() {
482
- return {
483
- blocks: 0,
484
- paragraphs: 0,
485
- headings: 0,
486
- tables: 0,
487
- lists: 0,
488
- images: 0,
489
- comments: 0,
490
- trackedChanges: 0,
491
- sections: 0,
492
- fields: 0,
493
- hyperlinks: 0,
494
- bookmarks: 0,
495
- contentControls: 0,
496
- permissionRanges: 0,
497
- styles: 0,
498
- headers: 0,
499
- footers: 0,
500
- };
501
- }
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ var documentEvidence = require('../runtime/document-evidence.cjs');
4
+
3
5
  const LEGACY_TRACKED_CHANGE_TYPES = {
4
6
  insertion: 'insert',
5
7
  deletion: 'delete',
@@ -57,13 +59,15 @@ function wrapV2PresetCompat(doc) {
57
59
  return value;
58
60
  },
59
61
  });
60
- return new Proxy(doc, {
62
+ const wrapped = new Proxy(doc, {
61
63
  get(target, property, receiver) {
62
64
  if (property === 'trackChanges')
63
65
  return trackChanges;
64
66
  return Reflect.get(target, property, receiver);
65
67
  },
66
68
  });
69
+ documentEvidence.forwardDocumentEvidence(doc, wrapped);
70
+ return wrapped;
67
71
  }
68
72
 
69
73
  exports.wrapV2PresetCompat = wrapV2PresetCompat;
@@ -1,3 +1,4 @@
1
+ import { forwardDocumentEvidence } from '../runtime/document-evidence.js';
1
2
  const LEGACY_TRACKED_CHANGE_TYPES = {
2
3
  insertion: 'insert',
3
4
  deletion: 'delete',
@@ -55,11 +56,13 @@ export function wrapV2PresetCompat(doc) {
55
56
  return value;
56
57
  },
57
58
  });
58
- return new Proxy(doc, {
59
+ const wrapped = new Proxy(doc, {
59
60
  get(target, property, receiver) {
60
61
  if (property === 'trackChanges')
61
62
  return trackChanges;
62
63
  return Reflect.get(target, property, receiver);
63
64
  },
64
65
  });
66
+ forwardDocumentEvidence(doc, wrapped);
67
+ return wrapped;
65
68
  }