@ixo/editor 6.31.4 → 6.32.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1094,12 +1094,11 @@ var PRESENTATION = {
1094
1094
  "qi/topic.action.receipt.record": { displayName: "Record Action Receipt", description: "Record a signed Action receipt against its Topic request" },
1095
1095
  "qi/topic.action.request": { displayName: "Request Topic Action", description: "Request an Action against the current Topic revision" },
1096
1096
  "qi/topic.context.link": { displayName: "Link Topic Context", description: "Link a referenced resource or service to the Topic" },
1097
- "qi/topic.contract.accept": { displayName: "Accept Topic Contract", description: "Accept the exact current Topic contract revision" },
1097
+ "qi/topic.contract.confirm-setup": { displayName: "Confirm Topic Setup", description: "Confirm the exact proposed Topic setup revision" },
1098
1098
  "qi/topic.decision.record": { displayName: "Record Topic Decision", description: "Record an authorised decision and its rationale" },
1099
1099
  "qi/topic.file.attach-reference": { displayName: "Attach File Reference", description: "Attach a pinned VFS file reference to the Topic" },
1100
1100
  "qi/topic.flow.bind": { displayName: "Bind Flow to Topic", description: "Bind a governed Flow revision to the Topic" },
1101
1101
  "qi/topic.flow.unbind": { displayName: "Unbind Flow from Topic", description: "Remove an existing Flow binding from the Topic" },
1102
- "qi/topic.outcome.confirm": { displayName: "Confirm Topic Outcome", description: "Confirm a proposed outcome using the stated authority" },
1103
1102
  "qi/topic.outcome.propose": { displayName: "Propose Topic Outcome", description: "Propose an evidence-backed outcome for review" },
1104
1103
  "qi/topic.status.transition": { displayName: "Change Topic Status", description: "Move the Topic between permitted lifecycle statuses" },
1105
1104
  "qi/wallet.fund": { displayName: "Fund Wallet", description: "Fund a wallet with an on-chain transfer" },
@@ -1336,7 +1335,11 @@ var ACTION_TYPE_ALIASES = {
1336
1335
  // qi/ixo.calendar.event.* instead.
1337
1336
  "qi/calendar.event.create": "qi/googlecalendar.event.create-self",
1338
1337
  "qi/calendar.event.update": "qi/googlecalendar.event.update-self",
1339
- "qi/calendar.event.list": "qi/googlecalendar.event.list-self"
1338
+ "qi/calendar.event.list": "qi/googlecalendar.event.list-self",
1339
+ // Topic v3 called setup confirmation "accept contract". Persisted Flow
1340
+ // documents keep that action key, so resolve it to the v4 confirmation
1341
+ // contract while new documents use the canonical name.
1342
+ "qi/topic.contract.accept": "qi/topic.contract.confirm-setup"
1340
1343
  };
1341
1344
  var aliases = new Map(Object.entries(ACTION_TYPE_ALIASES));
1342
1345
  function resolveActionType(type) {
@@ -1601,6 +1604,345 @@ function actionManifestIssues(manifest = generateActionManifest()) {
1601
1604
  return issues;
1602
1605
  }
1603
1606
 
1607
+ // src/core/lib/actionRegistry/manifestV2.schema.ts
1608
+ var ACTION_MANIFEST_V2_SCHEMA = {
1609
+ $schema: "https://json-schema.org/draft/2020-12/schema",
1610
+ $id: "https://ixo.world/schemas/qi/action-manifest-v2.schema.json",
1611
+ type: "object",
1612
+ additionalProperties: false,
1613
+ required: ["manifestVersion", "manifestId", "generatedAt", "package", "issuer", "compatibility", "actions", "primitives", "integrity"],
1614
+ properties: {
1615
+ manifestVersion: { const: "2.0" },
1616
+ manifestId: { type: "string", minLength: 1 },
1617
+ generatedAt: {
1618
+ type: "string",
1619
+ pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$"
1620
+ },
1621
+ package: {
1622
+ type: "object",
1623
+ additionalProperties: false,
1624
+ required: ["name", "version", "sourceCommit"],
1625
+ properties: {
1626
+ name: { type: "string", minLength: 1 },
1627
+ version: { type: "string", minLength: 1 },
1628
+ sourceCommit: { type: "string", minLength: 1 },
1629
+ buildId: { type: "string", minLength: 1 },
1630
+ distributionUrl: { type: "string", pattern: "^[a-zA-Z][a-zA-Z0-9+.-]*:" }
1631
+ }
1632
+ },
1633
+ issuer: {
1634
+ type: "object",
1635
+ additionalProperties: false,
1636
+ required: ["did", "verificationMethod"],
1637
+ properties: {
1638
+ did: { type: "string", pattern: "^did:[a-z0-9]+:.+" },
1639
+ verificationMethod: { type: "string", pattern: "^did:[a-z0-9]+:.+#.+" }
1640
+ }
1641
+ },
1642
+ compatibility: {
1643
+ type: "object",
1644
+ additionalProperties: false,
1645
+ required: ["engine", "flowSchemaVersions", "features"],
1646
+ properties: {
1647
+ engine: {
1648
+ type: "object",
1649
+ additionalProperties: false,
1650
+ required: ["versionRange"],
1651
+ properties: {
1652
+ versionRange: { type: "string", minLength: 1 }
1653
+ }
1654
+ },
1655
+ flowSchemaVersions: {
1656
+ type: "array",
1657
+ minItems: 1,
1658
+ uniqueItems: true,
1659
+ items: { type: "string", minLength: 1 }
1660
+ },
1661
+ features: {
1662
+ type: "array",
1663
+ minItems: 1,
1664
+ uniqueItems: true,
1665
+ items: { type: "string", minLength: 1 }
1666
+ }
1667
+ }
1668
+ },
1669
+ actions: {
1670
+ type: "array",
1671
+ items: { $ref: "#/$defs/contract" }
1672
+ },
1673
+ primitives: {
1674
+ type: "array",
1675
+ items: { $ref: "#/$defs/contract" }
1676
+ },
1677
+ integrity: {
1678
+ type: "object",
1679
+ additionalProperties: false,
1680
+ required: ["canonicalization", "digest", "signature"],
1681
+ properties: {
1682
+ canonicalization: { const: "RFC8785" },
1683
+ digest: {
1684
+ type: "object",
1685
+ additionalProperties: false,
1686
+ required: ["algorithm", "value"],
1687
+ properties: {
1688
+ algorithm: { const: "SHA-256" },
1689
+ value: { type: "string", pattern: "^sha256:[0-9a-f]{64}$" }
1690
+ }
1691
+ },
1692
+ signature: {
1693
+ type: "object",
1694
+ additionalProperties: false,
1695
+ required: ["algorithm", "verificationMethod", "value"],
1696
+ properties: {
1697
+ algorithm: { type: "string", minLength: 1 },
1698
+ verificationMethod: { type: "string", pattern: "^did:[a-z0-9]+:.+#.+" },
1699
+ value: { type: "string", minLength: 1 }
1700
+ }
1701
+ }
1702
+ }
1703
+ }
1704
+ },
1705
+ $defs: {
1706
+ contract: {
1707
+ type: "object",
1708
+ required: ["type", "contractVersion"],
1709
+ properties: {
1710
+ type: { type: "string", minLength: 1 },
1711
+ contractVersion: { type: "string", minLength: 1 },
1712
+ aliases: {
1713
+ type: "array",
1714
+ uniqueItems: true,
1715
+ items: { type: "string", minLength: 1 }
1716
+ }
1717
+ }
1718
+ }
1719
+ }
1720
+ };
1721
+
1722
+ // src/core/lib/actionRegistry/manifestV2.ts
1723
+ import Ajv20202 from "ajv/dist/2020.js";
1724
+ var ActionManifestV2VerificationError = class extends Error {
1725
+ constructor(code, message) {
1726
+ super(message);
1727
+ this.name = "ActionManifestV2VerificationError";
1728
+ this.code = code;
1729
+ }
1730
+ };
1731
+ var validateSignedManifest = new Ajv20202({ allErrors: true, strict: false }).compile(ACTION_MANIFEST_V2_SCHEMA);
1732
+ var SIGNATURE_DOMAIN = "qi.action-manifest.v2\0";
1733
+ function isPlainObject(value) {
1734
+ const prototype = Object.getPrototypeOf(value);
1735
+ return prototype === Object.prototype || prototype === null;
1736
+ }
1737
+ function assertValidUnicode(value) {
1738
+ for (let index = 0; index < value.length; index += 1) {
1739
+ const codeUnit = value.charCodeAt(index);
1740
+ if (codeUnit >= 55296 && codeUnit <= 56319) {
1741
+ const next = value.charCodeAt(index + 1);
1742
+ if (!(next >= 56320 && next <= 57343)) {
1743
+ throw new TypeError("Manifest v2 contains an unpaired Unicode surrogate");
1744
+ }
1745
+ index += 1;
1746
+ } else if (codeUnit >= 56320 && codeUnit <= 57343) {
1747
+ throw new TypeError("Manifest v2 contains an unpaired Unicode surrogate");
1748
+ }
1749
+ }
1750
+ }
1751
+ function canonicalizeManifestV2Payload(payload) {
1752
+ const ancestors = /* @__PURE__ */ new Set();
1753
+ const serialize = (value) => {
1754
+ if (value === null || typeof value === "boolean") {
1755
+ return JSON.stringify(value);
1756
+ }
1757
+ if (typeof value === "string") {
1758
+ assertValidUnicode(value);
1759
+ return JSON.stringify(value);
1760
+ }
1761
+ if (typeof value === "number") {
1762
+ if (!Number.isFinite(value)) {
1763
+ throw new TypeError("Manifest v2 contains a non-finite number");
1764
+ }
1765
+ return JSON.stringify(value);
1766
+ }
1767
+ if (typeof value !== "object") {
1768
+ throw new TypeError(`Manifest v2 contains a non-JSON value: ${typeof value}`);
1769
+ }
1770
+ if (ancestors.has(value)) {
1771
+ throw new TypeError("Manifest v2 contains a circular reference");
1772
+ }
1773
+ ancestors.add(value);
1774
+ try {
1775
+ if (Array.isArray(value)) {
1776
+ return `[${value.map((entry) => serialize(entry)).join(",")}]`;
1777
+ }
1778
+ if (!isPlainObject(value)) {
1779
+ throw new TypeError("Manifest v2 contains a non-plain object");
1780
+ }
1781
+ return `{${Object.keys(value).sort().map((key) => {
1782
+ assertValidUnicode(key);
1783
+ return `${JSON.stringify(key)}:${serialize(value[key])}`;
1784
+ }).join(",")}}`;
1785
+ } finally {
1786
+ ancestors.delete(value);
1787
+ }
1788
+ };
1789
+ return serialize(payload);
1790
+ }
1791
+ async function computeActionManifestV2Digest(payload) {
1792
+ if (!globalThis.crypto?.subtle) {
1793
+ throw new Error("Web Crypto is required to digest an Action Manifest v2 contract");
1794
+ }
1795
+ const canonicalPayload = canonicalizeManifestV2Payload(payload);
1796
+ const digest2 = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(canonicalPayload));
1797
+ const hex = Array.from(new Uint8Array(digest2), (byte) => byte.toString(16).padStart(2, "0")).join("");
1798
+ return `sha256:${hex}`;
1799
+ }
1800
+ function signatureInput(digest2) {
1801
+ return new TextEncoder().encode(`${SIGNATURE_DOMAIN}${digest2}`);
1802
+ }
1803
+ function schemaFailure() {
1804
+ const details = (validateSignedManifest.errors || []).map((error) => `${error.instancePath || "/"} ${error.message || "is invalid"}`).join("; ");
1805
+ return new ActionManifestV2VerificationError("INVALID_MANIFEST", details ? `Action Manifest v2 is invalid: ${details}` : "Action Manifest v2 is invalid");
1806
+ }
1807
+ function payloadFromSignedManifest(manifest) {
1808
+ const { integrity: _integrity, ...payload } = manifest;
1809
+ return payload;
1810
+ }
1811
+ function assertContractIdentities(payload) {
1812
+ const contracts = [...payload.actions, ...payload.primitives];
1813
+ const canonicalTypes = /* @__PURE__ */ new Set();
1814
+ for (const contract of contracts) {
1815
+ if (canonicalTypes.has(contract.type)) {
1816
+ throw new ActionManifestV2VerificationError("DUPLICATE_CONTRACT_TYPE", `Manifest contains duplicate canonical contract type: ${contract.type}`);
1817
+ }
1818
+ canonicalTypes.add(contract.type);
1819
+ }
1820
+ for (const [groupName, group] of [
1821
+ ["actions", payload.actions],
1822
+ ["primitives", payload.primitives]
1823
+ ]) {
1824
+ for (let index = 1; index < group.length; index += 1) {
1825
+ if (group[index - 1].type > group[index].type) {
1826
+ throw new ActionManifestV2VerificationError("UNSORTED_CONTRACTS", `Manifest ${groupName} must be sorted by canonical type`);
1827
+ }
1828
+ }
1829
+ }
1830
+ const claimedNames = new Map(Array.from(canonicalTypes, (type) => [type, type]));
1831
+ for (const contract of contracts) {
1832
+ const aliases2 = contract.aliases;
1833
+ if (!Array.isArray(aliases2)) continue;
1834
+ for (const alias of aliases2) {
1835
+ const existingOwner = claimedNames.get(alias);
1836
+ if (existingOwner) {
1837
+ throw new ActionManifestV2VerificationError("AMBIGUOUS_ALIAS", `Manifest alias ${alias} conflicts with contract ${existingOwner}`);
1838
+ }
1839
+ claimedNames.set(alias, contract.type);
1840
+ }
1841
+ }
1842
+ }
1843
+ function deepFreeze(value) {
1844
+ for (const child of Object.values(value)) {
1845
+ if (typeof child === "object" && child !== null && !Object.isFrozen(child)) {
1846
+ deepFreeze(child);
1847
+ }
1848
+ }
1849
+ return Object.freeze(value);
1850
+ }
1851
+ async function signActionManifestV2(payload, signer) {
1852
+ if (signer.issuerDid !== payload.issuer.did || signer.verificationMethod !== payload.issuer.verificationMethod) {
1853
+ throw new ActionManifestV2VerificationError("SIGNER_MISMATCH", "The Manifest v2 signer does not match the declared issuer verification method");
1854
+ }
1855
+ assertContractIdentities(payload);
1856
+ let digest2;
1857
+ try {
1858
+ digest2 = await computeActionManifestV2Digest(payload);
1859
+ } catch (error) {
1860
+ throw new ActionManifestV2VerificationError(
1861
+ "INVALID_MANIFEST",
1862
+ error instanceof Error ? `Action Manifest v2 cannot be canonicalized: ${error.message}` : "Action Manifest v2 cannot be canonicalized"
1863
+ );
1864
+ }
1865
+ const signature = await signer.sign(signatureInput(digest2));
1866
+ if (!signature) {
1867
+ throw new ActionManifestV2VerificationError("INVALID_SIGNATURE", "The Manifest v2 signer returned an empty signature");
1868
+ }
1869
+ const signed = {
1870
+ ...payload,
1871
+ integrity: {
1872
+ canonicalization: "RFC8785",
1873
+ digest: {
1874
+ algorithm: "SHA-256",
1875
+ value: digest2
1876
+ },
1877
+ signature: {
1878
+ algorithm: signer.algorithm,
1879
+ verificationMethod: signer.verificationMethod,
1880
+ value: signature
1881
+ }
1882
+ }
1883
+ };
1884
+ if (!validateSignedManifest(signed)) {
1885
+ throw schemaFailure();
1886
+ }
1887
+ assertContractIdentities(payload);
1888
+ return signed;
1889
+ }
1890
+ async function verifyActionManifestV2(candidate, trustPolicy) {
1891
+ if (typeof candidate !== "object" || candidate === null || !("manifestVersion" in candidate) || candidate.manifestVersion !== "2.0") {
1892
+ throw new ActionManifestV2VerificationError("UNSUPPORTED_MANIFEST_VERSION", "Only Action Manifest version 2.0 can cross this execution boundary");
1893
+ }
1894
+ if (!("integrity" in candidate) || typeof candidate.integrity !== "object") {
1895
+ throw new ActionManifestV2VerificationError("UNSIGNED_MANIFEST", "A signature and digest are required to load Action Manifest v2 for execution");
1896
+ }
1897
+ if (!validateSignedManifest(candidate)) {
1898
+ throw schemaFailure();
1899
+ }
1900
+ const manifest = candidate;
1901
+ const { issuer, integrity } = manifest;
1902
+ assertContractIdentities(manifest);
1903
+ if (!trustPolicy.trustedIssuerDids.includes(issuer.did)) {
1904
+ throw new ActionManifestV2VerificationError("UNTRUSTED_ISSUER", `Manifest issuer is not trusted: ${issuer.did}`);
1905
+ }
1906
+ if (trustPolicy.revokedIssuerDids?.includes(issuer.did)) {
1907
+ throw new ActionManifestV2VerificationError("REVOKED_ISSUER", `Manifest issuer is revoked: ${issuer.did}`);
1908
+ }
1909
+ if (integrity.signature.verificationMethod !== issuer.verificationMethod || !integrity.signature.verificationMethod.startsWith(`${issuer.did}#`)) {
1910
+ throw new ActionManifestV2VerificationError("VERIFICATION_METHOD_MISMATCH", "The signature verification method does not belong to the declared manifest issuer");
1911
+ }
1912
+ if (!trustPolicy.allowedSignatureAlgorithms.includes(integrity.signature.algorithm)) {
1913
+ throw new ActionManifestV2VerificationError("DISALLOWED_SIGNATURE_ALGORITHM", `Manifest signature algorithm is not allowed: ${integrity.signature.algorithm}`);
1914
+ }
1915
+ let digest2;
1916
+ try {
1917
+ digest2 = await computeActionManifestV2Digest(payloadFromSignedManifest(manifest));
1918
+ } catch (error) {
1919
+ throw new ActionManifestV2VerificationError(
1920
+ "INVALID_MANIFEST",
1921
+ error instanceof Error ? `Action Manifest v2 cannot be canonicalized: ${error.message}` : "Action Manifest v2 cannot be canonicalized"
1922
+ );
1923
+ }
1924
+ if (digest2 !== integrity.digest.value) {
1925
+ throw new ActionManifestV2VerificationError("DIGEST_MISMATCH", "Manifest payload does not match its signed digest");
1926
+ }
1927
+ let validSignature = false;
1928
+ try {
1929
+ validSignature = await trustPolicy.verifySignature({
1930
+ issuerDid: issuer.did,
1931
+ verificationMethod: integrity.signature.verificationMethod,
1932
+ algorithm: integrity.signature.algorithm,
1933
+ data: signatureInput(digest2),
1934
+ signature: integrity.signature.value
1935
+ });
1936
+ } catch {
1937
+ validSignature = false;
1938
+ }
1939
+ if (!validSignature) {
1940
+ throw new ActionManifestV2VerificationError("INVALID_SIGNATURE", "Manifest signature verification failed");
1941
+ }
1942
+ const trustedSnapshot = JSON.parse(JSON.stringify(manifest));
1943
+ return deepFreeze(trustedSnapshot);
1944
+ }
1945
+
1604
1946
  // src/core/lib/actionRegistry/inputRequirements.ts
1605
1947
  function isBlankInputValue(value) {
1606
1948
  if (value == null) return true;
@@ -10498,7 +10840,7 @@ function stripHtml2(s) {
10498
10840
  }
10499
10841
 
10500
10842
  // src/core/lib/actionRegistry/actions/evalRubric/schemaGate.ts
10501
- import Ajv20202 from "ajv/dist/2020.js";
10843
+ import Ajv20203 from "ajv/dist/2020.js";
10502
10844
 
10503
10845
  // src/core/lib/actionRegistry/actions/evalRubric/types.ts
10504
10846
  var RUBRIC_CTX_TOKENS = [
@@ -10582,7 +10924,7 @@ async function getValidator(fetchSchema, evalEngineUrl) {
10582
10924
  if (compiledValidator) return compiledValidator;
10583
10925
  const schema = await fetchSchema(evalEngineUrl);
10584
10926
  if (!schema || typeof schema !== "object") throw new Error("the rules service returned no schema");
10585
- const validate = new Ajv20202({ allErrors: true, strict: false }).compile(schema);
10927
+ const validate = new Ajv20203({ allErrors: true, strict: false }).compile(schema);
10586
10928
  compiledValidator = validate;
10587
10929
  return validate;
10588
10930
  }
@@ -12756,6 +13098,7 @@ registerTopicOperation({
12756
13098
  executorPreference: { type: "string", enum: ["qi-flow", "qiforge", "mcp"] },
12757
13099
  bindingId: { type: "string" },
12758
13100
  confirmationPolicy: { type: "string", enum: ["inherit", "required"] },
13101
+ transitionCode: { type: "string" },
12759
13102
  idempotencyKey: { type: "string" }
12760
13103
  }
12761
13104
  },
@@ -12774,6 +13117,7 @@ registerTopicOperation({
12774
13117
  const inputDigest = requiredString(inputs.inputDigest, "inputDigest");
12775
13118
  const derived = (purpose) => sha256Digest({ purpose, parentRequestId: topic.requestId, topicId: topic.topicId, actionType: target.type, inputDigest });
12776
13119
  return {
13120
+ ...inputs.transitionCode ? { transitionCode: String(inputs.transitionCode) } : {},
12777
13121
  request: {
12778
13122
  version: 1,
12779
13123
  requestId: String(inputs.requestId || derived("topic-action-request")),
@@ -12839,9 +13183,15 @@ registerTopicOperation({
12839
13183
  ability: "topic/change-status",
12840
13184
  inputSchema: {
12841
13185
  type: "object",
12842
- required: ["from", "to", "reason"],
13186
+ required: ["from", "to", "transitionCode", "reason"],
12843
13187
  additionalProperties: false,
12844
- properties: { from: { type: "string" }, to: { type: "string" }, reason: { type: "string" }, idempotencyKey: { type: "string" } }
13188
+ properties: {
13189
+ from: { type: "string" },
13190
+ to: { type: "string" },
13191
+ transitionCode: { type: "string" },
13192
+ reason: { type: "string" },
13193
+ idempotencyKey: { type: "string" }
13194
+ }
12845
13195
  },
12846
13196
  buildPayload: async (inputs, ctx) => {
12847
13197
  const from = requiredString(inputs.from, "from");
@@ -12854,16 +13204,21 @@ registerTopicOperation({
12854
13204
  const outcome = projection.outcome;
12855
13205
  if (completion?.requiresOutcomeRecord === true && !outcome?.outcomeRecordId) throw new Error("Topic policy requires an accepted outcome record before resolution");
12856
13206
  }
12857
- return { from, to, reason: requiredString(inputs.reason, "reason") };
13207
+ return {
13208
+ from,
13209
+ to,
13210
+ transitionCode: requiredString(inputs.transitionCode, "transitionCode"),
13211
+ reason: requiredString(inputs.reason, "reason")
13212
+ };
12858
13213
  }
12859
13214
  });
12860
13215
  registerTopicOperation({
12861
- type: "qi/topic.contract.accept",
12862
- can: "topic/contract.accept",
12863
- operationType: "accept-contract",
13216
+ type: "qi/topic.contract.confirm-setup",
13217
+ can: "topic/contract.confirm-setup",
13218
+ operationType: "confirm-setup",
12864
13219
  confirmation: true,
12865
13220
  owner: "human",
12866
- ability: "topic/accept-contract",
13221
+ ability: "topic/confirm-setup",
12867
13222
  inputSchema: {
12868
13223
  type: "object",
12869
13224
  required: ["contractRevision", "contractDigest", "confirmationReference"],
@@ -12872,6 +13227,7 @@ registerTopicOperation({
12872
13227
  contractRevision: { type: "string" },
12873
13228
  contractDigest: { type: "string", pattern: "^sha256:" },
12874
13229
  confirmationReference: { type: "string" },
13230
+ transitionCode: { type: "string" },
12875
13231
  idempotencyKey: { type: "string" }
12876
13232
  }
12877
13233
  },
@@ -12879,8 +13235,13 @@ registerTopicOperation({
12879
13235
  const revision = requiredString(inputs.contractRevision, "contractRevision");
12880
13236
  const digest2 = requiredString(inputs.contractDigest, "contractDigest");
12881
13237
  if (revision !== topicContext(ctx).contract.revision || digest2 !== topicContext(ctx).contract.digest)
12882
- throw new Error("Contract acceptance must target the exact effective revision and digest");
12883
- return { contractRevision: revision, contractDigest: digest2, confirmationReference: requiredString(inputs.confirmationReference, "confirmationReference") };
13238
+ throw new Error("Setup confirmation must target the exact proposed contract revision and digest");
13239
+ return {
13240
+ contractRevision: revision,
13241
+ contractDigest: digest2,
13242
+ confirmationReference: requiredString(inputs.confirmationReference, "confirmationReference"),
13243
+ ...inputs.transitionCode ? { transitionCode: String(inputs.transitionCode) } : {}
13244
+ };
12884
13245
  }
12885
13246
  });
12886
13247
  registerTopicOperation({
@@ -12904,35 +13265,6 @@ registerTopicOperation({
12904
13265
  }
12905
13266
  })
12906
13267
  });
12907
- registerTopicOperation({
12908
- type: "qi/topic.outcome.confirm",
12909
- can: "topic/outcome.confirm",
12910
- operationType: "update-contract",
12911
- confirmation: true,
12912
- owner: "human",
12913
- ability: "topic/update-contract",
12914
- inputSchema: {
12915
- type: "object",
12916
- required: ["proposedOutcomeRecordId", "confirmationAuthorityDid", "confirmationReference"],
12917
- additionalProperties: false,
12918
- properties: {
12919
- proposedOutcomeRecordId: { type: "string" },
12920
- confirmationAuthorityDid: { type: "string", pattern: "^did:" },
12921
- confirmationReference: { type: "string" },
12922
- idempotencyKey: { type: "string" }
12923
- }
12924
- },
12925
- buildPayload: (inputs) => ({
12926
- patch: {
12927
- outcome: {
12928
- status: "achieved",
12929
- outcomeRecordId: requiredString(inputs.proposedOutcomeRecordId, "proposedOutcomeRecordId"),
12930
- confirmedBy: requiredString(inputs.confirmationAuthorityDid, "confirmationAuthorityDid"),
12931
- confirmationReference: requiredString(inputs.confirmationReference, "confirmationReference")
12932
- }
12933
- }
12934
- })
12935
- });
12936
13268
  registerTopicOperation({
12937
13269
  type: "qi/topic.decision.record",
12938
13270
  can: "topic/decision.record",
@@ -13341,6 +13673,60 @@ registerSemanticAction({
13341
13673
  occurredAt: String(inputs.occurredAt || (/* @__PURE__ */ new Date()).toISOString())
13342
13674
  })
13343
13675
  });
13676
+ registerAction({
13677
+ type: "qi/topic.remind",
13678
+ can: "topic/reminder.deliver",
13679
+ displayName: "Send a Topic reminder",
13680
+ description: "Posts a reminder into the Topic thread and notifies the people responsible for it.",
13681
+ sideEffect: true,
13682
+ proof: { fields: ["deliveredAt"] },
13683
+ done: doneWhenCompleted,
13684
+ defaultRequiresConfirmation: false,
13685
+ executionOwner: "agent",
13686
+ riskTier: "low",
13687
+ requiredServices: ["topic"],
13688
+ eligibleForTimeTrigger: true,
13689
+ scheduling: {
13690
+ riskTier: "R0",
13691
+ misfirePolicies: ["run_once_immediately", "skip"],
13692
+ // Reminders never queue: two firings of the same reminder are the same
13693
+ // reminder, and delivering it twice is the failure mode, not the goal.
13694
+ overlapPolicies: ["skip"],
13695
+ approvalRequirement: "none",
13696
+ idempotencyStrategy: "idempotency_key",
13697
+ inputModes: ["resolve_at_fire"],
13698
+ runModes: ["new_run_per_occurrence"]
13699
+ },
13700
+ topic: topicMetadata(ALL_KINDS2, [], ["topic/manage-reminder"], "contextual"),
13701
+ inputSchema: {
13702
+ type: "object",
13703
+ required: ["recipients"],
13704
+ additionalProperties: false,
13705
+ properties: {
13706
+ recipients: { type: "array", items: { type: "string" } },
13707
+ message: { type: "string" },
13708
+ idempotencyKey: { type: "string" }
13709
+ }
13710
+ },
13711
+ outputSchema: [
13712
+ { path: "deliveredAt", displayName: "Delivered at", type: "string" },
13713
+ { path: "recipients", displayName: "Reminded", type: "string" }
13714
+ ],
13715
+ run: async (inputs, ctx) => {
13716
+ const service = topicService(ctx);
13717
+ if (!service.deliverReminder) throw new Error("The host did not grant a Topic reminder delivery service");
13718
+ const recipients = requiredArray(inputs.recipients, "recipients");
13719
+ if (recipients.length === 0) throw new Error("A reminder needs at least one recipient");
13720
+ return {
13721
+ output: await service.deliverReminder({
13722
+ context: topicContext(ctx),
13723
+ recipients,
13724
+ message: String(inputs.message || ""),
13725
+ idempotencyKey: idempotencyKey("qi/topic.remind", inputs, ctx)
13726
+ })
13727
+ };
13728
+ }
13729
+ });
13344
13730
 
13345
13731
  // src/core/lib/actionRegistry/actions/calendar/eventCreate.types.ts
13346
13732
  var EMPTY = {
@@ -22462,6 +22848,12 @@ export {
22462
22848
  ACTION_REGISTRY_VERSION,
22463
22849
  generateActionManifest,
22464
22850
  actionManifestIssues,
22851
+ ACTION_MANIFEST_V2_SCHEMA,
22852
+ ActionManifestV2VerificationError,
22853
+ canonicalizeManifestV2Payload,
22854
+ computeActionManifestV2Digest,
22855
+ signActionManifestV2,
22856
+ verifyActionManifestV2,
22465
22857
  isBlankInputValue,
22466
22858
  getMissingActionInputs,
22467
22859
  SERVICE_VERBS,
@@ -22756,4 +23148,4 @@ export {
22756
23148
  executeQueuedFlowAgentCoreCommands,
22757
23149
  FlowAgentService
22758
23150
  };
22759
- //# sourceMappingURL=chunk-6OMM32CE.js.map
23151
+ //# sourceMappingURL=chunk-NEOPTPDF.js.map