@superbee/core 0.2.0-pre.4 → 0.2.0-pre.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1255 -0
- package/dist/artifact-contract.d.ts +90 -0
- package/dist/artifact-contract.d.ts.map +1 -0
- package/dist/artifact-contract.js +205 -0
- package/dist/artifact-contract.js.map +1 -0
- package/dist/backend.d.ts +22 -2
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +153 -38
- package/dist/backend.js.map +1 -1
- package/dist/blob-contract.d.ts +82 -0
- package/dist/blob-contract.d.ts.map +1 -0
- package/dist/blob-contract.js +134 -0
- package/dist/blob-contract.js.map +1 -0
- package/dist/blob-journal.d.ts +303 -0
- package/dist/blob-journal.d.ts.map +1 -0
- package/dist/blob-journal.js +541 -0
- package/dist/blob-journal.js.map +1 -0
- package/dist/body-replace-guards.d.ts +52 -0
- package/dist/body-replace-guards.d.ts.map +1 -0
- package/dist/body-replace-guards.js +176 -0
- package/dist/body-replace-guards.js.map +1 -0
- package/dist/builtin-recipes/content.d.ts +93 -0
- package/dist/builtin-recipes/content.d.ts.map +1 -0
- package/dist/builtin-recipes/content.js +288 -0
- package/dist/builtin-recipes/content.js.map +1 -0
- package/dist/builtin-recipes.d.ts +10 -0
- package/dist/builtin-recipes.d.ts.map +1 -0
- package/dist/builtin-recipes.js +97 -0
- package/dist/builtin-recipes.js.map +1 -0
- package/dist/bundle-health/index.d.ts +14 -0
- package/dist/bundle-health/index.d.ts.map +1 -0
- package/dist/bundle-health/index.js +12 -0
- package/dist/bundle-health/index.js.map +1 -0
- package/dist/bundle-health/kind-findings.d.ts +44 -0
- package/dist/bundle-health/kind-findings.d.ts.map +1 -0
- package/dist/bundle-health/kind-findings.js +68 -0
- package/dist/bundle-health/kind-findings.js.map +1 -0
- package/dist/bundle-health/legacy-page.d.ts +39 -0
- package/dist/bundle-health/legacy-page.d.ts.map +1 -0
- package/dist/bundle-health/legacy-page.js +50 -0
- package/dist/bundle-health/legacy-page.js.map +1 -0
- package/dist/bundle-health/report.d.ts +207 -0
- package/dist/bundle-health/report.d.ts.map +1 -0
- package/dist/bundle-health/report.js +454 -0
- package/dist/bundle-health/report.js.map +1 -0
- package/dist/bundle-ops.d.ts +3 -2
- package/dist/bundle-ops.d.ts.map +1 -1
- package/dist/bundle-ops.js +4 -4
- package/dist/bundle-ops.js.map +1 -1
- package/dist/bundle.d.ts +3 -4
- package/dist/bundle.d.ts.map +1 -1
- package/dist/bundle.js +5 -14
- package/dist/bundle.js.map +1 -1
- package/dist/code-point-order.d.ts +8 -0
- package/dist/code-point-order.d.ts.map +1 -0
- package/dist/code-point-order.js +20 -0
- package/dist/code-point-order.js.map +1 -0
- package/dist/code-unit-order.d.ts +15 -0
- package/dist/code-unit-order.d.ts.map +1 -0
- package/dist/code-unit-order.js +17 -0
- package/dist/code-unit-order.js.map +1 -0
- package/dist/connector-request.d.ts +112 -0
- package/dist/connector-request.d.ts.map +1 -0
- package/dist/connector-request.js +495 -0
- package/dist/connector-request.js.map +1 -0
- package/dist/connectors.d.ts +297 -0
- package/dist/connectors.d.ts.map +1 -0
- package/dist/connectors.js +1233 -0
- package/dist/connectors.js.map +1 -0
- package/dist/cross-bundle.d.ts +135 -0
- package/dist/cross-bundle.d.ts.map +1 -0
- package/dist/cross-bundle.js +191 -0
- package/dist/cross-bundle.js.map +1 -0
- package/dist/dependencies.d.ts +94 -0
- package/dist/dependencies.d.ts.map +1 -0
- package/dist/dependencies.js +180 -0
- package/dist/dependencies.js.map +1 -0
- package/dist/document-codec.d.ts +7 -2
- package/dist/document-codec.d.ts.map +1 -1
- package/dist/document-codec.js +8 -5
- package/dist/document-codec.js.map +1 -1
- package/dist/document-field-actions.d.ts +31 -1
- package/dist/document-field-actions.d.ts.map +1 -1
- package/dist/document-field-actions.js +137 -34
- package/dist/document-field-actions.js.map +1 -1
- package/dist/document-mutation.d.ts +37 -13
- package/dist/document-mutation.d.ts.map +1 -1
- package/dist/document-mutation.js +129 -60
- package/dist/document-mutation.js.map +1 -1
- package/dist/document-transfer.d.ts +208 -0
- package/dist/document-transfer.d.ts.map +1 -0
- package/dist/document-transfer.js +928 -0
- package/dist/document-transfer.js.map +1 -0
- package/dist/document-write-policy.d.ts.map +1 -1
- package/dist/document-write-policy.js +4 -2
- package/dist/document-write-policy.js.map +1 -1
- package/dist/domain-model.d.ts +73 -0
- package/dist/domain-model.d.ts.map +1 -0
- package/dist/domain-model.js +169 -0
- package/dist/domain-model.js.map +1 -0
- package/dist/engine.d.ts +21 -9
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +72 -14
- package/dist/engine.js.map +1 -1
- package/dist/file-journaled-backend-internal.d.ts +21 -0
- package/dist/file-journaled-backend-internal.d.ts.map +1 -0
- package/dist/file-journaled-backend-internal.js +12 -0
- package/dist/file-journaled-backend-internal.js.map +1 -0
- package/dist/file-journaled-backend.d.ts +160 -0
- package/dist/file-journaled-backend.d.ts.map +1 -0
- package/dist/file-journaled-backend.js +1345 -0
- package/dist/file-journaled-backend.js.map +1 -0
- package/dist/filesystem-host.d.ts +15 -0
- package/dist/filesystem-host.d.ts.map +1 -0
- package/dist/filesystem-host.js +20 -0
- package/dist/filesystem-host.js.map +1 -0
- package/dist/filesystem-identity.d.ts +97 -21
- package/dist/filesystem-identity.d.ts.map +1 -1
- package/dist/filesystem-identity.js +398 -119
- package/dist/filesystem-identity.js.map +1 -1
- package/dist/filesystem-lock.d.ts +16 -3
- package/dist/filesystem-lock.d.ts.map +1 -1
- package/dist/filesystem-lock.js +323 -77
- package/dist/filesystem-lock.js.map +1 -1
- package/dist/filesystem-push-role.d.ts +77 -0
- package/dist/filesystem-push-role.d.ts.map +1 -0
- package/dist/filesystem-push-role.js +165 -0
- package/dist/filesystem-push-role.js.map +1 -0
- package/dist/filesystem.d.ts +17 -0
- package/dist/filesystem.d.ts.map +1 -0
- package/dist/filesystem.js +18 -0
- package/dist/filesystem.js.map +1 -0
- package/dist/frontmatter.d.ts +8 -6
- package/dist/frontmatter.d.ts.map +1 -1
- package/dist/frontmatter.js +5 -4
- package/dist/frontmatter.js.map +1 -1
- package/dist/governed-body-write.d.ts +8 -1
- package/dist/governed-body-write.d.ts.map +1 -1
- package/dist/governed-body-write.js +9 -2
- package/dist/governed-body-write.js.map +1 -1
- package/dist/heads-digest.d.ts.map +1 -1
- package/dist/heads-digest.js +2 -1
- package/dist/heads-digest.js.map +1 -1
- package/dist/host-policy-snapshot.d.ts +9 -0
- package/dist/host-policy-snapshot.d.ts.map +1 -0
- package/dist/host-policy-snapshot.js +33 -0
- package/dist/host-policy-snapshot.js.map +1 -0
- package/dist/host-text.d.ts +31 -0
- package/dist/host-text.d.ts.map +1 -0
- package/dist/host-text.js +77 -0
- package/dist/host-text.js.map +1 -0
- package/dist/hosted-client/binding.d.ts +7 -0
- package/dist/hosted-client/binding.d.ts.map +1 -0
- package/dist/hosted-client/binding.js +10 -0
- package/dist/hosted-client/binding.js.map +1 -0
- package/dist/hosted-client/blob-delivery.d.ts +68 -0
- package/dist/hosted-client/blob-delivery.d.ts.map +1 -0
- package/dist/hosted-client/blob-delivery.js +170 -0
- package/dist/hosted-client/blob-delivery.js.map +1 -0
- package/dist/hosted-client/blob-write.d.ts +387 -0
- package/dist/hosted-client/blob-write.d.ts.map +1 -0
- package/dist/hosted-client/blob-write.js +770 -0
- package/dist/hosted-client/blob-write.js.map +1 -0
- package/dist/hosted-client/bundle-listing.d.ts +46 -0
- package/dist/hosted-client/bundle-listing.d.ts.map +1 -0
- package/dist/hosted-client/bundle-listing.js +36 -0
- package/dist/hosted-client/bundle-listing.js.map +1 -0
- package/dist/hosted-client/client.d.ts +219 -0
- package/dist/hosted-client/client.d.ts.map +1 -0
- package/dist/hosted-client/client.js +309 -0
- package/dist/hosted-client/client.js.map +1 -0
- package/dist/hosted-client/directory.d.ts +46 -0
- package/dist/hosted-client/directory.d.ts.map +1 -0
- package/dist/hosted-client/directory.js +74 -0
- package/dist/hosted-client/directory.js.map +1 -0
- package/dist/hosted-client/errors.d.ts +26 -0
- package/dist/hosted-client/errors.d.ts.map +1 -0
- package/dist/hosted-client/errors.js +35 -0
- package/dist/hosted-client/errors.js.map +1 -0
- package/dist/hosted-client/inbox.d.ts +320 -0
- package/dist/hosted-client/inbox.d.ts.map +1 -0
- package/dist/hosted-client/inbox.js +379 -0
- package/dist/hosted-client/inbox.js.map +1 -0
- package/dist/hosted-client/index.d.ts +30 -0
- package/dist/hosted-client/index.d.ts.map +1 -0
- package/dist/hosted-client/index.js +32 -0
- package/dist/hosted-client/index.js.map +1 -0
- package/dist/hosted-client/journal.d.ts +103 -0
- package/dist/hosted-client/journal.d.ts.map +1 -0
- package/dist/hosted-client/journal.js +464 -0
- package/dist/hosted-client/journal.js.map +1 -0
- package/dist/hosted-client/node.d.ts +74 -0
- package/dist/hosted-client/node.d.ts.map +1 -0
- package/dist/hosted-client/node.js +219 -0
- package/dist/hosted-client/node.js.map +1 -0
- package/dist/hosted-client/reference.d.ts +12 -0
- package/dist/hosted-client/reference.d.ts.map +1 -0
- package/dist/hosted-client/reference.js +29 -0
- package/dist/hosted-client/reference.js.map +1 -0
- package/dist/hosted-client/write.d.ts +217 -0
- package/dist/hosted-client/write.d.ts.map +1 -0
- package/dist/hosted-client/write.js +418 -0
- package/dist/hosted-client/write.js.map +1 -0
- package/dist/hosted-transport/answer-rows.d.ts +243 -0
- package/dist/hosted-transport/answer-rows.d.ts.map +1 -0
- package/dist/hosted-transport/answer-rows.js +457 -0
- package/dist/hosted-transport/answer-rows.js.map +1 -0
- package/dist/hosted-transport/blob-reads.d.ts +35 -0
- package/dist/hosted-transport/blob-reads.d.ts.map +1 -0
- package/dist/hosted-transport/blob-reads.js +179 -0
- package/dist/hosted-transport/blob-reads.js.map +1 -0
- package/dist/hosted-transport/blob-write.d.ts +34 -0
- package/dist/hosted-transport/blob-write.d.ts.map +1 -0
- package/dist/hosted-transport/blob-write.js +115 -0
- package/dist/hosted-transport/blob-write.js.map +1 -0
- package/dist/hosted-transport/carrier.d.ts +151 -0
- package/dist/hosted-transport/carrier.d.ts.map +1 -0
- package/dist/hosted-transport/carrier.js +260 -0
- package/dist/hosted-transport/carrier.js.map +1 -0
- package/dist/hosted-transport/document-backend.d.ts +74 -0
- package/dist/hosted-transport/document-backend.d.ts.map +1 -0
- package/dist/hosted-transport/document-backend.js +160 -0
- package/dist/hosted-transport/document-backend.js.map +1 -0
- package/dist/hosted-transport/history-pages.d.ts +55 -0
- package/dist/hosted-transport/history-pages.d.ts.map +1 -0
- package/dist/hosted-transport/history-pages.js +69 -0
- package/dist/hosted-transport/history-pages.js.map +1 -0
- package/dist/hosted-transport/index.d.ts +19 -0
- package/dist/hosted-transport/index.d.ts.map +1 -0
- package/dist/hosted-transport/index.js +19 -0
- package/dist/hosted-transport/index.js.map +1 -0
- package/dist/hosted-transport/operations.d.ts +85 -0
- package/dist/hosted-transport/operations.d.ts.map +1 -0
- package/dist/hosted-transport/operations.js +128 -0
- package/dist/hosted-transport/operations.js.map +1 -0
- package/dist/hosted-transport/paged-reads.d.ts +87 -0
- package/dist/hosted-transport/paged-reads.d.ts.map +1 -0
- package/dist/hosted-transport/paged-reads.js +238 -0
- package/dist/hosted-transport/paged-reads.js.map +1 -0
- package/dist/hosted-transport/read-adapter.d.ts +256 -0
- package/dist/hosted-transport/read-adapter.d.ts.map +1 -0
- package/dist/hosted-transport/read-adapter.js +568 -0
- package/dist/hosted-transport/read-adapter.js.map +1 -0
- package/dist/hosted-transport/root-write.d.ts +114 -0
- package/dist/hosted-transport/root-write.d.ts.map +1 -0
- package/dist/hosted-transport/root-write.js +171 -0
- package/dist/hosted-transport/root-write.js.map +1 -0
- package/dist/hosted-transport/whole-document-transport.d.ts +204 -0
- package/dist/hosted-transport/whole-document-transport.d.ts.map +1 -0
- package/dist/hosted-transport/whole-document-transport.js +447 -0
- package/dist/hosted-transport/whole-document-transport.js.map +1 -0
- package/dist/identity-fold.d.ts +18 -0
- package/dist/identity-fold.d.ts.map +1 -0
- package/dist/identity-fold.js +22 -0
- package/dist/identity-fold.js.map +1 -0
- package/dist/index.d.ts +41 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -9
- package/dist/index.js.map +1 -1
- package/dist/indexeddb-backend.d.ts +25 -33
- package/dist/indexeddb-backend.d.ts.map +1 -1
- package/dist/indexeddb-backend.js +264 -8
- package/dist/indexeddb-backend.js.map +1 -1
- package/dist/inventory-guard.d.ts +58 -0
- package/dist/inventory-guard.d.ts.map +1 -0
- package/dist/inventory-guard.js +169 -0
- package/dist/inventory-guard.js.map +1 -0
- package/dist/journaled-backend.d.ts +149 -9
- package/dist/journaled-backend.d.ts.map +1 -1
- package/dist/journaled-backend.js +64 -58
- package/dist/journaled-backend.js.map +1 -1
- package/dist/kind-authoring.d.ts +170 -0
- package/dist/kind-authoring.d.ts.map +1 -0
- package/dist/kind-authoring.js +452 -0
- package/dist/kind-authoring.js.map +1 -0
- package/dist/kind-changes.d.ts +45 -0
- package/dist/kind-changes.d.ts.map +1 -0
- package/dist/kind-changes.js +96 -0
- package/dist/kind-changes.js.map +1 -0
- package/dist/kind-conformance-error.d.ts +15 -0
- package/dist/kind-conformance-error.d.ts.map +1 -0
- package/dist/kind-conformance-error.js +20 -0
- package/dist/kind-conformance-error.js.map +1 -0
- package/dist/kind-coverage.d.ts +99 -0
- package/dist/kind-coverage.d.ts.map +1 -0
- package/dist/kind-coverage.js +186 -0
- package/dist/kind-coverage.js.map +1 -0
- package/dist/kind-deprecation.d.ts +32 -0
- package/dist/kind-deprecation.d.ts.map +1 -0
- package/dist/kind-deprecation.js +141 -0
- package/dist/kind-deprecation.js.map +1 -0
- package/dist/kind-field-mutation.d.ts +4 -0
- package/dist/kind-field-mutation.d.ts.map +1 -1
- package/dist/kind-field-mutation.js +28 -3
- package/dist/kind-field-mutation.js.map +1 -1
- package/dist/kinds-load.js +4 -4
- package/dist/kinds-load.js.map +1 -1
- package/dist/kinds.d.ts +111 -2
- package/dist/kinds.d.ts.map +1 -1
- package/dist/kinds.js +386 -20
- package/dist/kinds.js.map +1 -1
- package/dist/link-emit.d.ts +29 -0
- package/dist/link-emit.d.ts.map +1 -0
- package/dist/link-emit.js +39 -0
- package/dist/link-emit.js.map +1 -0
- package/dist/link-types.d.ts +27 -0
- package/dist/link-types.d.ts.map +1 -0
- package/dist/link-types.js +60 -0
- package/dist/link-types.js.map +1 -0
- package/dist/links.d.ts +52 -3
- package/dist/links.d.ts.map +1 -1
- package/dist/links.js +259 -18
- package/dist/links.js.map +1 -1
- package/dist/managed-document-metadata.d.ts +2 -0
- package/dist/managed-document-metadata.d.ts.map +1 -0
- package/dist/managed-document-metadata.js +9 -0
- package/dist/managed-document-metadata.js.map +1 -0
- package/dist/memory-backend.d.ts +3 -0
- package/dist/memory-backend.d.ts.map +1 -1
- package/dist/memory-backend.js +44 -2
- package/dist/memory-backend.js.map +1 -1
- package/dist/okf-actor.d.ts +1 -0
- package/dist/okf-actor.d.ts.map +1 -1
- package/dist/okf-actor.js +1 -0
- package/dist/okf-actor.js.map +1 -1
- package/dist/okf-authoring.d.ts +4 -0
- package/dist/okf-authoring.d.ts.map +1 -0
- package/dist/okf-authoring.js +12 -0
- package/dist/okf-authoring.js.map +1 -0
- package/dist/okf-standard-fields.d.ts +3 -0
- package/dist/okf-standard-fields.d.ts.map +1 -1
- package/dist/okf-standard-fields.js +3 -0
- package/dist/okf-standard-fields.js.map +1 -1
- package/dist/path-fold.d.ts +39 -0
- package/dist/path-fold.d.ts.map +1 -0
- package/dist/path-fold.js +41 -0
- package/dist/path-fold.js.map +1 -0
- package/dist/paths.d.ts +18 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +32 -0
- package/dist/paths.js.map +1 -1
- package/dist/platform.d.ts +13 -2
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +16 -0
- package/dist/platform.js.map +1 -1
- package/dist/portable-export.d.ts +119 -0
- package/dist/portable-export.d.ts.map +1 -0
- package/dist/portable-export.js +501 -0
- package/dist/portable-export.js.map +1 -0
- package/dist/publication-filesystem.d.ts +1 -1
- package/dist/publication-filesystem.d.ts.map +1 -1
- package/dist/publication-filesystem.js +1 -1
- package/dist/publication-filesystem.js.map +1 -1
- package/dist/qualified-links.d.ts +67 -0
- package/dist/qualified-links.d.ts.map +1 -0
- package/dist/qualified-links.js +117 -0
- package/dist/qualified-links.js.map +1 -0
- package/dist/query-filter.d.ts +16 -2
- package/dist/query-filter.d.ts.map +1 -1
- package/dist/query-filter.js +40 -1
- package/dist/query-filter.js.map +1 -1
- package/dist/query-order.d.ts +22 -0
- package/dist/query-order.d.ts.map +1 -0
- package/dist/query-order.js +43 -0
- package/dist/query-order.js.map +1 -0
- package/dist/recipe-lifecycle/errors.d.ts +96 -0
- package/dist/recipe-lifecycle/errors.d.ts.map +1 -0
- package/dist/recipe-lifecycle/errors.js +122 -0
- package/dist/recipe-lifecycle/errors.js.map +1 -0
- package/dist/recipe-lifecycle/evolution.d.ts +124 -0
- package/dist/recipe-lifecycle/evolution.d.ts.map +1 -0
- package/dist/recipe-lifecycle/evolution.js +993 -0
- package/dist/recipe-lifecycle/evolution.js.map +1 -0
- package/dist/recipe-lifecycle/index.d.ts +34 -0
- package/dist/recipe-lifecycle/index.d.ts.map +1 -0
- package/dist/recipe-lifecycle/index.js +28 -0
- package/dist/recipe-lifecycle/index.js.map +1 -0
- package/dist/recipe-lifecycle/install.d.ts +155 -0
- package/dist/recipe-lifecycle/install.d.ts.map +1 -0
- package/dist/recipe-lifecycle/install.js +604 -0
- package/dist/recipe-lifecycle/install.js.map +1 -0
- package/dist/recipe-lifecycle/legacy-aliases.d.ts +16 -0
- package/dist/recipe-lifecycle/legacy-aliases.d.ts.map +1 -0
- package/dist/recipe-lifecycle/legacy-aliases.js +51 -0
- package/dist/recipe-lifecycle/legacy-aliases.js.map +1 -0
- package/dist/recipe-lifecycle/retirement-apply.d.ts +59 -0
- package/dist/recipe-lifecycle/retirement-apply.d.ts.map +1 -0
- package/dist/recipe-lifecycle/retirement-apply.js +165 -0
- package/dist/recipe-lifecycle/retirement-apply.js.map +1 -0
- package/dist/recipe-lifecycle/retirement.d.ts +89 -0
- package/dist/recipe-lifecycle/retirement.d.ts.map +1 -0
- package/dist/recipe-lifecycle/retirement.js +347 -0
- package/dist/recipe-lifecycle/retirement.js.map +1 -0
- package/dist/recipe-lifecycle/stamp.d.ts +151 -0
- package/dist/recipe-lifecycle/stamp.d.ts.map +1 -0
- package/dist/recipe-lifecycle/stamp.js +364 -0
- package/dist/recipe-lifecycle/stamp.js.map +1 -0
- package/dist/recipe-set-preflight.d.ts +139 -0
- package/dist/recipe-set-preflight.d.ts.map +1 -0
- package/dist/recipe-set-preflight.js +536 -0
- package/dist/recipe-set-preflight.js.map +1 -0
- package/dist/recipes.d.ts +29 -1
- package/dist/recipes.d.ts.map +1 -1
- package/dist/recipes.js +137 -1
- package/dist/recipes.js.map +1 -1
- package/dist/record-overlay/apply.d.ts +43 -0
- package/dist/record-overlay/apply.d.ts.map +1 -0
- package/dist/record-overlay/apply.js +527 -0
- package/dist/record-overlay/apply.js.map +1 -0
- package/dist/record-overlay/canonical.d.ts +22 -0
- package/dist/record-overlay/canonical.d.ts.map +1 -0
- package/dist/record-overlay/canonical.js +47 -0
- package/dist/record-overlay/canonical.js.map +1 -0
- package/dist/record-overlay/digest.d.ts +4 -0
- package/dist/record-overlay/digest.d.ts.map +1 -0
- package/dist/record-overlay/digest.js +11 -0
- package/dist/record-overlay/digest.js.map +1 -0
- package/dist/record-overlay/errors.d.ts +44 -0
- package/dist/record-overlay/errors.d.ts.map +1 -0
- package/dist/record-overlay/errors.js +55 -0
- package/dist/record-overlay/errors.js.map +1 -0
- package/dist/record-overlay/findings.d.ts +5 -0
- package/dist/record-overlay/findings.d.ts.map +1 -0
- package/dist/record-overlay/findings.js +83 -0
- package/dist/record-overlay/findings.js.map +1 -0
- package/dist/record-overlay/index.d.ts +23 -0
- package/dist/record-overlay/index.d.ts.map +1 -0
- package/dist/record-overlay/index.js +21 -0
- package/dist/record-overlay/index.js.map +1 -0
- package/dist/record-overlay/plan.d.ts +12 -0
- package/dist/record-overlay/plan.d.ts.map +1 -0
- package/dist/record-overlay/plan.js +285 -0
- package/dist/record-overlay/plan.js.map +1 -0
- package/dist/record-overlay/policy.d.ts +74 -0
- package/dist/record-overlay/policy.d.ts.map +1 -0
- package/dist/record-overlay/policy.js +235 -0
- package/dist/record-overlay/policy.js.map +1 -0
- package/dist/record-overlay/redact.d.ts +3 -0
- package/dist/record-overlay/redact.d.ts.map +1 -0
- package/dist/record-overlay/redact.js +27 -0
- package/dist/record-overlay/redact.js.map +1 -0
- package/dist/record-overlay/signal-kind.d.ts +30 -0
- package/dist/record-overlay/signal-kind.d.ts.map +1 -0
- package/dist/record-overlay/signal-kind.js +38 -0
- package/dist/record-overlay/signal-kind.js.map +1 -0
- package/dist/record-overlay/signal-text.d.ts +45 -0
- package/dist/record-overlay/signal-text.d.ts.map +1 -0
- package/dist/record-overlay/signal-text.js +116 -0
- package/dist/record-overlay/signal-text.js.map +1 -0
- package/dist/record-overlay/source-index.d.ts +19 -0
- package/dist/record-overlay/source-index.d.ts.map +1 -0
- package/dist/record-overlay/source-index.js +102 -0
- package/dist/record-overlay/source-index.js.map +1 -0
- package/dist/record-overlay/types.d.ts +291 -0
- package/dist/record-overlay/types.d.ts.map +1 -0
- package/dist/record-overlay/types.js +2 -0
- package/dist/record-overlay/types.js.map +1 -0
- package/dist/remote-backend.d.ts +58 -13
- package/dist/remote-backend.d.ts.map +1 -1
- package/dist/remote-backend.js +156 -36
- package/dist/remote-backend.js.map +1 -1
- package/dist/remote-document-codec.d.ts +2 -1
- package/dist/remote-document-codec.d.ts.map +1 -1
- package/dist/remote-document-codec.js +3 -2
- package/dist/remote-document-codec.js.map +1 -1
- package/dist/remote-error.d.ts +17 -1
- package/dist/remote-error.d.ts.map +1 -1
- package/dist/remote-error.js +32 -2
- package/dist/remote-error.js.map +1 -1
- package/dist/remote-operations.d.ts +0 -40
- package/dist/remote-operations.d.ts.map +1 -1
- package/dist/remote-operations.js +26 -6
- package/dist/remote-operations.js.map +1 -1
- package/dist/remote-parsers.d.ts +1 -1
- package/dist/remote-parsers.js +1 -1
- package/dist/search/backend-source.d.ts +6 -0
- package/dist/search/backend-source.d.ts.map +1 -0
- package/dist/search/backend-source.js +33 -0
- package/dist/search/backend-source.js.map +1 -0
- package/dist/search/contract.d.ts +151 -0
- package/dist/search/contract.d.ts.map +1 -0
- package/dist/search/contract.js +78 -0
- package/dist/search/contract.js.map +1 -0
- package/dist/search/errors.d.ts +30 -0
- package/dist/search/errors.d.ts.map +1 -0
- package/dist/search/errors.js +50 -0
- package/dist/search/errors.js.map +1 -0
- package/dist/search/identity.d.ts +12 -0
- package/dist/search/identity.d.ts.map +1 -0
- package/dist/search/identity.js +90 -0
- package/dist/search/identity.js.map +1 -0
- package/dist/search/index.d.ts +16 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +15 -0
- package/dist/search/index.js.map +1 -0
- package/dist/search/memory-source.d.ts +36 -0
- package/dist/search/memory-source.d.ts.map +1 -0
- package/dist/search/memory-source.js +235 -0
- package/dist/search/memory-source.js.map +1 -0
- package/dist/search/rank.d.ts +34 -0
- package/dist/search/rank.d.ts.map +1 -0
- package/dist/search/rank.js +129 -0
- package/dist/search/rank.js.map +1 -0
- package/dist/search/row-text.d.ts +38 -0
- package/dist/search/row-text.d.ts.map +1 -0
- package/dist/search/row-text.js +88 -0
- package/dist/search/row-text.js.map +1 -0
- package/dist/search/search.d.ts +28 -0
- package/dist/search/search.d.ts.map +1 -0
- package/dist/search/search.js +257 -0
- package/dist/search/search.js.map +1 -0
- package/dist/search/source.d.ts +67 -0
- package/dist/search/source.d.ts.map +1 -0
- package/dist/search/source.js +2 -0
- package/dist/search/source.js.map +1 -0
- package/dist/search/text.d.ts +47 -0
- package/dist/search/text.d.ts.map +1 -0
- package/dist/search/text.js +150 -0
- package/dist/search/text.js.map +1 -0
- package/dist/site-capabilities.d.ts +345 -0
- package/dist/site-capabilities.d.ts.map +1 -0
- package/dist/site-capabilities.js +724 -0
- package/dist/site-capabilities.js.map +1 -0
- package/dist/source-engine/driver.d.ts +42 -0
- package/dist/source-engine/driver.d.ts.map +1 -0
- package/dist/source-engine/driver.js +529 -0
- package/dist/source-engine/driver.js.map +1 -0
- package/dist/source-engine/evidence.d.ts +39 -0
- package/dist/source-engine/evidence.d.ts.map +1 -0
- package/dist/source-engine/evidence.js +54 -0
- package/dist/source-engine/evidence.js.map +1 -0
- package/dist/source-engine/guard.d.ts +87 -0
- package/dist/source-engine/guard.d.ts.map +1 -0
- package/dist/source-engine/guard.js +427 -0
- package/dist/source-engine/guard.js.map +1 -0
- package/dist/source-engine/index.d.ts +35 -0
- package/dist/source-engine/index.d.ts.map +1 -0
- package/dist/source-engine/index.js +27 -0
- package/dist/source-engine/index.js.map +1 -0
- package/dist/source-engine/machine-state.d.ts +87 -0
- package/dist/source-engine/machine-state.d.ts.map +1 -0
- package/dist/source-engine/machine-state.js +94 -0
- package/dist/source-engine/machine-state.js.map +1 -0
- package/dist/source-engine/observe.d.ts +10 -0
- package/dist/source-engine/observe.d.ts.map +1 -0
- package/dist/source-engine/observe.js +276 -0
- package/dist/source-engine/observe.js.map +1 -0
- package/dist/source-engine/overlay.d.ts +50 -0
- package/dist/source-engine/overlay.d.ts.map +1 -0
- package/dist/source-engine/overlay.js +114 -0
- package/dist/source-engine/overlay.js.map +1 -0
- package/dist/source-engine/robots.d.ts +53 -0
- package/dist/source-engine/robots.d.ts.map +1 -0
- package/dist/source-engine/robots.js +158 -0
- package/dist/source-engine/robots.js.map +1 -0
- package/dist/source-engine/snapshot.d.ts +104 -0
- package/dist/source-engine/snapshot.d.ts.map +1 -0
- package/dist/source-engine/snapshot.js +240 -0
- package/dist/source-engine/snapshot.js.map +1 -0
- package/dist/source-engine/target-consumer.d.ts +12 -0
- package/dist/source-engine/target-consumer.d.ts.map +1 -0
- package/dist/source-engine/target-consumer.js +15 -0
- package/dist/source-engine/target-consumer.js.map +1 -0
- package/dist/source-engine/target-descriptor.d.ts +3 -0
- package/dist/source-engine/target-descriptor.d.ts.map +1 -0
- package/dist/source-engine/target-descriptor.js +26 -0
- package/dist/source-engine/target-descriptor.js.map +1 -0
- package/dist/source-engine/target.d.ts +82 -0
- package/dist/source-engine/target.d.ts.map +1 -0
- package/dist/source-engine/target.js +213 -0
- package/dist/source-engine/target.js.map +1 -0
- package/dist/source-engine/types.d.ts +296 -0
- package/dist/source-engine/types.d.ts.map +1 -0
- package/dist/source-engine/types.js +2 -0
- package/dist/source-engine/types.js.map +1 -0
- package/dist/source-harness/conformance.d.ts +16 -0
- package/dist/source-harness/conformance.d.ts.map +1 -0
- package/dist/source-harness/conformance.js +266 -0
- package/dist/source-harness/conformance.js.map +1 -0
- package/dist/source-harness/fixture.d.ts +97 -0
- package/dist/source-harness/fixture.d.ts.map +1 -0
- package/dist/source-harness/fixture.js +276 -0
- package/dist/source-harness/fixture.js.map +1 -0
- package/dist/source-harness/index.d.ts +22 -0
- package/dist/source-harness/index.d.ts.map +1 -0
- package/dist/source-harness/index.js +19 -0
- package/dist/source-harness/index.js.map +1 -0
- package/dist/source-harness/peer.d.ts +74 -0
- package/dist/source-harness/peer.d.ts.map +1 -0
- package/dist/source-harness/peer.js +106 -0
- package/dist/source-harness/peer.js.map +1 -0
- package/dist/source-profiles/index.d.ts +31 -0
- package/dist/source-profiles/index.d.ts.map +1 -0
- package/dist/source-profiles/index.js +206 -0
- package/dist/source-profiles/index.js.map +1 -0
- package/dist/source-profiles/link-header.d.ts +6 -0
- package/dist/source-profiles/link-header.d.ts.map +1 -0
- package/dist/source-profiles/link-header.js +131 -0
- package/dist/source-profiles/link-header.js.map +1 -0
- package/dist/storage.d.ts +9 -2
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +6 -1
- package/dist/storage.js.map +1 -1
- package/dist/string-identity-agreement.d.ts +35 -0
- package/dist/string-identity-agreement.d.ts.map +1 -0
- package/dist/string-identity-agreement.js +72 -0
- package/dist/string-identity-agreement.js.map +1 -0
- package/dist/types.d.ts +41 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -10
- package/dist/types.js.map +1 -1
- package/dist/uncertain-write.d.ts +157 -17
- package/dist/uncertain-write.d.ts.map +1 -1
- package/dist/uncertain-write.js +86 -30
- package/dist/uncertain-write.js.map +1 -1
- package/dist/versioning.d.ts +9 -4
- package/dist/versioning.d.ts.map +1 -1
- package/dist/versioning.js +9 -4
- package/dist/versioning.js.map +1 -1
- package/dist/view-launch-contract.d.ts +14 -0
- package/dist/view-launch-contract.d.ts.map +1 -0
- package/dist/view-launch-contract.js +23 -0
- package/dist/view-launch-contract.js.map +1 -0
- package/dist/write-identity.d.ts +5 -0
- package/dist/write-identity.d.ts.map +1 -0
- package/dist/write-identity.js +9 -0
- package/dist/write-identity.js.map +1 -0
- package/package.json +140 -3
|
@@ -0,0 +1,724 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Site capabilities: one contract for a read-only tool a site or a recipe pack provides, so the
|
|
3
|
+
* same value serves an assistant's provider adapter, Portal's WebMCP registration and a remote
|
|
4
|
+
* MCP server. This module owns the contract's rules: the descriptor shape, names and the reserved
|
|
5
|
+
* `superbee_` prefix, conflict refusal, deterministic order, per-surface filtering, limits, the
|
|
6
|
+
* composition identity digest, fixture checks, and host-minted `site-record` citations.
|
|
7
|
+
*
|
|
8
|
+
* Pure and platform-neutral: `TextEncoder`, `URL` and the package's own synchronous SHA-256, with
|
|
9
|
+
* no Node built-in, so it runs unchanged in a Cloudflare Worker, a browser and Node. Nothing here
|
|
10
|
+
* executes a capability except `checkSiteCapability` and the conformance cases, which run the
|
|
11
|
+
* caller's own fixtures.
|
|
12
|
+
*/
|
|
13
|
+
import { sha256HexOfUtf8 } from "./sha256.js";
|
|
14
|
+
export const SITE_CAPABILITY_NAME_PATTERN = /^[a-z][a-z0-9_]{2,63}$/;
|
|
15
|
+
/** Package-owned tools only (Portal's `PORTAL_WEBMCP_RESERVED_PREFIX_V0`). */
|
|
16
|
+
export const RESERVED_TOOL_PREFIX = "superbee_";
|
|
17
|
+
export const DEFAULT_MAXIMUM_OUTPUT_BYTES = 65536;
|
|
18
|
+
/** The closed error-code set a site capability's `{ ok: false }` results use by default. */
|
|
19
|
+
export const SITE_CAPABILITY_ERROR_CODES = ["INVALID_INPUT", "NOT_FOUND", "AMBIGUOUS", "NOT_AVAILABLE"];
|
|
20
|
+
const NAMESPACE_PATTERN = /^[a-z][a-z0-9]{1,15}$/;
|
|
21
|
+
const MAX_IDENTIFIER_BYTES = 256;
|
|
22
|
+
const encoder = new TextEncoder();
|
|
23
|
+
export const utf8Bytes = (text) => encoder.encode(text).byteLength;
|
|
24
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25
|
+
/**
|
|
26
|
+
* JSON with object keys sorted at every depth, so equal values serialize to equal bytes. Throws on
|
|
27
|
+
* anything JSON cannot carry exactly: functions, symbols, `undefined` array items, non-finite
|
|
28
|
+
* numbers, bigints and cycles. Object properties whose value is `undefined` are omitted, as
|
|
29
|
+
* `JSON.stringify` does.
|
|
30
|
+
*/
|
|
31
|
+
export function canonicalJson(value) {
|
|
32
|
+
const seen = new Set();
|
|
33
|
+
const walk = (item) => {
|
|
34
|
+
if (item === null || typeof item === "boolean" || typeof item === "string")
|
|
35
|
+
return JSON.stringify(item);
|
|
36
|
+
if (typeof item === "number") {
|
|
37
|
+
if (!Number.isFinite(item))
|
|
38
|
+
throw new TypeError("canonicalJson: non-finite number");
|
|
39
|
+
return JSON.stringify(item);
|
|
40
|
+
}
|
|
41
|
+
if (typeof item !== "object")
|
|
42
|
+
throw new TypeError(`canonicalJson: ${typeof item}`);
|
|
43
|
+
if (seen.has(item))
|
|
44
|
+
throw new TypeError("canonicalJson: cycle");
|
|
45
|
+
seen.add(item);
|
|
46
|
+
try {
|
|
47
|
+
if (Array.isArray(item))
|
|
48
|
+
return `[${item
|
|
49
|
+
.map((entry) => {
|
|
50
|
+
if (entry === undefined)
|
|
51
|
+
throw new TypeError("canonicalJson: undefined array item");
|
|
52
|
+
return walk(entry);
|
|
53
|
+
})
|
|
54
|
+
.join(",")}]`;
|
|
55
|
+
const record = item;
|
|
56
|
+
return `{${Object.keys(record)
|
|
57
|
+
.sort()
|
|
58
|
+
.filter((key) => record[key] !== undefined)
|
|
59
|
+
.map((key) => `${JSON.stringify(key)}:${walk(record[key])}`)
|
|
60
|
+
.join(",")}}`;
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
seen.delete(item);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
return walk(value);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Whether `path` is a plain same-origin page path: absolute, not protocol-relative, no query,
|
|
70
|
+
* fragment, backslash, control character, encoded slash or dot segment, and unchanged by URL
|
|
71
|
+
* normalization. The same checks as agent-surface's `portalAssistantRoute`. Syntax only: a path
|
|
72
|
+
* such as `/a/%252e%252e/b` or `/a/..;/b` passes, so the host's route-membership check
|
|
73
|
+
* (`admittedPath` in `siteRecordSource`) must still refuse any path it does not serve.
|
|
74
|
+
*/
|
|
75
|
+
export function isSafeSitePath(path) {
|
|
76
|
+
if (typeof path !== "string" ||
|
|
77
|
+
!path.startsWith("/") ||
|
|
78
|
+
path.startsWith("//") ||
|
|
79
|
+
path.length > 2048 ||
|
|
80
|
+
/[\\?#\u0000-\u001f\u007f]/u.test(path) ||
|
|
81
|
+
/%2f|%5c/iu.test(path))
|
|
82
|
+
return false;
|
|
83
|
+
try {
|
|
84
|
+
if (path.split("/").some((part) => {
|
|
85
|
+
const decoded = decodeURIComponent(part);
|
|
86
|
+
return decoded === "." || decoded === ".." || /[\u0000-\u001f\u007f]/u.test(decoded);
|
|
87
|
+
}))
|
|
88
|
+
return false;
|
|
89
|
+
const url = new URL(path, "https://site.invalid");
|
|
90
|
+
return url.origin === "https://site.invalid" && url.pathname === path;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** An ISO 8601 calendar date, or a date and time with a `Z` or numeric offset (a date-time without an offset is refused, since it names no instant). */
|
|
97
|
+
const ISO_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:Z|[+-]\d{2}:\d{2}))?$/;
|
|
98
|
+
const isDate = (value) => typeof value === "string" && ISO_DATE_PATTERN.test(value) && !Number.isNaN(Date.parse(value));
|
|
99
|
+
/**
|
|
100
|
+
* The host-minted `site-record` source for one citation a capability returned, or `undefined`
|
|
101
|
+
* when the citation is malformed, its path is not a safe same-origin path, or the path is not one
|
|
102
|
+
* the host admits. `sourceId` is the host's own ID for this read; a model-produced ID or URL never
|
|
103
|
+
* mints a source. `admittedPath` is the host's route-membership check (the publication's
|
|
104
|
+
* digest-bound route list, or the build's route list): syntax alone does not admit a path.
|
|
105
|
+
*/
|
|
106
|
+
export function siteRecordSource(options) {
|
|
107
|
+
const { sourceId, siteId, publication, citation, admittedPath } = options;
|
|
108
|
+
if (typeof admittedPath !== "function" || !isRecord(publication) || !isRecord(citation))
|
|
109
|
+
return undefined;
|
|
110
|
+
// Each field is read once; only these copies are checked and returned, so a getter cannot pass
|
|
111
|
+
// the checks with one value and mint a source with another.
|
|
112
|
+
const artifactDigest = publication.artifactDigest, snapshotDigest = publication.snapshotDigest;
|
|
113
|
+
const recordId = citation.recordId, path = citation.path, asOf = citation.asOf;
|
|
114
|
+
const bounded = (value) => typeof value === "string" && value.length > 0 && utf8Bytes(value) <= MAX_IDENTIFIER_BYTES;
|
|
115
|
+
if (!bounded(sourceId) || !bounded(siteId))
|
|
116
|
+
return undefined;
|
|
117
|
+
if (!bounded(artifactDigest) || !bounded(snapshotDigest))
|
|
118
|
+
return undefined;
|
|
119
|
+
if (!bounded(recordId) || typeof path !== "string" || !isSafeSitePath(path) || !isDate(asOf))
|
|
120
|
+
return undefined;
|
|
121
|
+
if (admittedPath(path) !== true)
|
|
122
|
+
return undefined;
|
|
123
|
+
return Object.freeze({
|
|
124
|
+
sourceId,
|
|
125
|
+
kind: "site-record",
|
|
126
|
+
siteId,
|
|
127
|
+
recordId,
|
|
128
|
+
path,
|
|
129
|
+
asOf,
|
|
130
|
+
artifactDigest,
|
|
131
|
+
snapshotDigest,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Portal's `additionalTools` shape as a `SiteCapability`. The Portal tool keeps receiving exactly
|
|
136
|
+
* `{ signal }`; `extras` adds what Portal's shape has no field for. The reverse needs no adapter:
|
|
137
|
+
* a `SiteCapability` is already a valid `PortalAdditionalTool`.
|
|
138
|
+
*/
|
|
139
|
+
export function siteCapabilityFromPortalTool(tool, extras = {}) {
|
|
140
|
+
return {
|
|
141
|
+
name: tool.name,
|
|
142
|
+
implementationId: tool.implementationId,
|
|
143
|
+
description: tool.description,
|
|
144
|
+
inputSchema: tool.inputSchema,
|
|
145
|
+
...(tool.annotations === undefined ? {} : { annotations: tool.annotations }),
|
|
146
|
+
...(extras.title === undefined ? {} : { title: extras.title }),
|
|
147
|
+
...(extras.outputSchema === undefined ? {} : { outputSchema: extras.outputSchema }),
|
|
148
|
+
...(extras.maximumOutputBytes === undefined ? {} : { maximumOutputBytes: extras.maximumOutputBytes }),
|
|
149
|
+
...(extras.mintSources === undefined ? {} : { mintSources: extras.mintSources }),
|
|
150
|
+
execute: (input, invocation) => tool.execute(input, { signal: invocation.signal }),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/** Portal's `additionalTools` entry for a capability: exactly the fields Portal binds, with the
|
|
154
|
+
* capability receiving `{ signal }`. */
|
|
155
|
+
export function toPortalAdditionalTool(capability) {
|
|
156
|
+
return {
|
|
157
|
+
name: capability.name,
|
|
158
|
+
implementationId: capability.implementationId,
|
|
159
|
+
description: capability.description,
|
|
160
|
+
inputSchema: capability.inputSchema,
|
|
161
|
+
...(capability.annotations === undefined ? {} : { annotations: capability.annotations }),
|
|
162
|
+
execute: (input, context) => capability.execute(input, { signal: context.signal }),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/** The provider port's tool shape (hosted `AgentProviderTool`): name, description, input schema
|
|
166
|
+
* and `execute(input, { signal, toolCallId })`. */
|
|
167
|
+
export function toProviderTool(capability) {
|
|
168
|
+
return {
|
|
169
|
+
name: capability.name,
|
|
170
|
+
description: capability.description,
|
|
171
|
+
inputSchema: capability.inputSchema,
|
|
172
|
+
execute: (input, invocation) => capability.execute(input, { signal: invocation.signal, toolCallId: invocation.toolCallId }),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/** The hosted `ToolDescriptor` shape, whose callers may omit the invocation or its signal; the
|
|
176
|
+
* capability then receives a signal that never aborts. */
|
|
177
|
+
export function toToolDescriptor(capability) {
|
|
178
|
+
return {
|
|
179
|
+
name: capability.name,
|
|
180
|
+
description: capability.description,
|
|
181
|
+
inputSchema: capability.inputSchema,
|
|
182
|
+
...(capability.annotations === undefined ? {} : { annotations: { ...capability.annotations } }),
|
|
183
|
+
execute: (input, invocation) => capability.execute(input, { signal: invocation?.signal ?? new AbortController().signal }),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* One annotation set from several declarations of the same capability (for example a WebMCP
|
|
188
|
+
* declaration and an MCP declaration that each carry some hints). For the MCP hints the more
|
|
189
|
+
* cautious value wins: `readOnlyHint` and `idempotentHint` are true only if no set says false;
|
|
190
|
+
* `destructiveHint`, `openWorldHint`, `untrustedContentHint` and `privateInputHint` are true if any
|
|
191
|
+
* set says true. A hint a set leaves out does not count either way, so the result can carry a hint
|
|
192
|
+
* only some sets declared; MCP's defaults for absent hints (`readOnlyHint` false, `openWorldHint`
|
|
193
|
+
* true) apply only when no set declares it. Any other key must not disagree between sets; a
|
|
194
|
+
* disagreement throws.
|
|
195
|
+
*/
|
|
196
|
+
export function mergeAnnotations(...sets) {
|
|
197
|
+
const merged = Object.create(null);
|
|
198
|
+
const allTrue = new Set(["readOnlyHint", "idempotentHint"]);
|
|
199
|
+
const anyTrue = new Set(["destructiveHint", "openWorldHint", "untrustedContentHint", "privateInputHint"]);
|
|
200
|
+
for (const set of sets) {
|
|
201
|
+
if (set === undefined)
|
|
202
|
+
continue;
|
|
203
|
+
if (!isRecord(set))
|
|
204
|
+
throw new TypeError("mergeAnnotations: annotations must be objects");
|
|
205
|
+
for (const [key, value] of Object.entries(set)) {
|
|
206
|
+
if (value === undefined)
|
|
207
|
+
continue;
|
|
208
|
+
if (allTrue.has(key) || anyTrue.has(key)) {
|
|
209
|
+
if (typeof value !== "boolean")
|
|
210
|
+
throw new TypeError(`mergeAnnotations: ${key} must be boolean`);
|
|
211
|
+
const prior = merged[key];
|
|
212
|
+
merged[key] = prior === undefined ? value : allTrue.has(key) ? prior && value : prior || value;
|
|
213
|
+
}
|
|
214
|
+
else if (key in merged && canonicalJson(merged[key]) !== canonicalJson(value)) {
|
|
215
|
+
throw new TypeError(`mergeAnnotations: ${key} disagrees`);
|
|
216
|
+
}
|
|
217
|
+
else
|
|
218
|
+
merged[key] = value;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return { ...merged };
|
|
222
|
+
}
|
|
223
|
+
/** The descriptor a surface shows a model: what the descriptor limits and the identity cover. */
|
|
224
|
+
export function siteCapabilityDescriptor(capability) {
|
|
225
|
+
return {
|
|
226
|
+
name: capability.name,
|
|
227
|
+
...(capability.title === undefined ? {} : { title: capability.title }),
|
|
228
|
+
description: capability.description,
|
|
229
|
+
inputSchema: capability.inputSchema,
|
|
230
|
+
...(capability.outputSchema === undefined ? {} : { outputSchema: capability.outputSchema }),
|
|
231
|
+
...(capability.annotations === undefined ? {} : { annotations: capability.annotations }),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/** Why a value is not a structurally valid `SiteCapability`, or `undefined` when it is. Checks the
|
|
235
|
+
* descriptor only; the name rules of a tier are compose's. */
|
|
236
|
+
export function siteCapabilityMetadataProblem(value) {
|
|
237
|
+
const capability = value;
|
|
238
|
+
if (!isRecord(capability))
|
|
239
|
+
return "not an object";
|
|
240
|
+
if (typeof capability.name !== "string" || !SITE_CAPABILITY_NAME_PATTERN.test(capability.name))
|
|
241
|
+
return "name must match ^[a-z][a-z0-9_]{2,63}$";
|
|
242
|
+
if (typeof capability.implementationId !== "string" ||
|
|
243
|
+
capability.implementationId.length === 0 ||
|
|
244
|
+
utf8Bytes(capability.implementationId) > MAX_IDENTIFIER_BYTES)
|
|
245
|
+
return "implementationId must be a non-empty string of at most 256 bytes";
|
|
246
|
+
if (typeof capability.description !== "string" || capability.description.trim().length === 0)
|
|
247
|
+
return "description must be non-empty text";
|
|
248
|
+
if (capability.title !== undefined && (typeof capability.title !== "string" || capability.title.trim().length === 0))
|
|
249
|
+
return "title must be non-empty text when present";
|
|
250
|
+
if (!isRecord(capability.inputSchema) || capability.inputSchema.type !== "object")
|
|
251
|
+
return 'inputSchema must be a JSON Schema object with type "object"';
|
|
252
|
+
if (capability.outputSchema !== undefined && !isRecord(capability.outputSchema))
|
|
253
|
+
return "outputSchema must be an object when present";
|
|
254
|
+
if (capability.annotations !== undefined && !isRecord(capability.annotations))
|
|
255
|
+
return "annotations must be an object when present";
|
|
256
|
+
if (capability.maximumOutputBytes !== undefined &&
|
|
257
|
+
(!Number.isSafeInteger(capability.maximumOutputBytes) || capability.maximumOutputBytes <= 0))
|
|
258
|
+
return "maximumOutputBytes must be a positive integer when present";
|
|
259
|
+
try {
|
|
260
|
+
canonicalJson(siteCapabilityDescriptor(capability));
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
return "descriptor must be plain JSON";
|
|
264
|
+
}
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
function descriptorProblem(capability) {
|
|
268
|
+
const problem = siteCapabilityMetadataProblem(capability);
|
|
269
|
+
if (problem)
|
|
270
|
+
return problem;
|
|
271
|
+
if (typeof capability.execute !== "function")
|
|
272
|
+
return "execute must be a function";
|
|
273
|
+
if (capability.mintSources !== undefined && typeof capability.mintSources !== "function")
|
|
274
|
+
return "mintSources must be a function when present";
|
|
275
|
+
return undefined;
|
|
276
|
+
}
|
|
277
|
+
/** Thrown by `capture` when a JSON field of a capability is not plain JSON. */
|
|
278
|
+
class NotPlainJson extends Error {
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Reads each field of a capability exactly once into a plain object, with its JSON fields
|
|
282
|
+
* (`inputSchema`, `outputSchema`, `annotations`) cloned through canonical JSON. Validation, the
|
|
283
|
+
* frozen snapshot, the digest and every surface then see the same values even when the caller
|
|
284
|
+
* passes getters or later mutates its objects. A non-object is returned unchanged for
|
|
285
|
+
* `descriptorProblem` to report. Throws when a field cannot be read, and `NotPlainJson` when a JSON
|
|
286
|
+
* field is not plain JSON.
|
|
287
|
+
*/
|
|
288
|
+
function capture(capability) {
|
|
289
|
+
if (!isRecord(capability))
|
|
290
|
+
return capability;
|
|
291
|
+
const source = capability;
|
|
292
|
+
const name = source.name, implementationId = source.implementationId, title = source.title, description = source.description, inputSchema = source.inputSchema, outputSchema = source.outputSchema, annotations = source.annotations, maximumOutputBytes = source.maximumOutputBytes, execute = source.execute, mintSources = source.mintSources;
|
|
293
|
+
const clone = (value) => {
|
|
294
|
+
if (value === undefined)
|
|
295
|
+
return value;
|
|
296
|
+
let text;
|
|
297
|
+
try {
|
|
298
|
+
text = canonicalJson(value);
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
throw new NotPlainJson();
|
|
302
|
+
}
|
|
303
|
+
return JSON.parse(text);
|
|
304
|
+
};
|
|
305
|
+
return {
|
|
306
|
+
name,
|
|
307
|
+
implementationId,
|
|
308
|
+
...(title === undefined ? {} : { title }),
|
|
309
|
+
description,
|
|
310
|
+
inputSchema: clone(inputSchema),
|
|
311
|
+
...(outputSchema === undefined ? {} : { outputSchema: clone(outputSchema) }),
|
|
312
|
+
...(annotations === undefined ? {} : { annotations: clone(annotations) }),
|
|
313
|
+
...(maximumOutputBytes === undefined ? {} : { maximumOutputBytes }),
|
|
314
|
+
execute: execute === undefined ? execute : bindTo(source, execute),
|
|
315
|
+
...(mintSources === undefined ? {} : { mintSources: bindTo(source, mintSources) }),
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
/** `method` called with `source` as `this`, or `method` itself when it is not a function, so
|
|
319
|
+
* `descriptorProblem` still sees what the caller passed. */
|
|
320
|
+
function bindTo(source, method) {
|
|
321
|
+
if (typeof method !== "function")
|
|
322
|
+
return method;
|
|
323
|
+
return ((...args) => method.apply(source, args));
|
|
324
|
+
}
|
|
325
|
+
/** The validated capture, frozen at every depth. */
|
|
326
|
+
function snapshot(captured) {
|
|
327
|
+
return Object.freeze({
|
|
328
|
+
...captured,
|
|
329
|
+
inputSchema: deepFreeze(captured.inputSchema),
|
|
330
|
+
...(captured.outputSchema === undefined ? {} : { outputSchema: deepFreeze(captured.outputSchema) }),
|
|
331
|
+
...(captured.annotations === undefined ? {} : { annotations: deepFreeze(captured.annotations) }),
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
function deepFreeze(value) {
|
|
335
|
+
if (typeof value === "object" && value !== null) {
|
|
336
|
+
for (const entry of Object.values(value))
|
|
337
|
+
deepFreeze(entry);
|
|
338
|
+
Object.freeze(value);
|
|
339
|
+
}
|
|
340
|
+
return value;
|
|
341
|
+
}
|
|
342
|
+
export const DEFAULT_SITE_CAPABILITY_LIMITS = Object.freeze({
|
|
343
|
+
maxTools: 32,
|
|
344
|
+
maxDescriptorBytes: 4096,
|
|
345
|
+
maxTotalDescriptorBytes: 49152,
|
|
346
|
+
});
|
|
347
|
+
const TIER_ORDER = ["generic", "pack", "site"];
|
|
348
|
+
/**
|
|
349
|
+
* Composes the capabilities one surface offers. This is the one owner of the composition rule;
|
|
350
|
+
* Portal's registration applies the same reserved-prefix and duplicate refusals to its own
|
|
351
|
+
* additional tools and can call this instead. Conflicts refuse the whole
|
|
352
|
+
* composition, never shadow: a duplicate name, a `superbee_` name outside the generic tier, or a
|
|
353
|
+
* pack or site name outside its namespace. Order is deterministic (generic, packs, site extras,
|
|
354
|
+
* each in declaration order), which prompt caching and the Managed Agents byte check depend on.
|
|
355
|
+
* The surface's exclusions apply after the conflict checks; the limits apply to what is kept.
|
|
356
|
+
*/
|
|
357
|
+
export function composeSiteCapabilities(groups, options) {
|
|
358
|
+
const refuse = (refused, detail, names = []) => Object.freeze({ ok: false, refused, names: Object.freeze([...names]), detail });
|
|
359
|
+
// A limit that is not a non-negative safe integer (undefined, NaN, Infinity) keeps its default,
|
|
360
|
+
// so a malformed override can never switch a limit off.
|
|
361
|
+
const limit = (key) => {
|
|
362
|
+
const value = options.limits?.[key];
|
|
363
|
+
return Number.isSafeInteger(value) && value >= 0 ? value : DEFAULT_SITE_CAPABILITY_LIMITS[key];
|
|
364
|
+
};
|
|
365
|
+
const limits = {
|
|
366
|
+
maxTools: limit("maxTools"),
|
|
367
|
+
maxDescriptorBytes: limit("maxDescriptorBytes"),
|
|
368
|
+
maxTotalDescriptorBytes: limit("maxTotalDescriptorBytes"),
|
|
369
|
+
};
|
|
370
|
+
if (typeof options.surface !== "string" || options.surface.length === 0 || utf8Bytes(options.surface) > 256)
|
|
371
|
+
return refuse("invalid_surface", "surface must be a non-empty string of at most 256 bytes");
|
|
372
|
+
const namespaces = new Set();
|
|
373
|
+
const entries = [];
|
|
374
|
+
for (const tier of TIER_ORDER) {
|
|
375
|
+
for (const group of groups) {
|
|
376
|
+
if (!isRecord(group) || !TIER_ORDER.includes(group.tier))
|
|
377
|
+
return refuse("invalid_group", "each group needs a tier of generic, pack or site");
|
|
378
|
+
if (group.tier !== tier)
|
|
379
|
+
continue;
|
|
380
|
+
if (tier === "generic") {
|
|
381
|
+
if (group.namespace !== undefined)
|
|
382
|
+
return refuse("invalid_group", "the generic tier has no namespace", [String(group.namespace)]);
|
|
383
|
+
}
|
|
384
|
+
else if (typeof group.namespace !== "string" ||
|
|
385
|
+
!NAMESPACE_PATTERN.test(group.namespace) ||
|
|
386
|
+
`${group.namespace}_` === RESERVED_TOOL_PREFIX ||
|
|
387
|
+
namespaces.has(group.namespace)) {
|
|
388
|
+
return refuse("invalid_group", "each pack and site group needs its own namespace matching ^[a-z][a-z0-9]{1,15}$, other than superbee", typeof group.namespace === "string" ? [group.namespace] : []);
|
|
389
|
+
}
|
|
390
|
+
if (tier !== "generic")
|
|
391
|
+
namespaces.add(group.namespace);
|
|
392
|
+
if (!Array.isArray(group.capabilities))
|
|
393
|
+
return refuse("invalid_group", "capabilities must be an array");
|
|
394
|
+
for (const capability of group.capabilities) {
|
|
395
|
+
let owned;
|
|
396
|
+
try {
|
|
397
|
+
// Validate the one capture, never the caller's object, so a getter cannot pass the
|
|
398
|
+
// checks with one value and ship another.
|
|
399
|
+
const captured = capture(capability);
|
|
400
|
+
const problem = descriptorProblem(captured);
|
|
401
|
+
if (problem)
|
|
402
|
+
return refuse("invalid_descriptor", problem, typeof captured?.name === "string" ? [captured.name] : []);
|
|
403
|
+
owned = snapshot(captured);
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
if (error instanceof NotPlainJson)
|
|
407
|
+
return refuse("invalid_descriptor", "descriptor must be plain JSON");
|
|
408
|
+
return refuse("invalid_descriptor", "descriptor could not be read");
|
|
409
|
+
}
|
|
410
|
+
entries.push({ tier, namespace: tier === "generic" ? null : group.namespace, capability: owned });
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
const seen = new Set();
|
|
415
|
+
for (const { tier, namespace, capability } of entries) {
|
|
416
|
+
if (seen.has(capability.name))
|
|
417
|
+
return refuse("duplicate_name", "two capabilities share a name", [capability.name]);
|
|
418
|
+
seen.add(capability.name);
|
|
419
|
+
if (tier !== "generic" && capability.name.startsWith(RESERVED_TOOL_PREFIX))
|
|
420
|
+
return refuse("reserved_prefix", "superbee_ names belong to the generic tier", [capability.name]);
|
|
421
|
+
if (namespace !== null && !capability.name.startsWith(`${namespace}_`))
|
|
422
|
+
return refuse("outside_namespace", "a pack or site name must start with its namespace and _", [capability.name]);
|
|
423
|
+
if (options.requireReadOnly === true &&
|
|
424
|
+
(capability.annotations?.readOnlyHint !== true || capability.annotations?.destructiveHint === true))
|
|
425
|
+
return refuse("not_read_only", "this surface takes only capabilities declared read-only", [capability.name]);
|
|
426
|
+
}
|
|
427
|
+
const exclude = options.exclude ?? [];
|
|
428
|
+
const unknown = exclude.filter((name) => !seen.has(name));
|
|
429
|
+
if (unknown.length)
|
|
430
|
+
return refuse("unknown_exclusion", "an excluded name is not composed", unknown);
|
|
431
|
+
const excluded = new Set(exclude);
|
|
432
|
+
const kept = entries.filter((entry) => !excluded.has(entry.capability.name));
|
|
433
|
+
const dropped = entries.filter((entry) => excluded.has(entry.capability.name)).map((entry) => entry.capability.name);
|
|
434
|
+
if (kept.length > limits.maxTools)
|
|
435
|
+
return refuse("too_many_tools", `at most ${limits.maxTools} tools per surface`);
|
|
436
|
+
let total = 0;
|
|
437
|
+
for (const { capability } of kept) {
|
|
438
|
+
const size = utf8Bytes(canonicalJson(siteCapabilityDescriptor(capability)));
|
|
439
|
+
if (size > limits.maxDescriptorBytes)
|
|
440
|
+
return refuse("descriptor_too_large", `a descriptor is over ${limits.maxDescriptorBytes} bytes`, [
|
|
441
|
+
capability.name,
|
|
442
|
+
]);
|
|
443
|
+
total += size;
|
|
444
|
+
}
|
|
445
|
+
if (total > limits.maxTotalDescriptorBytes)
|
|
446
|
+
return refuse("total_too_large", `descriptors total over ${limits.maxTotalDescriptorBytes} bytes`);
|
|
447
|
+
const identityDigest = `sha256:${sha256HexOfUtf8(canonicalJson({
|
|
448
|
+
schema: "superbee-site-capabilities/v1",
|
|
449
|
+
surface: options.surface,
|
|
450
|
+
tools: kept.map(({ tier, namespace, capability }) => ({
|
|
451
|
+
tier,
|
|
452
|
+
namespace,
|
|
453
|
+
implementationId: capability.implementationId,
|
|
454
|
+
maximumOutputBytes: capability.maximumOutputBytes ?? null,
|
|
455
|
+
descriptor: siteCapabilityDescriptor(capability),
|
|
456
|
+
})),
|
|
457
|
+
}))}`;
|
|
458
|
+
return Object.freeze({
|
|
459
|
+
ok: true,
|
|
460
|
+
surface: options.surface,
|
|
461
|
+
capabilities: Object.freeze(kept.map((entry) => entry.capability)),
|
|
462
|
+
tiers: Object.freeze(kept.map((entry) => entry.tier)),
|
|
463
|
+
dropped: Object.freeze(dropped),
|
|
464
|
+
identityDigest,
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
const DEFAULT_CHECK_NOW = Date.UTC(2026, 0, 1, 12);
|
|
468
|
+
const UNKNOWN_ARGUMENT = "zz_unexpected_argument";
|
|
469
|
+
function* strings(value, depth = 0) {
|
|
470
|
+
if (depth > 64)
|
|
471
|
+
return;
|
|
472
|
+
if (typeof value === "string")
|
|
473
|
+
yield value;
|
|
474
|
+
else if (Array.isArray(value))
|
|
475
|
+
for (const entry of value)
|
|
476
|
+
yield* strings(entry, depth + 1);
|
|
477
|
+
else if (isRecord(value))
|
|
478
|
+
for (const entry of Object.values(value))
|
|
479
|
+
yield* strings(entry, depth + 1);
|
|
480
|
+
}
|
|
481
|
+
/** A script or data URL as a whole string, or as a Markdown link, quoted or attribute value. */
|
|
482
|
+
const SCRIPT_URL_IN_TEXT = /(?:^\s*|\]\(\s*|["'<=]\s*)(?:javascript|data|vbscript):/iu;
|
|
483
|
+
/** An absolute URL in text: any scheme with `//`, a web scheme with up to two slashes or
|
|
484
|
+
* backslashes (URL parsing reads `https:evil.example` as `https://evil.example`), or a
|
|
485
|
+
* protocol-relative URL at the start of a word or attribute value. */
|
|
486
|
+
const URL_IN_TEXT = /(?:[a-z][a-z0-9+.-]*:\/\/|(?<![a-z0-9+.-])(?:https?|wss?|ftp):[\\/]{0,2}(?=[^\s\\/])|(?<![^\s("'<[=>])\/\/)[^\s<>"'`)\]]+/giu;
|
|
487
|
+
/** Sentence punctuation that ends prose, not the URL before it. */
|
|
488
|
+
const TRAILING_PUNCTUATION = /[.,;:!?]+$/u;
|
|
489
|
+
/**
|
|
490
|
+
* Tests one capability against recorded fixtures and the site capability requirements:
|
|
491
|
+
* a valid, read-only descriptor; results that are deterministic for a pinned clock, bounded,
|
|
492
|
+
* dated (`as_of`) when successful, and `{ ok: false, error: { code, message } }` with a closed code
|
|
493
|
+
* and a message that never echoes input when not; absolute URLs only on the site's origin or its
|
|
494
|
+
* declared external hosts; every citation from `mintSources` naming a record ID present in the
|
|
495
|
+
* result and a safe same-origin path; unknown arguments refused; and a descriptor that running
|
|
496
|
+
* the fixtures does not change. Runs each fixture twice. Never throws for a capability's fault.
|
|
497
|
+
*/
|
|
498
|
+
export async function checkSiteCapability(capability, fixtures, options) {
|
|
499
|
+
const problems = [];
|
|
500
|
+
const add = (requirement, detail, fixture) => problems.push(Object.freeze({ requirement, detail, ...(fixture === undefined ? {} : { fixture }) }));
|
|
501
|
+
const structural = descriptorProblem(capability);
|
|
502
|
+
if (structural) {
|
|
503
|
+
add("descriptor", structural);
|
|
504
|
+
return Object.freeze({ ok: false, problems: Object.freeze(problems) });
|
|
505
|
+
}
|
|
506
|
+
if (capability.name.startsWith(RESERVED_TOOL_PREFIX))
|
|
507
|
+
add("descriptor", "site capabilities never use superbee_");
|
|
508
|
+
if (capability.annotations?.readOnlyHint !== true)
|
|
509
|
+
add("read_only", "annotations.readOnlyHint must be true");
|
|
510
|
+
if (capability.annotations?.destructiveHint === true)
|
|
511
|
+
add("read_only", "annotations.destructiveHint must not be true");
|
|
512
|
+
const descriptorBefore = canonicalJson({
|
|
513
|
+
...siteCapabilityDescriptor(capability),
|
|
514
|
+
implementationId: capability.implementationId,
|
|
515
|
+
});
|
|
516
|
+
const origin = new URL(options.origin).origin;
|
|
517
|
+
const externalHosts = new Set((options.externalHosts ?? []).map((host) => host.toLowerCase()));
|
|
518
|
+
const codes = new Set(options.errorCodes ?? SITE_CAPABILITY_ERROR_CODES);
|
|
519
|
+
const maximum = capability.maximumOutputBytes ?? DEFAULT_MAXIMUM_OUTPUT_BYTES;
|
|
520
|
+
const run = async (input, now) => {
|
|
521
|
+
try {
|
|
522
|
+
const controller = new AbortController();
|
|
523
|
+
return { result: await capability.execute(structuredClone(input), { signal: controller.signal, now: () => now }) };
|
|
524
|
+
}
|
|
525
|
+
catch {
|
|
526
|
+
return { thrown: true };
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
const examine = async (fixture, label) => {
|
|
530
|
+
const now = fixture.now ?? options.now ?? DEFAULT_CHECK_NOW;
|
|
531
|
+
const first = await run(fixture.input, now);
|
|
532
|
+
const second = await run(fixture.input, now);
|
|
533
|
+
if (first.thrown || second.thrown) {
|
|
534
|
+
add("errors", "execute threw; a refusal must be a { ok: false } result", label);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
const result = first.result;
|
|
538
|
+
let serialized;
|
|
539
|
+
try {
|
|
540
|
+
serialized = canonicalJson(result);
|
|
541
|
+
}
|
|
542
|
+
catch {
|
|
543
|
+
add("bounded", "the result is not plain JSON", label);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
if (utf8Bytes(serialized) > maximum)
|
|
547
|
+
add("bounded", `the result is over ${maximum} bytes`, label);
|
|
548
|
+
let again;
|
|
549
|
+
try {
|
|
550
|
+
again = canonicalJson(second.result);
|
|
551
|
+
}
|
|
552
|
+
catch {
|
|
553
|
+
again = undefined;
|
|
554
|
+
}
|
|
555
|
+
if (again !== serialized)
|
|
556
|
+
add("deterministic", "the same input and clock gave different results", label);
|
|
557
|
+
if (!isRecord(result) || typeof result.ok !== "boolean") {
|
|
558
|
+
add("errors", "a result must be an object with a boolean ok", label);
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
for (const text of strings(result)) {
|
|
562
|
+
// URL parsing removes tabs and newlines, so `java\tscript:` is a script URL too.
|
|
563
|
+
if (SCRIPT_URL_IN_TEXT.test(text.replace(/[\t\r\n]/gu, ""))) {
|
|
564
|
+
add("public_urls", "a result carries a script or data URL", label);
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
// Every absolute or protocol-relative URL in the text, not only a string that is one URL:
|
|
568
|
+
// prose and Markdown links can carry them too.
|
|
569
|
+
for (const [match] of text.matchAll(URL_IN_TEXT)) {
|
|
570
|
+
let url;
|
|
571
|
+
try {
|
|
572
|
+
const candidate = match.replace(TRAILING_PUNCTUATION, "");
|
|
573
|
+
url = new URL(candidate.startsWith("//") ? `${new URL(origin).protocol}${candidate}` : candidate);
|
|
574
|
+
}
|
|
575
|
+
catch {
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
if (url.origin === origin)
|
|
579
|
+
continue;
|
|
580
|
+
if ((url.protocol === "https:" || url.protocol === "http:") && externalHosts.has(url.hostname.toLowerCase()))
|
|
581
|
+
continue;
|
|
582
|
+
add("public_urls", `a result links off the site: ${url.origin}`, label);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
if (fixture.expect === "ok") {
|
|
586
|
+
if (result.ok !== true) {
|
|
587
|
+
add("errors", "expected a successful result", label);
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
if (!isDate(result.as_of))
|
|
591
|
+
add("dated", "a successful result needs an ISO 8601 as_of", label);
|
|
592
|
+
if (capability.mintSources) {
|
|
593
|
+
let citations;
|
|
594
|
+
try {
|
|
595
|
+
citations = capability.mintSources(result);
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
add("stable_ids", "mintSources threw", label);
|
|
599
|
+
citations = [];
|
|
600
|
+
}
|
|
601
|
+
if (!Array.isArray(citations))
|
|
602
|
+
add("stable_ids", "mintSources must return an array", label);
|
|
603
|
+
else {
|
|
604
|
+
const present = new Set(strings(result));
|
|
605
|
+
for (const citation of citations) {
|
|
606
|
+
if (!isRecord(citation) || typeof citation.recordId !== "string" || !present.has(citation.recordId))
|
|
607
|
+
add("stable_ids", "a cited record ID does not appear in the result", label);
|
|
608
|
+
else if (!isSafeSitePath(citation.path))
|
|
609
|
+
add("stable_ids", `the citation of ${citation.recordId} has an unsafe path`, label);
|
|
610
|
+
else if (!isDate(citation.asOf))
|
|
611
|
+
add("stable_ids", `the citation of ${citation.recordId} has no ISO 8601 asOf`, label);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
const error = result.error;
|
|
618
|
+
if (result.ok !== false || !isRecord(error) || typeof error.code !== "string" || typeof error.message !== "string") {
|
|
619
|
+
add("errors", "expected { ok: false, error: { code, message } }", label);
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
if (!codes.has(error.code))
|
|
623
|
+
add("errors", `error code ${error.code} is outside the closed set`, label);
|
|
624
|
+
if (fixture.errorCode !== undefined && error.code !== fixture.errorCode)
|
|
625
|
+
add("errors", `expected error code ${fixture.errorCode}, got ${error.code}`, label);
|
|
626
|
+
const message = error.message;
|
|
627
|
+
for (const text of strings(fixture.input))
|
|
628
|
+
if (text.trim().length >= 3 && message.includes(text)) {
|
|
629
|
+
add("errors", "an error message echoes the input", label);
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
try {
|
|
634
|
+
const verdict = fixture.check?.(result);
|
|
635
|
+
if (typeof verdict === "string" && verdict)
|
|
636
|
+
add("site", verdict, label);
|
|
637
|
+
}
|
|
638
|
+
catch {
|
|
639
|
+
add("site", "the fixture's own check threw", label);
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
for (const [index, fixture] of fixtures.entries())
|
|
643
|
+
await examine(fixture, fixture.name ?? String(index));
|
|
644
|
+
const sample = fixtures.find((fixture) => fixture.expect === "ok" && isRecord(fixture.input));
|
|
645
|
+
if (options.probeUnknownArgument !== false) {
|
|
646
|
+
if (!sample)
|
|
647
|
+
add("input_checked", "no successful fixture with an object input to probe");
|
|
648
|
+
else {
|
|
649
|
+
const probe = await run({ ...sample.input, [UNKNOWN_ARGUMENT]: 1 }, sample.now ?? options.now ?? DEFAULT_CHECK_NOW);
|
|
650
|
+
if (probe.thrown || !isRecord(probe.result) || probe.result.ok !== false)
|
|
651
|
+
add("input_checked", "an unknown argument was not refused with { ok: false }", "unknown-argument probe");
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
const descriptorAfter = (() => {
|
|
655
|
+
try {
|
|
656
|
+
return canonicalJson({ ...siteCapabilityDescriptor(capability), implementationId: capability.implementationId });
|
|
657
|
+
}
|
|
658
|
+
catch {
|
|
659
|
+
return undefined;
|
|
660
|
+
}
|
|
661
|
+
})();
|
|
662
|
+
if (descriptorAfter !== descriptorBefore)
|
|
663
|
+
add("fixed_text", "running the capability changed its descriptor");
|
|
664
|
+
return Object.freeze({ ok: problems.length === 0, problems: Object.freeze(problems) });
|
|
665
|
+
}
|
|
666
|
+
const REQUIREMENT_TITLES = {
|
|
667
|
+
descriptor: "has a valid descriptor with a fixed name, implementation ID and object input schema",
|
|
668
|
+
read_only: "declares itself read-only and not destructive",
|
|
669
|
+
dated: "dates every successful result with as_of",
|
|
670
|
+
stable_ids: "cites only record IDs present in the result, with safe paths and dates",
|
|
671
|
+
public_urls: "links only to the site's origin or its declared external hosts",
|
|
672
|
+
errors: "refuses with { ok: false, error: { code, message } } from the closed code set, never echoing input",
|
|
673
|
+
bounded: "stays within its maximum output bytes",
|
|
674
|
+
deterministic: "returns the same result for the same input and clock",
|
|
675
|
+
input_checked: "refuses unknown arguments",
|
|
676
|
+
fixed_text: "keeps its descriptor fixed while it runs",
|
|
677
|
+
site: "passes the site's own fixture assertions",
|
|
678
|
+
};
|
|
679
|
+
/**
|
|
680
|
+
* The reusable conformance suite for one capability: one case per requirement checked by
|
|
681
|
+
* `checkSiteCapability`, plus the composition and mapping cases every surface relies on (it
|
|
682
|
+
* composes alone in its own namespace on a read-only surface, and its provider, Portal and
|
|
683
|
+
* descriptor mappings carry the same name, description and schema). A site, a pack and any host
|
|
684
|
+
* run the same cases over the same fixtures.
|
|
685
|
+
*/
|
|
686
|
+
export function siteCapabilityConformanceCases(capability, fixtures, options) {
|
|
687
|
+
let checked;
|
|
688
|
+
const check = () => (checked ??= checkSiteCapability(capability, fixtures, options));
|
|
689
|
+
const label = typeof capability?.name === "string" ? capability.name : "capability";
|
|
690
|
+
const cases = Object.keys(REQUIREMENT_TITLES).map((requirement) => ({
|
|
691
|
+
name: `${label} ${REQUIREMENT_TITLES[requirement]}`,
|
|
692
|
+
async run() {
|
|
693
|
+
const problems = (await check()).problems.filter((problem) => problem.requirement === requirement);
|
|
694
|
+
if (problems.length)
|
|
695
|
+
throw new Error(problems.map((problem) => (problem.fixture ? `[${problem.fixture}] ` : "") + problem.detail).join("\n"));
|
|
696
|
+
},
|
|
697
|
+
}));
|
|
698
|
+
cases.push({
|
|
699
|
+
name: `${label} composes alone in its namespace on a read-only surface`,
|
|
700
|
+
async run() {
|
|
701
|
+
const namespace = options.namespace ?? (typeof capability?.name === "string" ? capability.name.split("_")[0] : "");
|
|
702
|
+
const composed = composeSiteCapabilities([{ tier: "site", namespace, capabilities: [capability] }], {
|
|
703
|
+
surface: "conformance",
|
|
704
|
+
requireReadOnly: true,
|
|
705
|
+
});
|
|
706
|
+
if (!composed.ok)
|
|
707
|
+
throw new Error(`refused: ${composed.refused} (${composed.detail})`);
|
|
708
|
+
},
|
|
709
|
+
});
|
|
710
|
+
cases.push({
|
|
711
|
+
name: `${label} maps to the provider, Portal and descriptor tool shapes unchanged`,
|
|
712
|
+
async run() {
|
|
713
|
+
const expected = canonicalJson([capability.name, capability.description, capability.inputSchema]);
|
|
714
|
+
for (const mapped of [toProviderTool(capability), toPortalAdditionalTool(capability), toToolDescriptor(capability)])
|
|
715
|
+
if (canonicalJson([mapped.name, mapped.description, mapped.inputSchema]) !== expected)
|
|
716
|
+
throw new Error("a mapping changed the name, description or input schema");
|
|
717
|
+
const portal = toPortalAdditionalTool(capability);
|
|
718
|
+
if (portal.implementationId !== capability.implementationId)
|
|
719
|
+
throw new Error("the Portal mapping lost implementationId");
|
|
720
|
+
},
|
|
721
|
+
});
|
|
722
|
+
return Object.freeze(cases);
|
|
723
|
+
}
|
|
724
|
+
//# sourceMappingURL=site-capabilities.js.map
|