chainlesschain 0.166.24 → 0.166.26
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 +52 -3
- package/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-DR-MWcZc.js → AIOps-Z8_dnLd8.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DT8B7sEb.js → ActionButton-C4bKCISV.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-asjh3dQJ.js → Analytics-GztAv2jy.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DivQ491i.js → AppLayout-DEqNcuVx.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-DPH2uUmy.js → Artifacts-D-R4Ehap.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-Bp2nLyc-.js → Audit-mzzdTIlM.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-CHS28khl.js → BackgroundAgents-DS2QupIC.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CKeaFSfz.js → Backup-CQSirC39.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CxwM9BAH.js → BaseInput-Bhuc-eOV.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DYLDwaCv.js → Chat-B-RLVGnt.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-DdBJDBP0.js → ChatBubbleRenderer-CBbMoVuE.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-uYTWOB8j.js → Checkbox-DxwgBryW.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CvYa_dwT.js → Codegen-Cwrm-7nh.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CanMAX4m.js → Col-DrSCT-nF.js} +1 -1
- package/src/assets/web-panel/assets/{Community-B4WaDtHw.js → Community-DY2D9WNs.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-K93Uof_3.js → Compact-o-tV0URw.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CKeDLgUW.js → Compliance-Bndh6S8f.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-P93grAeB.js → Cowork-CSNQODak.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-BSHnMtDn.js → Cron-CTgxRHdy.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-epAOpUVJ.js → Crosschain-BJoGQzDI.js} +1 -1
- package/src/assets/web-panel/assets/{DID-ByjXfP7t.js → DID-V0Ztm8ne.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-QztTU8tb.js → Dashboard-B-38THOj.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DhUc9yhN.js → Dropdown-B3FdOwMO.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-C2sLhJJN.js → EmailListRenderer-YQDz4mUF.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-CdfbWNDn.js → FamilyGuardDashboard-DYE45IF9.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CcF_R7NW.js → Federation-DjBFjdRZ.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-Cm_X2IBk.js → FormItemContext-CVTXEa0R.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DUi4NWlb.js → GenericCardRenderer-B6Tkke8X.js} +1 -1
- package/src/assets/web-panel/assets/{Git-4Sq5FjBt.js → Git-CD0hyAzT.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-Dx2_v4V6.js → Governance-CWOQQJr8.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-7Vr1xekR.js → Inference-lHYwUk-C.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Cnd2XOzf.js → KnowledgeGraph-Cledxv7t.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-B66QPVf-.js → Logs-D45mt-rM.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-PLsEvhly.js → MarkdownRenderer-DJjPaOnM.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-BMIK9BmC.js → Marketplace-Yy30UZPw.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BDbyp3J_.js → McpTools-Cil5l-bh.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-CJdzfZGi.js → Memory-DdJcnyqy.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-C2TH5Y3-.js → MobileBridge-IrzFYHEv.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-D1dYXkvV.js → MobileProjects-D8q64LMd.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-B-4A1I1j.js → Mtc-CpdTJzW7.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-Bgcc9UOl.js → MtcAudit-C7Tvw2m6.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-DqOe-etP.js → Multisig-Dmj9F5Kr.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-7VnhzE_6.js → NLProgramming-C9Xchob5.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BxawpeUQ.js → Notes-Bzq6Yry5.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-Cr08G2aE.js → NotificationSettings-DxG9eKb4.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-Ce8bEGlK.js → OrderTableRenderer-BS_IYoCe.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BC7lveGv.js → Organization-kazsRvcq.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CakOW-G0.js → Overflow-i4wP-hwn.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-Cf8CaOBZ.js → P2P-CfGeU3h-.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Dc7b98W9.js → PdhVaultBrowser-CS-VhVUx.js} +5 -5
- package/src/assets/web-panel/assets/{Permissions-DGJyMEfS.js → Permissions-DnEHXs0I.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DNIwSdwu.js → PersonalDataHub-DeD82qke.js} +6 -6
- package/src/assets/web-panel/assets/{Pipeline-5B1S12wa.js → Pipeline-CMGGLgBY.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CMB6Ke5t.js → Privacy-CvHoFv32.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-D5PqTFiT.js → ProjectInit-DnJapCc1.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CbKNdSjV.js → ProjectSettings-CqVdv3-g.js} +2 -2
- package/src/assets/web-panel/assets/Projects-B_o8vcs_.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CJF5_-Gs.js → Providers-CPKseoDW.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CT6iEAdi.js → QrScannerModal-_iBfKbPQ.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Ci0h9mJ_.js → QuickAsk-CMrlZ69a.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Da5mobZu.js → Recommend-BwDtSZ0l.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BaCY8eey.js → RemoteSession-DomUAtn4.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-CaITfTsI.js → Reputation-Cuu2GCHr.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DtUDamyP.js → Row-C7GVts31.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DX27gFpg.js → RssFeed-DRKvgEog.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DFL69osN.js → Search-BGI9wOeA.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BqGeoWv-.js → Security-BlLaHy_L.js} +3 -3
- package/src/assets/web-panel/assets/{Services-3z1WnEAc.js → Services-DFqZbjmX.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-C_OJQQ9Z.js → Skeleton-BCGxdooP.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BY7aT6Mo.js → Skills-CwFKgcmm.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BZv7DSaD.js → Sla-CiXXh9eS.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-WSx2CLqO.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-B6D7STlI.js → SyncSettings-BBIqQOtW.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Co_FCZAO.js → Tasks-D1BGLjO1.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CnY-9fgz.js → Templates-CUIl9axA.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-B71yXfZ8.js → Tenant-BFZJ0hNz.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-Dd_P7awo.js → Terminal-Cw-3eYVn.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-CvIEMyli.js → TimelineRenderer-bOVPB56L.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DknFBGOi.js → Tokens-Czfyq2VY.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-D_4nJONB.js → Trigger-C-o4uJY0.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BNl21fa7.js → Trust-BKq_S6iF.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CFrCI7uK.js → UkeySign-fKlWUvMx.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BVfY6JLg.js → VideoEditing-DB5MLuS7.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Cj9CAK0J.js → Wallet-WGXBn7IS.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DcqZlzA3.js → WebAuthn-B8TC4EMF.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-Dfr_dMld.js → WorkflowEditor-DLY--KmM.js} +1 -1
- package/src/assets/web-panel/assets/{chat-Yv6UF0AD.js → chat-Crw3XQaA.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CDW_-1fs.js → colors-CxhWauPV.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BYOQvd-6.js → compact-item-Cp3ojtk5.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BCGO0BiP.js → createContext-CRuRXjSo.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CKAQY2zn.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DY-70cfQ.js → hasIn-XGW474dj.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9ZogJJM.js → index-38TRdZb6.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBs2mv5M.js → index-B45Vkh3T.js} +1 -1
- package/src/assets/web-panel/assets/{index-CCwWp3mT.js → index-B5fJS-kn.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUo27q9J.js → index-BElgeKJ0.js} +3 -3
- package/src/assets/web-panel/assets/{index-CFa2fPCk.js → index-BUWZ57T4.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCtsjhhT.js → index-BVmSfIkJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-NaCESahy.js → index-BW028Pwn.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaT8trvf.js → index-BXnNbbwa.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRFC2Qt0.js → index-BYkjs7eP.js} +1 -1
- package/src/assets/web-panel/assets/{index-Br3wQLVY.js → index-BetOkRLs.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuIPq5MX.js → index-BuVA26SD.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4UJA2ju.js → index-C73pqyDb.js} +1 -1
- package/src/assets/web-panel/assets/{index-BvK4fjkk.js → index-C8UOVaUH.js} +1 -1
- package/src/assets/web-panel/assets/{index-hZ0Eyrrw.js → index-CDIpH_8Q.js} +1 -1
- package/src/assets/web-panel/assets/{index-D--TEyDj.js → index-CPFam-me.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUP-A2_h.js → index-CTE32qol.js} +1 -1
- package/src/assets/web-panel/assets/{index-CnZNbcux.js → index-CdlfrcC-.js} +1 -1
- package/src/assets/web-panel/assets/index-Cetfwq8j.js +1 -0
- package/src/assets/web-panel/assets/{index-C40UdtlS.js → index-Ctu98x57.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9m5QAMg.js → index-DBcBsJYg.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXlEx4UH.js → index-DHNixOg_.js} +1 -1
- package/src/assets/web-panel/assets/index-DOpcRO2-.js +1 -0
- package/src/assets/web-panel/assets/{index-Dg-AQYDf.js → index-DUSVsOGS.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzeEEazr.js → index-De2FN0xN.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUDoRk9r.js → index-DfPLIA-f.js} +1 -1
- package/src/assets/web-panel/assets/{index-Y9akzgFO.js → index-DhomnuaB.js} +1 -1
- package/src/assets/web-panel/assets/{index-366wfQBU.js → index-DoUC5_ry.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bb9nyts1.js → index-Dp2nC5On.js} +1 -1
- package/src/assets/web-panel/assets/{index-CIyHxV4u.js → index-DxOtBFVy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFv1hzF6.js → index-E-RSfImO.js} +1 -1
- package/src/assets/web-panel/assets/{index-DLOf7BSz.js → index-J_08R33H.js} +1 -1
- package/src/assets/web-panel/assets/{index-RjIMPG0u.js → index-QqAuH7-S.js} +1 -1
- package/src/assets/web-panel/assets/{index-CB_Iw73G.js → index-W8_0yAi_.js} +1 -1
- package/src/assets/web-panel/assets/{index-DM5c4pc-.js → index-WvNZXbIO.js} +1 -1
- package/src/assets/web-panel/assets/{index-DT_qVaaV.js → index-Yq3bFbYX.js} +1 -1
- package/src/assets/web-panel/assets/{index-NYVBUAVN.js → index-jrIcbPUa.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cy2NE6AO.js → index-nJR64pA_.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bf_Va0S-.js → index-qzmihx1d.js} +1 -1
- package/src/assets/web-panel/assets/{index-BayRTE5w.js → index-rsACoVT9.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CG99CcR4.js → initDefaultProps-DAu-hgme.js} +1 -1
- package/src/assets/web-panel/assets/{motion-Z7MH0_0G.js → motion-Db8yk0Hu.js} +1 -1
- package/src/assets/web-panel/assets/{move-BX67lOdq.js → move-Cii2jOin.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CmAbG1fE.js → mtc-parser-CnZBDkyZ.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DiU5tI1_.js → omit-Buzt1FTL.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BGfvLrYL.js → pickAttrs-CpZ_NmE6.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CggLEgaJ.js → placementArrow-mBrx3dYm.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-t3nlghPZ.js → responsiveObserve-DC_QzljB.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BkV4dL9s.js → slide-DT_UVED-.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-qec1PUoC.js → statusUtils-78t6eNLV.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-ChjX6T8D.js → styleChecker-CV_T1ULL.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-B1jLoZcM.js → useFlexGapSupport-s8edGuXD.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-C3X53s4E.js → useFs-Pk9dH2HC.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DOvbbnkZ.js → usePersonalDataHub-B_GqAm8c.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-Ie0TEVFT.js → vnode-Cxyss24q.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CRUMVS8l.js → zoom-0UY3cOuc.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/evolution-knowledge.js +40 -1
- package/src/commands/serve.js +11 -0
- package/src/data/changelog.json +29 -3
- package/src/generated/cc-agent-protocol.schema.json +3 -1
- package/src/lib/app-server/server.js +49 -0
- package/src/lib/app-server/websocket-transport.js +13 -0
- package/src/lib/evolution/agent-model-projection.js +252 -32
- package/src/lib/evolution/evidence-backed-wiki-maintainer.js +22 -9
- package/src/lib/evolution/evolution-artifact-ports.js +3 -0
- package/src/lib/evolution/evolution-deployment-loader.js +77 -0
- package/src/lib/evolution/evolution-evidence-projector.js +36 -5
- package/src/lib/evolution/evolution-ledger-ports.js +210 -18
- package/src/lib/evolution/governed-knowledge-dependency-inventory.js +392 -0
- package/src/lib/evolution/governed-knowledge-dependency-ledger-executor.js +123 -0
- package/src/lib/evolution/governed-knowledge-quarantine-release.js +1013 -0
- package/src/lib/evolution/governed-knowledge-revocation-host.js +59 -0
- package/src/lib/evolution/governed-knowledge-skill-rollback.js +62 -27
- package/src/lib/evolution/governed-knowledge-sync.js +164 -1
- package/src/lib/evolution/governed-knowledge-wiki-tombstone.js +88 -34
- package/src/lib/evolution/knowledge-wiki-source-admission.js +160 -1
- package/src/lib/evolution/knowledge-wiki-source-provenance.js +1 -0
- package/src/lib/evolution/skill-candidate-registry.js +21 -3
- package/src/lib/evolution/skill-release-registry.js +143 -0
- package/src/lib/evolution/wiki-maintainer-ledger-adapter.js +61 -0
- package/src/lib/evolution/wiki-target-tombstone-revision.js +15 -7
- package/src/lib/read-file-loop-guard.js +222 -0
- package/src/lib/read-file-page.js +55 -2
- package/src/repl/agent-repl.js +6 -1
- package/src/runtime/agent-core.js +177 -57
- package/src/assets/web-panel/assets/Projects-ccXaHzYL.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-6lJKLhCn.js +0 -1
- package/src/assets/web-panel/assets/devWarning-H-kXSuf-.js +0 -1
- package/src/assets/web-panel/assets/index-B0o4MeNK.js +0 -1
- package/src/assets/web-panel/assets/index-BVafOL_I.js +0 -1
|
@@ -8,11 +8,15 @@ import {
|
|
|
8
8
|
readRetainedWikiRevision,
|
|
9
9
|
createKnowledgeWikiSourceTracker,
|
|
10
10
|
} from "./knowledge-wiki-source-provenance.js";
|
|
11
|
+
import {
|
|
12
|
+
GOVERNED_KNOWLEDGE_QUARANTINE_RELEASE_EVENT_TYPE,
|
|
13
|
+
verifyGovernedKnowledgeQuarantineReleaseRecord,
|
|
14
|
+
} from "./governed-knowledge-quarantine-release.js";
|
|
11
15
|
|
|
12
16
|
export { WIKI_SOURCE_ADMISSION_INVALID_CODE } from "./knowledge-wiki-source-provenance.js";
|
|
13
17
|
export const WIKI_SOURCE_REVOKED_CODE = "CC_EVOLUTION_WIKI_SOURCE_REVOKED";
|
|
14
18
|
const WIKI_EVENT = "wiki.revision.committed";
|
|
15
|
-
const TERMINAL = new Set(["revoked", "tombstoned"]);
|
|
19
|
+
const TERMINAL = new Set(["quarantined", "revoked", "tombstoned"]);
|
|
16
20
|
|
|
17
21
|
function canonical(value) {
|
|
18
22
|
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
@@ -24,6 +28,121 @@ function canonical(value) {
|
|
|
24
28
|
}
|
|
25
29
|
const same = (a, b) => canonical(a) === canonical(b);
|
|
26
30
|
|
|
31
|
+
function quarantineReleaseFor({
|
|
32
|
+
events,
|
|
33
|
+
prepared,
|
|
34
|
+
descriptor,
|
|
35
|
+
resolveSubject,
|
|
36
|
+
fail,
|
|
37
|
+
}) {
|
|
38
|
+
const eventId = `${GOVERNED_KNOWLEDGE_QUARANTINE_RELEASE_EVENT_TYPE}.${prepared.operationDigest.slice(7)}`;
|
|
39
|
+
const matches = events.filter(
|
|
40
|
+
(event) =>
|
|
41
|
+
event.type === GOVERNED_KNOWLEDGE_QUARANTINE_RELEASE_EVENT_TYPE &&
|
|
42
|
+
event.eventId === eventId &&
|
|
43
|
+
event.tenantId === prepared.tenantId,
|
|
44
|
+
);
|
|
45
|
+
if (matches.length > 1) fail("quarantine release is ambiguous");
|
|
46
|
+
if (matches.length === 0) return null;
|
|
47
|
+
const event = matches[0];
|
|
48
|
+
const value = readWikiSourceRecord(
|
|
49
|
+
event,
|
|
50
|
+
"governed-knowledge-quarantine-release",
|
|
51
|
+
descriptor,
|
|
52
|
+
resolveSubject,
|
|
53
|
+
).value;
|
|
54
|
+
let release;
|
|
55
|
+
try {
|
|
56
|
+
release = verifyGovernedKnowledgeQuarantineReleaseRecord(value, {
|
|
57
|
+
tenantId: prepared.tenantId,
|
|
58
|
+
});
|
|
59
|
+
} catch {
|
|
60
|
+
fail("quarantine release record is invalid");
|
|
61
|
+
}
|
|
62
|
+
const quarantines = prepared.knowledge.dependencies.filter(
|
|
63
|
+
(dependency) => dependency.disposition === "quarantine",
|
|
64
|
+
);
|
|
65
|
+
if (
|
|
66
|
+
event.decision !== "committed" ||
|
|
67
|
+
event.timestamp !== release.committedAt ||
|
|
68
|
+
!Array.isArray(event.sourceRefs) ||
|
|
69
|
+
event.sourceRefs.length !== 0 ||
|
|
70
|
+
release.preparedRecordDigest !== prepared.recordDigest ||
|
|
71
|
+
release.knowledgeId !== prepared.knowledge.knowledgeId ||
|
|
72
|
+
release.contentDigest !== prepared.knowledge.contentDigest ||
|
|
73
|
+
!same(release.dependencies, quarantines)
|
|
74
|
+
) {
|
|
75
|
+
fail("quarantine release does not bind the prepared revocation");
|
|
76
|
+
}
|
|
77
|
+
return release;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function isReleasedWikiRestoration(
|
|
81
|
+
previous,
|
|
82
|
+
candidate,
|
|
83
|
+
patternId,
|
|
84
|
+
release,
|
|
85
|
+
releaseCoversRun,
|
|
86
|
+
baseline,
|
|
87
|
+
) {
|
|
88
|
+
if (
|
|
89
|
+
!release ||
|
|
90
|
+
!releaseCoversRun ||
|
|
91
|
+
!baseline ||
|
|
92
|
+
previous.patterns[patternId]?.status !== "quarantined" ||
|
|
93
|
+
!candidate.state.patterns[patternId] ||
|
|
94
|
+
TERMINAL.has(candidate.state.patterns[patternId].status) ||
|
|
95
|
+
!release.dependencies.some(
|
|
96
|
+
(dependency) =>
|
|
97
|
+
dependency.kind === "wiki" && dependency.disposition === "quarantine",
|
|
98
|
+
)
|
|
99
|
+
) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
const expectedPattern = {
|
|
103
|
+
...baseline.patterns[patternId],
|
|
104
|
+
updatedAt: release.committedAt,
|
|
105
|
+
};
|
|
106
|
+
const actualPattern = candidate.state.patterns[patternId];
|
|
107
|
+
const expectedIndex = [
|
|
108
|
+
{
|
|
109
|
+
patternId: expectedPattern.patternId,
|
|
110
|
+
kind: expectedPattern.kind,
|
|
111
|
+
status: expectedPattern.status,
|
|
112
|
+
summary: expectedPattern.summary,
|
|
113
|
+
confidence: expectedPattern.operationalConfidence,
|
|
114
|
+
actionable: expectedPattern.actionable,
|
|
115
|
+
skillNames: expectedPattern.skillNames,
|
|
116
|
+
},
|
|
117
|
+
];
|
|
118
|
+
const refs = [
|
|
119
|
+
...new Set([
|
|
120
|
+
...expectedPattern.positiveEvidence,
|
|
121
|
+
...expectedPattern.negativeEvidence,
|
|
122
|
+
]),
|
|
123
|
+
].sort();
|
|
124
|
+
if (
|
|
125
|
+
!same(actualPattern, expectedPattern) ||
|
|
126
|
+
!same(
|
|
127
|
+
candidate.state.index.filter((entry) => entry.patternId === patternId),
|
|
128
|
+
expectedIndex,
|
|
129
|
+
) ||
|
|
130
|
+
!same(
|
|
131
|
+
refs.map((ref) => candidate.state.evidence[ref]),
|
|
132
|
+
refs.map((ref) => baseline.evidence[ref]),
|
|
133
|
+
)
|
|
134
|
+
) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
return candidate.state.evolutionLog.some(
|
|
138
|
+
(entry) =>
|
|
139
|
+
entry.type === "pattern-quarantine-released" &&
|
|
140
|
+
entry.subjectId === patternId &&
|
|
141
|
+
entry.details?.operationDigest === release.operationDigest &&
|
|
142
|
+
entry.details?.releaseRecordDigest === release.recordDigest,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
27
146
|
// Only called with bytes resolved and digest-authenticated by the real Ledger
|
|
28
147
|
// while its write lock is held. There is no public success flag / writer port.
|
|
29
148
|
export function assertKnowledgeWikiSourceAdmission({
|
|
@@ -83,16 +202,44 @@ export function assertKnowledgeWikiSourceAdmission({
|
|
|
83
202
|
`${GOVERNED_KNOWLEDGE_DEPENDENCY_PREPARED_EVENT_TYPE}.${prepared.operationDigest.slice(7)}`
|
|
84
203
|
)
|
|
85
204
|
fail("revocation is not an exact durable prepared operation");
|
|
205
|
+
const release = quarantineReleaseFor({
|
|
206
|
+
events,
|
|
207
|
+
prepared,
|
|
208
|
+
descriptor,
|
|
209
|
+
resolveSubject,
|
|
210
|
+
fail,
|
|
211
|
+
});
|
|
86
212
|
const tracker = createKnowledgeWikiSourceTracker({
|
|
87
213
|
tenantId: input.tenantId,
|
|
88
214
|
knowledge: prepared.knowledge,
|
|
89
215
|
readWiki: wiki,
|
|
90
216
|
});
|
|
91
217
|
for (const entry of history) tracker.visit(entry, wiki(entry));
|
|
218
|
+
const releaseWikiDigests = new Set(
|
|
219
|
+
(release?.dependencies ?? [])
|
|
220
|
+
.filter(
|
|
221
|
+
(dependency) =>
|
|
222
|
+
dependency.kind === "wiki" &&
|
|
223
|
+
dependency.disposition === "quarantine",
|
|
224
|
+
)
|
|
225
|
+
.map((dependency) => dependency.digest),
|
|
226
|
+
);
|
|
227
|
+
const releaseCoversRun = history
|
|
228
|
+
.filter((event) => event.correlationId === input.correlationId)
|
|
229
|
+
.some((event) => releaseWikiDigests.has(wiki(event).stateDigest));
|
|
92
230
|
const { previous, unsafePatternIds } = tracker.visit(input, candidate, {
|
|
93
231
|
proposed: true,
|
|
94
232
|
});
|
|
95
233
|
for (const id of unsafePatternIds) {
|
|
234
|
+
let restorationBaseline = null;
|
|
235
|
+
for (const event of history) {
|
|
236
|
+
if (event.correlationId !== input.correlationId) continue;
|
|
237
|
+
const state = wiki(event).state;
|
|
238
|
+
if (state.patterns[id] && state.patterns[id].status !== "quarantined") {
|
|
239
|
+
restorationBaseline = state;
|
|
240
|
+
}
|
|
241
|
+
if (state.patterns[id]?.status === "quarantined") break;
|
|
242
|
+
}
|
|
96
243
|
const next = candidate.state.patterns[id];
|
|
97
244
|
// Permit removal/terminalization and unchanged pending cleanup batches.
|
|
98
245
|
if (!next || (TERMINAL.has(next.status) && next.actionable === false))
|
|
@@ -101,6 +248,18 @@ export function assertKnowledgeWikiSourceAdmission({
|
|
|
101
248
|
!previous.patterns[id] ||
|
|
102
249
|
!same(exposure(previous, id), exposure(candidate.state, id))
|
|
103
250
|
) {
|
|
251
|
+
if (
|
|
252
|
+
isReleasedWikiRestoration(
|
|
253
|
+
previous,
|
|
254
|
+
candidate,
|
|
255
|
+
id,
|
|
256
|
+
release,
|
|
257
|
+
releaseCoversRun,
|
|
258
|
+
restorationBaseline,
|
|
259
|
+
)
|
|
260
|
+
) {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
104
263
|
const error = new Error(
|
|
105
264
|
"Wiki source admission: revoked Knowledge cannot create, rewrite or reactivate a Wiki pattern",
|
|
106
265
|
);
|
|
@@ -27,6 +27,7 @@ const DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
|
27
27
|
const PATTERN = /^pat-[a-z0-9][a-z0-9-]{2,127}$/u;
|
|
28
28
|
const STATUSES = new Set(Object.values(WIKI_PATTERN_STATUS));
|
|
29
29
|
const TERMINAL = new Set([
|
|
30
|
+
WIKI_PATTERN_STATUS.QUARANTINED,
|
|
30
31
|
WIKI_PATTERN_STATUS.REVOKED,
|
|
31
32
|
WIKI_PATTERN_STATUS.TOMBSTONED,
|
|
32
33
|
]);
|
|
@@ -1596,6 +1596,9 @@ export class SkillCandidateRegistry {
|
|
|
1596
1596
|
Object.freeze({
|
|
1597
1597
|
tenantId,
|
|
1598
1598
|
read: Object.freeze(SkillCandidateRegistry.prototype.read.bind(this)),
|
|
1599
|
+
readInventory: Object.freeze(
|
|
1600
|
+
SkillCandidateRegistry.prototype.readInventory.bind(this),
|
|
1601
|
+
),
|
|
1599
1602
|
}),
|
|
1600
1603
|
);
|
|
1601
1604
|
}
|
|
@@ -2914,14 +2917,29 @@ export class SkillCandidateRegistry {
|
|
|
2914
2917
|
`limit must be an integer from 1 to ${MAX_LIST_LIMIT}`,
|
|
2915
2918
|
);
|
|
2916
2919
|
}
|
|
2917
|
-
|
|
2920
|
+
return Object.freeze(this.readInventory().slice(0, limit));
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2923
|
+
readInventory() {
|
|
2924
|
+
const before = this._assertNoMixedTenantArtifacts()
|
|
2918
2925
|
.filter((name) => CANDIDATE_FILE_PATTERN.test(name))
|
|
2919
2926
|
.sort();
|
|
2920
|
-
const candidates =
|
|
2927
|
+
const candidates = before.map((name) => {
|
|
2921
2928
|
const match = CANDIDATE_FILE_PATTERN.exec(name);
|
|
2922
2929
|
return this.read(`sha256:${match[1]}`);
|
|
2923
2930
|
});
|
|
2924
|
-
this.
|
|
2931
|
+
const after = this._assertNoMixedTenantArtifacts()
|
|
2932
|
+
.filter((name) => CANDIDATE_FILE_PATTERN.test(name))
|
|
2933
|
+
.sort();
|
|
2934
|
+
if (
|
|
2935
|
+
before.length !== after.length ||
|
|
2936
|
+
before.some((name, index) => name !== after[index])
|
|
2937
|
+
) {
|
|
2938
|
+
throw registryError(
|
|
2939
|
+
"SKILL_CANDIDATE_STORE_UNSAFE",
|
|
2940
|
+
"candidate tenant inventory changed during authenticated enumeration",
|
|
2941
|
+
);
|
|
2942
|
+
}
|
|
2925
2943
|
return Object.freeze(candidates);
|
|
2926
2944
|
}
|
|
2927
2945
|
}
|
|
@@ -87,6 +87,9 @@ const TENANT_KEY_PATTERN = /^[a-f0-9]{64}$/u;
|
|
|
87
87
|
const TEMP_PATTERN = /^\.(?:release|state|write)-[A-Za-z0-9._-]+\.tmp$/u;
|
|
88
88
|
const MAX_FILE_BYTES = 4 * 1024 * 1024;
|
|
89
89
|
const MAX_TENANT_MARKER_BYTES = 4096;
|
|
90
|
+
export const SKILL_RELEASE_INVENTORY_MAX_ENTRIES = 4_096;
|
|
91
|
+
export const SKILL_RELEASE_INVENTORY_MAX_BYTES = 32 * 1024 * 1024;
|
|
92
|
+
export const SKILL_RELEASE_INVENTORY_MAX_NODES = 1_000_000;
|
|
90
93
|
const MAX_CANONICAL_DEPTH = 32;
|
|
91
94
|
const MAX_CANONICAL_NODES = 100_000;
|
|
92
95
|
const MAX_CANONICAL_ARRAY_ENTRIES = 65_536;
|
|
@@ -2783,6 +2786,8 @@ export class SkillReleaseRegistry {
|
|
|
2783
2786
|
readActive: SkillReleaseRegistry.prototype.readActive.bind(this),
|
|
2784
2787
|
readState: SkillReleaseRegistry.prototype.readState.bind(this),
|
|
2785
2788
|
readRelease: SkillReleaseRegistry.prototype.readRelease.bind(this),
|
|
2789
|
+
readInventory:
|
|
2790
|
+
SkillReleaseRegistry.prototype.readInventory.bind(this),
|
|
2786
2791
|
matchesTransactionLedger: (value) => value === transactionLedger,
|
|
2787
2792
|
}),
|
|
2788
2793
|
);
|
|
@@ -3882,6 +3887,144 @@ export class SkillReleaseRegistry {
|
|
|
3882
3887
|
});
|
|
3883
3888
|
}
|
|
3884
3889
|
|
|
3890
|
+
#readInventoryNames(area, pattern) {
|
|
3891
|
+
const directory = this.#directories[area]?.path;
|
|
3892
|
+
if (!directory || typeof this.#fs.opendirSync !== "function") {
|
|
3893
|
+
throw failure(
|
|
3894
|
+
"SKILL_RELEASE_STORE_UNSAFE",
|
|
3895
|
+
"release inventory requires bounded directory enumeration",
|
|
3896
|
+
);
|
|
3897
|
+
}
|
|
3898
|
+
const names = [];
|
|
3899
|
+
let handle = null;
|
|
3900
|
+
let enumerationError = null;
|
|
3901
|
+
try {
|
|
3902
|
+
handle = this.#fs.opendirSync(directory);
|
|
3903
|
+
while (true) {
|
|
3904
|
+
const entry = handle.readSync();
|
|
3905
|
+
if (entry === null) break;
|
|
3906
|
+
if (
|
|
3907
|
+
!entry ||
|
|
3908
|
+
typeof entry.name !== "string" ||
|
|
3909
|
+
!pattern.test(entry.name)
|
|
3910
|
+
) {
|
|
3911
|
+
throw failure(
|
|
3912
|
+
"SKILL_RELEASE_STORE_UNSAFE",
|
|
3913
|
+
`release inventory found an unexpected ${area} entry`,
|
|
3914
|
+
);
|
|
3915
|
+
}
|
|
3916
|
+
if (names.length >= SKILL_RELEASE_INVENTORY_MAX_ENTRIES) {
|
|
3917
|
+
throw failure(
|
|
3918
|
+
"SKILL_RELEASE_STORE_LIMIT",
|
|
3919
|
+
`release inventory ${area} entry limit was exceeded`,
|
|
3920
|
+
);
|
|
3921
|
+
}
|
|
3922
|
+
names.push(entry.name);
|
|
3923
|
+
}
|
|
3924
|
+
} catch (cause) {
|
|
3925
|
+
enumerationError =
|
|
3926
|
+
cause instanceof SkillReleaseRegistryError
|
|
3927
|
+
? cause
|
|
3928
|
+
: failure(
|
|
3929
|
+
"SKILL_RELEASE_STORE_UNSAFE",
|
|
3930
|
+
`release inventory could not enumerate ${area} safely`,
|
|
3931
|
+
{ cause },
|
|
3932
|
+
);
|
|
3933
|
+
}
|
|
3934
|
+
if (handle !== null && typeof handle.closeSync === "function") {
|
|
3935
|
+
try {
|
|
3936
|
+
handle.closeSync();
|
|
3937
|
+
} catch (cause) {
|
|
3938
|
+
enumerationError = failure(
|
|
3939
|
+
"SKILL_RELEASE_STORE_UNSAFE",
|
|
3940
|
+
`release inventory could not close ${area} safely`,
|
|
3941
|
+
{ cause, enumerationError },
|
|
3942
|
+
);
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
if (enumerationError !== null) throw enumerationError;
|
|
3946
|
+
return names.sort();
|
|
3947
|
+
}
|
|
3948
|
+
|
|
3949
|
+
readInventory() {
|
|
3950
|
+
this.#assertBoundary();
|
|
3951
|
+
const releasePattern = /^[a-f0-9]{64}\.json$/u;
|
|
3952
|
+
const statePattern = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*\.json$/u;
|
|
3953
|
+
const beforeReleases = this.#readInventoryNames(
|
|
3954
|
+
"artifacts",
|
|
3955
|
+
releasePattern,
|
|
3956
|
+
);
|
|
3957
|
+
const beforeStates = this.#readInventoryNames("active", statePattern);
|
|
3958
|
+
if (
|
|
3959
|
+
beforeReleases.length + beforeStates.length >
|
|
3960
|
+
SKILL_RELEASE_INVENTORY_MAX_ENTRIES
|
|
3961
|
+
) {
|
|
3962
|
+
throw failure(
|
|
3963
|
+
"SKILL_RELEASE_STORE_LIMIT",
|
|
3964
|
+
"release inventory aggregate entry limit was exceeded",
|
|
3965
|
+
);
|
|
3966
|
+
}
|
|
3967
|
+
const releases = beforeReleases.map((name) =>
|
|
3968
|
+
this.readRelease(`sha256:${name.slice(0, -".json".length)}`),
|
|
3969
|
+
);
|
|
3970
|
+
const active = beforeStates.map((name) => {
|
|
3971
|
+
const skill = name.slice(0, -".json".length);
|
|
3972
|
+
const value = this.readActive(skill);
|
|
3973
|
+
if (value === null) {
|
|
3974
|
+
throw failure(
|
|
3975
|
+
"SKILL_RELEASE_STATE_CORRUPT",
|
|
3976
|
+
"persisted active state has no committed release",
|
|
3977
|
+
);
|
|
3978
|
+
}
|
|
3979
|
+
return deepFreeze({ skillName: skill, ...value });
|
|
3980
|
+
});
|
|
3981
|
+
let bytes = 0;
|
|
3982
|
+
let nodes = 0;
|
|
3983
|
+
const stack = [...releases, ...active.map(({ state }) => state)];
|
|
3984
|
+
for (const value of stack) {
|
|
3985
|
+
bytes += Buffer.byteLength(canonicalJson(value), "utf8");
|
|
3986
|
+
const pending = [value];
|
|
3987
|
+
while (pending.length > 0) {
|
|
3988
|
+
const node = pending.pop();
|
|
3989
|
+
nodes += 1;
|
|
3990
|
+
if (nodes > SKILL_RELEASE_INVENTORY_MAX_NODES) {
|
|
3991
|
+
throw failure(
|
|
3992
|
+
"SKILL_RELEASE_STORE_LIMIT",
|
|
3993
|
+
"release inventory aggregate node limit was exceeded",
|
|
3994
|
+
);
|
|
3995
|
+
}
|
|
3996
|
+
if (node !== null && typeof node === "object") {
|
|
3997
|
+
pending.push(...(Array.isArray(node) ? node : Object.values(node)));
|
|
3998
|
+
}
|
|
3999
|
+
}
|
|
4000
|
+
}
|
|
4001
|
+
if (bytes > SKILL_RELEASE_INVENTORY_MAX_BYTES) {
|
|
4002
|
+
throw failure(
|
|
4003
|
+
"SKILL_RELEASE_STORE_LIMIT",
|
|
4004
|
+
"release inventory aggregate byte limit was exceeded",
|
|
4005
|
+
);
|
|
4006
|
+
}
|
|
4007
|
+
const afterReleases = this.#readInventoryNames("artifacts", releasePattern);
|
|
4008
|
+
const afterStates = this.#readInventoryNames("active", statePattern);
|
|
4009
|
+
const afterStateDigests = afterStates.map((name) =>
|
|
4010
|
+
this.readState(name.slice(0, -".json".length)).stateDigest,
|
|
4011
|
+
);
|
|
4012
|
+
if (
|
|
4013
|
+
canonicalJson(beforeReleases) !== canonicalJson(afterReleases) ||
|
|
4014
|
+
canonicalJson(beforeStates) !== canonicalJson(afterStates) ||
|
|
4015
|
+
active.some(
|
|
4016
|
+
({ state }, index) => state.stateDigest !== afterStateDigests[index],
|
|
4017
|
+
)
|
|
4018
|
+
) {
|
|
4019
|
+
throw failure(
|
|
4020
|
+
"SKILL_RELEASE_STORE_UNSAFE",
|
|
4021
|
+
"release inventory changed during authenticated enumeration",
|
|
4022
|
+
);
|
|
4023
|
+
}
|
|
4024
|
+
this.#assertBoundary();
|
|
4025
|
+
return deepFreeze({ active, releases });
|
|
4026
|
+
}
|
|
4027
|
+
|
|
3885
4028
|
pinActive(name) {
|
|
3886
4029
|
const active = this.readActive(name);
|
|
3887
4030
|
if (!active)
|
|
@@ -162,6 +162,7 @@ export class WikiMaintainerLedgerAdapter {
|
|
|
162
162
|
descriptor: this.descriptor,
|
|
163
163
|
matchesLedger: (candidate) => candidate === ledger,
|
|
164
164
|
loadWiki: () => this.#loadWiki(),
|
|
165
|
+
readInventory: () => this.#readInventory(),
|
|
165
166
|
readRevision: (input) => this.#readRevision(input),
|
|
166
167
|
readKnowledgeProvenance: (input) =>
|
|
167
168
|
this.#readKnowledgeProvenance(input),
|
|
@@ -177,6 +178,7 @@ export class WikiMaintainerLedgerAdapter {
|
|
|
177
178
|
sourceDigest = null,
|
|
178
179
|
allowed = [],
|
|
179
180
|
checkpoint = null,
|
|
181
|
+
inventory = false,
|
|
180
182
|
revisionId = null,
|
|
181
183
|
stateRevisionDigest = null,
|
|
182
184
|
} = {}) {
|
|
@@ -231,12 +233,25 @@ export class WikiMaintainerLedgerAdapter {
|
|
|
231
233
|
event.tenantId === this.descriptor.tenantId &&
|
|
232
234
|
event.correlationId === this.descriptor.evolutionRunId,
|
|
233
235
|
);
|
|
236
|
+
const tenantWikiRunIds = [
|
|
237
|
+
...new Set(
|
|
238
|
+
events
|
|
239
|
+
.filter(
|
|
240
|
+
(event) =>
|
|
241
|
+
event.schema === EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA &&
|
|
242
|
+
event.type === WIKI_LEDGER_EVENT_TYPE &&
|
|
243
|
+
event.tenantId === this.descriptor.tenantId,
|
|
244
|
+
)
|
|
245
|
+
.map((event) => requiredString(event.correlationId, "correlationId")),
|
|
246
|
+
),
|
|
247
|
+
].sort();
|
|
234
248
|
let state = createEmptyWikiState(this.descriptor.tenantId);
|
|
235
249
|
let viewState = state;
|
|
236
250
|
let latest = null;
|
|
237
251
|
let selectedRevision = null;
|
|
238
252
|
let source = sourceDigest === digestWikiState(state) ? state : null;
|
|
239
253
|
const successors = [];
|
|
254
|
+
const revisions = [];
|
|
240
255
|
let retainedBytes = 0;
|
|
241
256
|
let batch = [];
|
|
242
257
|
for (const [index, event] of matches.entries()) {
|
|
@@ -301,6 +316,37 @@ export class WikiMaintainerLedgerAdapter {
|
|
|
301
316
|
);
|
|
302
317
|
}
|
|
303
318
|
verifyRequestTransition(state, revision);
|
|
319
|
+
if (inventory) {
|
|
320
|
+
const wikiSourceRevisionIds = [
|
|
321
|
+
...new Set(
|
|
322
|
+
Object.values(revision.state.evidence)
|
|
323
|
+
.flatMap((entry) => [entry.ref, entry.artifactRef])
|
|
324
|
+
.filter(
|
|
325
|
+
(ref) =>
|
|
326
|
+
typeof ref === "string" &&
|
|
327
|
+
ref.startsWith(`wiki-source://${this.descriptor.tenantId}/wiki:`),
|
|
328
|
+
)
|
|
329
|
+
.map((ref) => ref.slice(ref.lastIndexOf("/") + 1)),
|
|
330
|
+
),
|
|
331
|
+
].sort();
|
|
332
|
+
revisions.push(
|
|
333
|
+
freeze({
|
|
334
|
+
revision: revision.revision,
|
|
335
|
+
revisionId: revision.revisionId,
|
|
336
|
+
priorStateDigest: revision.priorStateDigest,
|
|
337
|
+
stateDigest: revision.stateDigest,
|
|
338
|
+
artifactRef: event.subjectRef,
|
|
339
|
+
wikiSourceRevisionIds,
|
|
340
|
+
checkpoint: {
|
|
341
|
+
epoch: event.epoch,
|
|
342
|
+
ledgerId: event.ledgerId,
|
|
343
|
+
identityDigest: event.identityDigest,
|
|
344
|
+
sequence: event.sequence,
|
|
345
|
+
headDigest: event.eventDigest,
|
|
346
|
+
},
|
|
347
|
+
}),
|
|
348
|
+
);
|
|
349
|
+
}
|
|
304
350
|
if (
|
|
305
351
|
revision.revisionId === revisionId ||
|
|
306
352
|
revision.stateDigest === stateRevisionDigest
|
|
@@ -380,9 +426,24 @@ export class WikiMaintainerLedgerAdapter {
|
|
|
380
426
|
source,
|
|
381
427
|
successors,
|
|
382
428
|
selectedRevision,
|
|
429
|
+
revisions,
|
|
430
|
+
tenantWikiRunIds,
|
|
383
431
|
};
|
|
384
432
|
}
|
|
385
433
|
|
|
434
|
+
#readInventory() {
|
|
435
|
+
const { head, revisions, tenantWikiRunIds } = this.#history({
|
|
436
|
+
inventory: true,
|
|
437
|
+
});
|
|
438
|
+
return freeze({
|
|
439
|
+
authenticated: true,
|
|
440
|
+
descriptor: this.descriptor,
|
|
441
|
+
ledgerHead: head,
|
|
442
|
+
revisions,
|
|
443
|
+
tenantWikiRunIds,
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
|
|
386
447
|
#readKnowledgeProvenance({
|
|
387
448
|
tenantId,
|
|
388
449
|
revisionId,
|
|
@@ -23,7 +23,7 @@ function freeze(value) {
|
|
|
23
23
|
// Pure derivation only; this function has no persistence or authorization port.
|
|
24
24
|
// Callers must authenticate the source and authorize the exact operations, then
|
|
25
25
|
// commit through the real adapter with the same authenticated head/sequence CAS.
|
|
26
|
-
export async function
|
|
26
|
+
export async function deriveWikiTargetDispositionRevision({
|
|
27
27
|
source,
|
|
28
28
|
descriptor,
|
|
29
29
|
operations,
|
|
@@ -34,15 +34,18 @@ export async function deriveWikiTargetTombstoneRevision({
|
|
|
34
34
|
!Array.isArray(operations) ||
|
|
35
35
|
operations.length < 1 ||
|
|
36
36
|
operations.length > 128 ||
|
|
37
|
-
operations.some(
|
|
37
|
+
operations.some(
|
|
38
|
+
(operation) => !["quarantine", "tombstone"].includes(operation.type),
|
|
39
|
+
) ||
|
|
40
|
+
new Set(operations.map((operation) => operation.type)).size !== 1
|
|
38
41
|
)
|
|
39
42
|
throw new TypeError(
|
|
40
|
-
"target-only Wiki derivation requires bounded
|
|
43
|
+
"target-only Wiki derivation requires one bounded disposition",
|
|
41
44
|
);
|
|
42
45
|
const ref = Object.keys(source.state.evidence ?? {}).sort()[0];
|
|
43
46
|
if (!ref)
|
|
44
47
|
throw new TypeError(
|
|
45
|
-
"Wiki
|
|
48
|
+
"Wiki dispositions require retained authenticated evidence metadata",
|
|
46
49
|
);
|
|
47
50
|
const core = source.state.evidence[ref];
|
|
48
51
|
const evidence = { ...core, envelopeDigest: digestWikiState(core) };
|
|
@@ -76,9 +79,7 @@ export async function deriveWikiTargetTombstoneRevision({
|
|
|
76
79
|
},
|
|
77
80
|
});
|
|
78
81
|
if (!revision)
|
|
79
|
-
throw new Error(
|
|
80
|
-
"Wiki tombstone derivation unexpectedly reused an existing request",
|
|
81
|
-
);
|
|
82
|
+
throw new Error("Wiki disposition unexpectedly reused an existing request");
|
|
82
83
|
// General maintenance also recalculates unrelated patterns. A targeted
|
|
83
84
|
// revocation has no authority to change their lifecycle, confidence or facts.
|
|
84
85
|
const scoped = structuredClone(source.state);
|
|
@@ -99,3 +100,10 @@ export async function deriveWikiTargetTombstoneRevision({
|
|
|
99
100
|
stateDigest: digestWikiState(scoped),
|
|
100
101
|
});
|
|
101
102
|
}
|
|
103
|
+
|
|
104
|
+
export async function deriveWikiTargetTombstoneRevision(input) {
|
|
105
|
+
if (input?.operations?.some((operation) => operation.type !== "tombstone")) {
|
|
106
|
+
throw new TypeError("target-only Wiki derivation requires tombstones");
|
|
107
|
+
}
|
|
108
|
+
return deriveWikiTargetDispositionRevision(input);
|
|
109
|
+
}
|