chainlesschain 0.166.141 → 0.166.143

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/README.md +249 -14
  2. package/package.json +2 -2
  3. package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
  4. package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
  5. package/src/assets/web-panel/assets/{AIOps-BefmcZw2.js → AIOps-DNySV2Kf.js} +1 -1
  6. package/src/assets/web-panel/assets/{ActionButton-BBlS9rmj.js → ActionButton-ByFuxJsa.js} +1 -1
  7. package/src/assets/web-panel/assets/{Analytics-DfNjXT9e.js → Analytics-ETbdywk5.js} +1 -1
  8. package/src/assets/web-panel/assets/{AppLayout-CIBZPPMS.js → AppLayout-CuHekZaP.js} +2 -2
  9. package/src/assets/web-panel/assets/{Artifacts-CE66rTTZ.js → Artifacts-BJJq2BDq.js} +1 -1
  10. package/src/assets/web-panel/assets/{Audit-DOC8Lt8U.js → Audit-DXrietv8.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-9TMG041Q.js → BackgroundAgents-CFB1fxsr.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-us3KWb9F.js → Backup-VLtflRmp.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-D6Q95Wr5.js → BaseInput-BbqBaS2r.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-BPrM5ARp.js → Chat-t0g7BXL8.js} +1 -1
  15. package/src/assets/web-panel/assets/{ChatBubbleRenderer-Drs9vSc4.js → ChatBubbleRenderer-JNFsjV0j.js} +1 -1
  16. package/src/assets/web-panel/assets/{Checkbox-DqZGMV0o.js → Checkbox-BCSclHQb.js} +1 -1
  17. package/src/assets/web-panel/assets/{Codegen-CmolL6wr.js → Codegen-BcyINEHT.js} +1 -1
  18. package/src/assets/web-panel/assets/{Col-BwHSmoYQ.js → Col-C-0WNlwS.js} +1 -1
  19. package/src/assets/web-panel/assets/{Community-DGWqobpd.js → Community-DxZeoORX.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compact-t_yy09-r.js → Compact-BNnX4jpz.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compliance-D0S8-EsN.js → Compliance-KnjHtn9C.js} +1 -1
  22. package/src/assets/web-panel/assets/{Cowork-Y1LiuRwP.js → Cowork-CDyCrhbX.js} +1 -1
  23. package/src/assets/web-panel/assets/{Cron-FTqEw7QV.js → Cron-DxpS3D1G.js} +1 -1
  24. package/src/assets/web-panel/assets/{Crosschain-C3rPZ4Qu.js → Crosschain-BSzTZYyc.js} +1 -1
  25. package/src/assets/web-panel/assets/{DID-Ba2zySM-.js → DID-Bfj9Vdne.js} +1 -1
  26. package/src/assets/web-panel/assets/{Dashboard-D6xyN2ND.js → Dashboard-CcERVdgo.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dropdown-BF4QuXJw.js → Dropdown-BtYXAz6E.js} +1 -1
  28. package/src/assets/web-panel/assets/{EmailListRenderer-s9dUxq14.js → EmailListRenderer-fdCMeGXU.js} +1 -1
  29. package/src/assets/web-panel/assets/{EvolutionSettings-CoAKOfqZ.js → EvolutionSettings-LHxhcuF2.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CEznwH1c.js → FamilyGuardDashboard-MIgtLsos.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-rIzOt8-a.js → Federation-9-TOztwa.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-BARFiExr.js → FormItemContext-BKcwEE6w.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-DI0A4swS.js → GenericCardRenderer-DJBPVUK2.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-DdmysND4.js → Git-C_XYISjG.js} +1 -1
  35. package/src/assets/web-panel/assets/{Governance-DrdhwUz2.js → Governance-DWsbPrwq.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-Scm8tumd.js → Inference-D0_bYket.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-_6bgPs4W.js → KnowledgeGraph-BoA3VQlw.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-DW_v7bSo.js → Logs-LEGItChf.js} +1 -1
  39. package/src/assets/web-panel/assets/{MarkdownRenderer-7JABuv1j.js → MarkdownRenderer-DOHVc-Uw.js} +1 -1
  40. package/src/assets/web-panel/assets/{Marketplace-0h3YSvME.js → Marketplace-BKovl9NH.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-Byrg0Y3e.js → McpTools-D61ktoGb.js} +1 -1
  42. package/src/assets/web-panel/assets/{Memory-Cyoq-o29.js → Memory-CllorS0l.js} +1 -1
  43. package/src/assets/web-panel/assets/{MobileBridge-ISIlploU.js → MobileBridge-D-dJ78bk.js} +1 -1
  44. package/src/assets/web-panel/assets/{MobileProjects-04EncaRx.js → MobileProjects-CaxMKkd3.js} +1 -1
  45. package/src/assets/web-panel/assets/{Mtc-BMzkpyOF.js → Mtc-qEc62Qsx.js} +1 -1
  46. package/src/assets/web-panel/assets/{MtcAudit-Cx7BR5sz.js → MtcAudit-BY68jOZJ.js} +1 -1
  47. package/src/assets/web-panel/assets/{Multisig-D4pmkSX4.js → Multisig-eCCR8ELt.js} +1 -1
  48. package/src/assets/web-panel/assets/{NLProgramming-CCfaaWNu.js → NLProgramming-VtGv8pky.js} +1 -1
  49. package/src/assets/web-panel/assets/{Notes-Mq-1ZyaH.js → Notes-CPkYQySe.js} +1 -1
  50. package/src/assets/web-panel/assets/{NotificationSettings-rzf5V1TR.js → NotificationSettings-BuAvlHUu.js} +1 -1
  51. package/src/assets/web-panel/assets/{OrderTableRenderer-OREUs5ue.js → OrderTableRenderer-CLf_HAGv.js} +1 -1
  52. package/src/assets/web-panel/assets/{Organization-BQfOATgw.js → Organization-DjujhMbD.js} +1 -1
  53. package/src/assets/web-panel/assets/{Overflow-D7Pa6aaA.js → Overflow-RznznTS4.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-CaJ79ZsC.js → P2P-DSa5ot0t.js} +1 -1
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-CtimQQN3.js → PdhVaultBrowser-oSEh6vkf.js} +2 -2
  56. package/src/assets/web-panel/assets/{Permissions-AzmmkLZ0.js → Permissions-CdOZguX9.js} +1 -1
  57. package/src/assets/web-panel/assets/{PersonalDataHub-BRU8ME7z.js → PersonalDataHub-B4--6iEF.js} +1 -1
  58. package/src/assets/web-panel/assets/{Pipeline-BnNqutXh.js → Pipeline-BnI57jE8.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-gMyZcxsz.js → Privacy-C8MId3ad.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-CkK-Fvr0.js → ProjectInit-DPGyYO8a.js} +1 -1
  61. package/src/assets/web-panel/assets/{ProjectSettings-CGY_uiIX.js → ProjectSettings-qBMqaJaO.js} +1 -1
  62. package/src/assets/web-panel/assets/{Projects-DBNC7xXG.js → Projects-Bq87Syce.js} +1 -1
  63. package/src/assets/web-panel/assets/{Providers-BVgWawtE.js → Providers-CVuG9Ugf.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-DZILESCa.js → QrScannerModal-C94c8m3q.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-DwRBzFWW.js → QuickAsk-BvfsXZ8s.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-B3VxvUih.js → Recommend-CoezBYH9.js} +1 -1
  67. package/src/assets/web-panel/assets/{RemoteSession-DM74lufU.js → RemoteSession-CcYC4v3u.js} +1 -1
  68. package/src/assets/web-panel/assets/{Reputation-DOOEuGHh.js → Reputation-diDbL4cO.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-BZJYAo0S.js → Row-5qp3_fNS.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-CYw44jcD.js → RssFeed-BCsMvpV4.js} +1 -1
  71. package/src/assets/web-panel/assets/{Search-CbypjDNG.js → Search-Br9nVxOp.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-0rSBDrXh.js → Security-BdOUzRfA.js} +1 -1
  73. package/src/assets/web-panel/assets/{Services-kqP79CBD.js → Services-Cp9TqzyN.js} +1 -1
  74. package/src/assets/web-panel/assets/{Skeleton-BjxvGVfI.js → Skeleton-DSdWsUMR.js} +1 -1
  75. package/src/assets/web-panel/assets/{Skills-SOoWyPCO.js → Skills-DRczAMTe.js} +1 -1
  76. package/src/assets/web-panel/assets/{Sla-kdRVAT19.js → Sla-B6G_Vd8N.js} +1 -1
  77. package/src/assets/web-panel/assets/{SpeechSettings-DILtcMaJ.js → SpeechSettings-BCcLx5x4.js} +1 -1
  78. package/src/assets/web-panel/assets/{SyncSettings-C98Jauc4.js → SyncSettings-BFhwtxCp.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tasks-Bw-KaQAs.js → Tasks-DGRE6VHO.js} +1 -1
  80. package/src/assets/web-panel/assets/{Templates-CDJ027Hd.js → Templates-CJkJRFzd.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-BqNV62UA.js → Tenant-CAOv6p00.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-B8aQ3GLO.js → Terminal-CDrwwrKj.js} +1 -1
  83. package/src/assets/web-panel/assets/{TimelineRenderer-BSpccq11.js → TimelineRenderer-DI0YE2SN.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-BbGMoFO6.js → Tokens-DmqWTZSG.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-D75Mawa_.js → Trigger-CkXkhmjk.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-BiK_Pk05.js → Trust-U8YJLcoJ.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-Dv2xX_b9.js → UkeySign-BLTozMmr.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-CdBWfeEE.js → VideoEditing-DHKyFlZa.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-Cvrp4agg.js → Wallet-DapBxIQt.js} +1 -1
  90. package/src/assets/web-panel/assets/{WebAuthn-D0Htlf3v.js → WebAuthn-CkvG3yOr.js} +1 -1
  91. package/src/assets/web-panel/assets/{WorkflowEditor-nuUBOHIF.js → WorkflowEditor-cNLsinXP.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-C6MLShIo.js → chat-BzgkgfMC.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-9G5FqFOC.js → colors-BPpVKQsb.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-0M49EurU.js → compact-item-zepznuTv.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-CiDDLED_.js → createContext-BRto-SQH.js} +1 -1
  96. package/src/assets/web-panel/assets/{hasIn-wZvUczmn.js → hasIn-CqsmU_n6.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-B0JZ_ccM.js → index-B-bNzvdU.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-B_XOMujL.js → index-B0Z6rfV-.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-DwX9cOL6.js → index-B9_jRD2e.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DwUOiN8M.js → index-BEXKES7V.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-8jz-X3xg.js → index-BGHd4D8F.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BVV2mBbo.js → index-BLwY9y0X.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-qJcOtd8J.js → index-B_l6gfDh.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-B6TAOS4G.js → index-BeBxP1M6.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-C63qiwQz.js → index-BeSzdB7x.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-k0BVfcwQ.js → index-BuBnM6Kz.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BXIY2BIN.js → index-Bz7pG8z3.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-D51V5vmz.js → index-CBI-7Q3W.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BWTcG128.js → index-CTk9gY88.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-sBsJaBmM.js → index-CdHXVSZ4.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Dclwd_lj.js → index-CmPnDXx4.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BlcOkfzz.js → index-CnezE05k.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DOxTmc3k.js → index-CwkhrUmw.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Cs66y4oC.js → index-CzoKAiyT.js} +3 -3
  115. package/src/assets/web-panel/assets/{index-BzeA3eRQ.js → index-DDPmr5Yr.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CdaLWIz-.js → index-DEbAW3Ec.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DvWRS-s7.js → index-DJSn3owP.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DN9t1qjW.js → index-DKa_jSfj.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BTCIIdoS.js → index-DUclk35X.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BmS2P_eX.js → index-DXkw-nt5.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CoxzvhoN.js → index-D_-w4KkD.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-Cz1SAgnb.js → index-D_asIcU7.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BfwJm0DU.js → index-Df_CY0We.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-JhfNEldK.js → index-Dnvj_XU-.js} +1 -1
  125. package/src/assets/web-panel/assets/index-DruOFzuF.js +1 -0
  126. package/src/assets/web-panel/assets/{index-D9J_tLGk.js → index-DvGttIXQ.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DcZxOXd8.js → index-JrVhUwyh.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CdUmgLxP.js → index-LVvzNdPa.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-CTy9gD3m.js → index-Q5sAV7wp.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CtbFv3Et.js → index-S8fkzbQ_.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BCfxxScC.js → index-Z6DBe2RU.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-dJ_V_Fqy.js → index-faHHtBKj.js} +1 -1
  133. package/src/assets/web-panel/assets/index-hhOrZ6HO.js +1 -0
  134. package/src/assets/web-panel/assets/{index-DeHSwWVG.js → index-w36TkwyO.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-D2wN6VIq.js → index-wbvZayBb.js} +1 -1
  136. package/src/assets/web-panel/assets/{initDefaultProps-DyIyjurJ.js → initDefaultProps-BlhbrsQk.js} +1 -1
  137. package/src/assets/web-panel/assets/{motion-B2D0PuTM.js → motion-UZx12NPM.js} +1 -1
  138. package/src/assets/web-panel/assets/{move-B22T5MnN.js → move-BTby5W4q.js} +1 -1
  139. package/src/assets/web-panel/assets/{mtc-parser-i4b7HNzF.js → mtc-parser-BOOaGiCg.js} +1 -1
  140. package/src/assets/web-panel/assets/{omit-ByNIr_gz.js → omit-BVylGzEr.js} +1 -1
  141. package/src/assets/web-panel/assets/{pickAttrs-_xP2RvxA.js → pickAttrs-Cp3Nflbr.js} +1 -1
  142. package/src/assets/web-panel/assets/{placementArrow-CVDvzK0z.js → placementArrow-BVXYvwH2.js} +1 -1
  143. package/src/assets/web-panel/assets/{responsiveObserve-CI1toXJ_.js → responsiveObserve-C67SbpBN.js} +1 -1
  144. package/src/assets/web-panel/assets/{slide-2_VosSRQ.js → slide-CnBvM22R.js} +1 -1
  145. package/src/assets/web-panel/assets/{statusUtils-CMiWWrZC.js → statusUtils-T8fy53tf.js} +1 -1
  146. package/src/assets/web-panel/assets/{styleChecker-DY8wTVCe.js → styleChecker-Bf7i4nlm.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFlexGapSupport-DkdcgIyl.js → useFlexGapSupport-BUKk7jqw.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFs-gpl11PEF.js → useFs-T5lg6SpD.js} +1 -1
  149. package/src/assets/web-panel/assets/{usePersonalDataHub-DqnQ0H9y.js → usePersonalDataHub-Brvye8Tg.js} +1 -1
  150. package/src/assets/web-panel/assets/{vnode-CgY9xUvr.js → vnode-CK992ojP.js} +1 -1
  151. package/src/assets/web-panel/assets/{zoom-Ch0jan5_.js → zoom-CcABB_97.js} +1 -1
  152. package/src/assets/web-panel/index.html +1 -1
  153. package/src/command-help-index.json +1 -1
  154. package/src/commands/goal.js +340 -145
  155. package/src/commands/setup.js +17 -0
  156. package/src/data/changelog.json +26 -0
  157. package/src/lib/llm-onboarding-catalog.json +4 -3
  158. package/src/lib/plugin-runtime/cli-goal-capabilities.js +29 -0
  159. package/src/lib/plugin-runtime/cli-goal-draft-request.js +25 -15
  160. package/src/lib/plugin-runtime/cli-goal-drafts.js +78 -35
  161. package/src/lib/plugin-runtime/cli-goal-event-history.js +284 -0
  162. package/src/lib/plugin-runtime/cli-goal-events.js +175 -0
  163. package/src/lib/plugin-runtime/cli-goal-extension-host.js +21 -13
  164. package/src/lib/plugin-runtime/cli-project-goal-events.js +212 -0
  165. package/src/lib/plugin-runtime/examples/goal-project-observer/plugin.json +20 -0
  166. package/src/lib/plugin-runtime/examples/goal-status-observer/plugin.json +17 -0
  167. package/src/lib/plugin-runtime/examples/goal-task-creator/README.md +60 -0
  168. package/src/lib/plugin-runtime/examples/goal-task-creator/plugin.json +13 -0
  169. package/src/lib/plugin-runtime/examples/project-delivery-assistant/README.md +125 -0
  170. package/src/lib/plugin-runtime/examples/project-delivery-assistant/plugin.json +8 -8
  171. package/src/lib/plugin-runtime/goal-extensions.js +17 -0
  172. package/src/lib/project-goal-connection.js +126 -30
  173. package/src/lib/project-goal-draft-connection.js +18 -11
  174. package/src/assets/web-panel/assets/index-C101Sec6.js +0 -1
  175. package/src/assets/web-panel/assets/index-D23TaGOp.js +0 -1
@@ -0,0 +1,29 @@
1
+ /** Implemented CLI surfaces; source leases and per-call activation still gate use. */
2
+ import { VERSION } from "../../constants.js";
3
+
4
+ export const CLI_GOAL_CAPABILITIES = Object.freeze([
5
+ "event:goal.status.changed",
6
+ "event:risk.review.completed",
7
+ "event:action.receipt.updated",
8
+ "panel:goal.summary",
9
+ "panel:goal.evidence",
10
+ "proposal:task.description.update",
11
+ "proposal:task.create",
12
+ ]);
13
+
14
+ export function cliGoalExtensionHost(activeCapabilities) {
15
+ if (
16
+ !Array.isArray(activeCapabilities) ||
17
+ activeCapabilities.some(
18
+ (value) => !CLI_GOAL_CAPABILITIES.includes(value),
19
+ ) ||
20
+ new Set(activeCapabilities).size !== activeCapabilities.length
21
+ )
22
+ throw new Error("CLI_GOAL_HOST_CAPABILITIES_INVALID");
23
+ return Object.freeze({
24
+ id: "cli",
25
+ version: VERSION,
26
+ capabilities: CLI_GOAL_CAPABILITIES,
27
+ activeCapabilities: Object.freeze([...activeCapabilities]),
28
+ });
29
+ }
@@ -1,15 +1,13 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { VERSION } from "../../constants.js";
3
+ import { cliGoalExtensionHost } from "./cli-goal-capabilities.js";
4
4
  import { createProjectGoalConnection } from "../project-goal-connection.js";
5
5
  import { collectGoalExtensions } from "./goal-extensions.js";
6
- import { createGoalDraftRequest } from "@chainlesschain/session-core/goal-draft-request-contract";
6
+ import {
7
+ createGoalDraftRequest,
8
+ createGoalTaskCreateRequest,
9
+ } from "@chainlesschain/session-core/goal-draft-request-contract";
7
10
  import { digestBusinessObjectContent as digest } from "@chainlesschain/session-core/business-object-contract";
8
- const HOST = Object.freeze({
9
- id: "cli",
10
- version: VERSION,
11
- capabilities: Object.freeze(["proposal:task.description.update"]),
12
- });
13
11
  function fail(code) {
14
12
  throw new Error(code);
15
13
  }
@@ -53,12 +51,19 @@ export function readGoalDraftDescription(file) {
53
51
  }
54
52
  }
55
53
  export async function exportCliGoalDraftRequest(id, options = {}) {
54
+ return exportRequest(id, options, false);
55
+ }
56
+ export async function exportCliGoalTaskCreateRequest(id, options = {}) {
57
+ return exportRequest(id, options, true);
58
+ }
59
+ async function exportRequest(id, options, creation) {
56
60
  options = Object.freeze({
57
61
  ...options,
58
62
  env: Object.freeze({ ...(options.env || process.env) }),
59
63
  });
60
64
  const cwd = path.resolve(options.cwd || process.cwd()),
61
65
  env = Object.freeze({ ...(options.env || process.env) });
66
+ const type = creation ? "task.create" : "task.description.update";
62
67
  if (
63
68
  !options.connection ||
64
69
  !options.plugin ||
@@ -77,7 +82,7 @@ export async function exportCliGoalDraftRequest(id, options = {}) {
77
82
  const hostOptions = {
78
83
  cwd,
79
84
  env,
80
- host: HOST,
85
+ host: cliGoalExtensionHost([`proposal:${type}`]),
81
86
  managedSettingsFile: options.managedSettingsFile,
82
87
  };
83
88
  const admitted = () =>
@@ -85,12 +90,11 @@ export async function exportCliGoalDraftRequest(id, options = {}) {
85
90
  (entry) => entry.name === options.plugin && entry.scope === options.scope,
86
91
  );
87
92
  const plugin = admitted();
88
- if (
89
- !plugin ||
90
- !plugin.declaration.proposals.includes("task.description.update")
91
- )
93
+ if (!plugin || !plugin.declaration.proposals.includes(type))
92
94
  fail("GOAL_DRAFT_PLUGIN_NOT_ADMITTED");
93
- const request = createGoalDraftRequest({
95
+ const request = (
96
+ creation ? createGoalTaskCreateRequest : createGoalDraftRequest
97
+ )({
94
98
  declaredPlugin: {
95
99
  name: plugin.name,
96
100
  scope: plugin.scope,
@@ -98,7 +102,8 @@ export async function exportCliGoalDraftRequest(id, options = {}) {
98
102
  },
99
103
  declarationDigest: digest(plugin.declaration),
100
104
  proposal: {
101
- type: "task.description.update",
105
+ type,
106
+ ...(creation ? { taskType: options.taskType } : {}),
102
107
  goalRef: snapshot.goalRef,
103
108
  sourceRef: options.source,
104
109
  requestId: options.requestId,
@@ -127,12 +132,17 @@ export async function exportCliGoalDraftRequest(id, options = {}) {
127
132
  fs.closeSync(fd);
128
133
  }
129
134
  return {
130
- schema: "chainlesschain.goal-draft-export/v1",
135
+ schema: creation
136
+ ? "chainlesschain.goal-task-create-export/v1"
137
+ : "chainlesschain.goal-draft-export/v1",
131
138
  state: "request-exported",
132
139
  goalRef: request.proposal.goalRef,
133
140
  declaredPlugin: request.declaredPlugin,
134
141
  requestId: request.proposal.requestId,
135
142
  contentDigest: request.contentDigest,
136
143
  file,
144
+ ...(creation
145
+ ? { type: "task.create", taskType: request.proposal.taskType }
146
+ : {}),
137
147
  };
138
148
  }
@@ -1,6 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { VERSION } from "../../constants.js";
3
+ import { cliGoalExtensionHost } from "./cli-goal-capabilities.js";
4
4
  import { managedSettingsPath } from "../settings-loader.cjs";
5
5
  import { createProjectGoalConnection } from "../project-goal-connection.js";
6
6
  import {
@@ -11,24 +11,26 @@ import { readGoalDraftDescription } from "./cli-goal-draft-request.js";
11
11
  import { digestBusinessObjectContent as digest } from "@chainlesschain/session-core/business-object-contract";
12
12
  import {
13
13
  ACCESS_SCHEMA,
14
+ CREATE_ACCESS_SCHEMA,
14
15
  MAX_SUBMISSION_REFERENCE_BYTES,
15
16
  validateGoalDraftAccess,
17
+ validateGoalCreateDraftAccess,
16
18
  validateGoalDraftProposal,
19
+ validateGoalCreateDraftProposal,
17
20
  validateGoalDraftReceipt,
18
21
  validateGoalDraftSources,
19
22
  createGoalDraftSubmissionReference,
20
23
  validateGoalDraftSubmissionReference,
21
24
  } from "@chainlesschain/session-core/goal-draft-connection-contract";
22
25
 
23
- const HOST = Object.freeze({
24
- id: "cli",
25
- version: VERSION,
26
- capabilities: Object.freeze(["proposal:task.description.update"]),
27
- });
28
26
  export const CLI_GOAL_DRAFT_SUBMISSION_SCHEMA =
29
27
  "chainlesschain.goal-draft-submission/v1";
30
28
  export const CLI_GOAL_DRAFT_SOURCES_SCHEMA =
31
29
  "chainlesschain.goal-draft-source-page/v1";
30
+ export const CLI_GOAL_CREATE_DRAFT_SUBMISSION_SCHEMA =
31
+ "chainlesschain.goal-create-draft-submission/v1";
32
+ export const CLI_GOAL_CREATE_DRAFT_SOURCES_SCHEMA =
33
+ "chainlesschain.goal-create-draft-source-page/v1";
32
34
  export const _deps = {
33
35
  createProjectGoalConnection,
34
36
  collectGoalExtensions,
@@ -48,7 +50,7 @@ function required(value) {
48
50
  fail("CLI_GOAL_DRAFT_ARGUMENTS_REQUIRED");
49
51
  return value;
50
52
  }
51
- function fixedOptions(raw) {
53
+ function fixedOptions(raw, creation = false) {
52
54
  const options = { ...raw },
53
55
  env = Object.freeze({ ...(options.env || process.env) });
54
56
  const cwd = path.resolve(options.cwd || process.cwd());
@@ -72,6 +74,8 @@ function fixedOptions(raw) {
72
74
  );
73
75
  return Object.freeze({
74
76
  ...options,
77
+ creation,
78
+ draftType: creation ? "task.create" : "task.description.update",
75
79
  cwd,
76
80
  env,
77
81
  managedSettingsFile: managedSettingsPath({
@@ -85,7 +89,7 @@ function hostOptions(options) {
85
89
  cwd: options.cwd,
86
90
  env: options.env,
87
91
  managedSettingsFile: options.managedSettingsFile,
88
- host: HOST,
92
+ host: cliGoalExtensionHost([`proposal:${options.draftType}`]),
89
93
  });
90
94
  }
91
95
  function admitted(options, expected) {
@@ -94,13 +98,12 @@ function admitted(options, expected) {
94
98
  .extensions.find(
95
99
  (item) => item.name === options.plugin && item.scope === options.scope,
96
100
  );
97
- if (
98
- !entry ||
99
- !entry.declaration.proposals.includes("task.description.update")
100
- )
101
+ if (!entry || !entry.declaration.proposals.includes(options.draftType))
101
102
  fail("CLI_GOAL_DRAFT_NOT_ADMITTED");
102
- const access = validateGoalDraftAccess({
103
- schema: ACCESS_SCHEMA,
103
+ const access = (
104
+ options.creation ? validateGoalCreateDraftAccess : validateGoalDraftAccess
105
+ )({
106
+ schema: options.creation ? CREATE_ACCESS_SCHEMA : ACCESS_SCHEMA,
104
107
  plugin: { name: entry.name, scope: entry.scope, version: entry.version },
105
108
  declarationDigest: digest(entry.declaration),
106
109
  });
@@ -113,7 +116,9 @@ function connected(id, options) {
113
116
  required(options.connection);
114
117
  const connection = _deps.createProjectGoalConnection(options.connection);
115
118
  if (
116
- connection.drafts !== true ||
119
+ (options.creation
120
+ ? connection.createDrafts !== true || connection.drafts === true
121
+ : connection.drafts !== true || connection.createDrafts === true) ||
117
122
  !["read", "sources", "receipt", "enqueue"].every(
118
123
  (key) => typeof connection[key] === "function",
119
124
  )
@@ -229,9 +234,12 @@ function referenceFor(connection, access, requestId, inputDigest) {
229
234
  inputDigest,
230
235
  });
231
236
  }
232
- function outcome(reference, file, receipt) {
237
+ function outcome(reference, file, receipt, creation = false) {
233
238
  return {
234
- schema: CLI_GOAL_DRAFT_SUBMISSION_SCHEMA,
239
+ schema: creation
240
+ ? CLI_GOAL_CREATE_DRAFT_SUBMISSION_SCHEMA
241
+ : CLI_GOAL_DRAFT_SUBMISSION_SCHEMA,
242
+ ...(creation ? { type: "task.create" } : {}),
235
243
  state: receipt?.state ?? "unknown",
236
244
  goalRef: reference.goalRef,
237
245
  plugin: reference.plugin,
@@ -257,28 +265,40 @@ async function queryReceipt(connection, saved, file) {
257
265
  receipt.inputDigest !== saved.reference.inputDigest
258
266
  )
259
267
  fail("CLI_GOAL_DRAFT_RECEIPT_MISMATCH");
260
- return outcome(saved.reference, file, receipt);
268
+ return outcome(
269
+ saved.reference,
270
+ file,
271
+ receipt,
272
+ connection.createDrafts === true,
273
+ );
261
274
  } catch {
262
- return outcome(saved.reference, file);
275
+ return outcome(
276
+ saved.reference,
277
+ file,
278
+ undefined,
279
+ connection.createDrafts === true,
280
+ );
263
281
  }
264
282
  }
265
283
 
266
284
  /** Exports an identity label and declaration digest, never native authority. */
267
- export async function exportCliGoalDraftAccess(raw = {}) {
268
- const options = fixedOptions(raw);
285
+ async function exportAccess(raw = {}, creation = false) {
286
+ const options = fixedOptions(raw, creation);
269
287
  required(options.out);
270
288
  const access = admitted(options);
271
289
  writeNew(options.out, access);
272
290
  return {
273
- schema: "chainlesschain.goal-draft-access-export/v1",
291
+ schema: options.creation
292
+ ? "chainlesschain.goal-create-draft-access-export/v1"
293
+ : "chainlesschain.goal-draft-access-export/v1",
274
294
  state: "access-exported",
275
295
  plugin: access.plugin,
276
296
  declarationDigest: access.declarationDigest,
277
297
  file: options.out,
278
298
  };
279
299
  }
280
- export async function readCliGoalDraftSources(id, raw = {}) {
281
- const options = fixedOptions(raw),
300
+ async function readSources(id, raw = {}, creation = false) {
301
+ const options = fixedOptions(raw, creation),
282
302
  { connection, access } = connected(id, options);
283
303
  if (
284
304
  options.after !== undefined &&
@@ -291,7 +311,9 @@ export async function readCliGoalDraftSources(id, raw = {}) {
291
311
  );
292
312
  admitted(options, access);
293
313
  return {
294
- schema: CLI_GOAL_DRAFT_SOURCES_SCHEMA,
314
+ schema: options.creation
315
+ ? CLI_GOAL_CREATE_DRAFT_SOURCES_SCHEMA
316
+ : CLI_GOAL_DRAFT_SOURCES_SCHEMA,
295
317
  goalRef: connection.goalRef,
296
318
  plugin: access.plugin,
297
319
  declarationDigest: access.declarationDigest,
@@ -299,13 +321,17 @@ export async function readCliGoalDraftSources(id, raw = {}) {
299
321
  sources,
300
322
  };
301
323
  }
302
- export async function sendCliGoalDraft(id, raw = {}) {
303
- const options = fixedOptions(raw);
324
+ async function sendDraft(id, raw = {}, creation = false) {
325
+ const options = fixedOptions(raw, creation);
304
326
  required(options.record);
305
327
  required(options.descriptionFile);
306
328
  const { connection, access } = connected(id, options);
307
- const proposal = validateGoalDraftProposal({
308
- type: "task.description.update",
329
+ const validateProposal = options.creation
330
+ ? validateGoalCreateDraftProposal
331
+ : validateGoalDraftProposal;
332
+ const proposal = validateProposal({
333
+ type: options.draftType,
334
+ ...(options.creation ? { taskType: options.taskType } : {}),
309
335
  goalRef: connection.goalRef,
310
336
  sourceRef: options.source,
311
337
  requestId: options.requestId,
@@ -349,7 +375,7 @@ export async function sendCliGoalDraft(id, raw = {}) {
349
375
  return true;
350
376
  },
351
377
  async enqueueProposal(context) {
352
- const projected = validateGoalDraftProposal(
378
+ const projected = validateProposal(
353
379
  Object.fromEntries(
354
380
  [
355
381
  "type",
@@ -358,6 +384,7 @@ export async function sendCliGoalDraft(id, raw = {}) {
358
384
  "requestId",
359
385
  "title",
360
386
  "description",
387
+ ...(options.creation ? ["taskType"] : []),
361
388
  ].map((key) => [key, context[key]]),
362
389
  ),
363
390
  );
@@ -389,16 +416,22 @@ export async function sendCliGoalDraft(id, raw = {}) {
389
416
  });
390
417
  if (!nativeReceipt) fail("CLI_GOAL_DRAFT_RECEIPT_MISMATCH");
391
418
  // A verified saved receipt is not undone by a later local admission change.
392
- return outcome(reference, options.record, nativeReceipt);
419
+ return outcome(reference, options.record, nativeReceipt, options.creation);
393
420
  } catch (error) {
394
- if (nativeReceipt) return outcome(reference, options.record, nativeReceipt);
421
+ if (nativeReceipt)
422
+ return outcome(
423
+ reference,
424
+ options.record,
425
+ nativeReceipt,
426
+ options.creation,
427
+ );
395
428
  if (dispatched || error.outcomeUnknown)
396
- return outcome(reference, options.record);
429
+ return outcome(reference, options.record, undefined, options.creation);
397
430
  throw error;
398
431
  }
399
432
  }
400
- export async function readCliGoalDraftReceipt(id, raw = {}) {
401
- const options = fixedOptions(raw);
433
+ async function readReceipt(id, raw = {}, creation = false) {
434
+ const options = fixedOptions(raw, creation);
402
435
  required(options.record);
403
436
  const { connection, access } = connected(id, options),
404
437
  saved = readReference(options.record);
@@ -411,3 +444,13 @@ export async function readCliGoalDraftReceipt(id, raw = {}) {
411
444
  if (!same(expected, saved.reference)) fail("CLI_GOAL_DRAFT_RECORD_CONFLICT");
412
445
  return queryReceipt(connection, saved, options.record);
413
446
  }
447
+ export const exportCliGoalDraftAccess = (raw) => exportAccess(raw, false);
448
+ export const exportCliGoalCreateDraftAccess = (raw) => exportAccess(raw, true);
449
+ export const readCliGoalDraftSources = (id, raw) => readSources(id, raw, false);
450
+ export const readCliGoalCreateDraftSources = (id, raw) =>
451
+ readSources(id, raw, true);
452
+ export const sendCliGoalDraft = (id, raw) => sendDraft(id, raw, false);
453
+ export const sendCliGoalCreateDraft = (id, raw) => sendDraft(id, raw, true);
454
+ export const readCliGoalDraftReceipt = (id, raw) => readReceipt(id, raw, false);
455
+ export const readCliGoalCreateDraftReceipt = (id, raw) =>
456
+ readReceipt(id, raw, true);
@@ -0,0 +1,284 @@
1
+ /** One acknowledged page at a time; checkpoints contain no credentials or bodies. */
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { createHash, randomUUID } from "node:crypto";
5
+ import { cliGoalExtensionHost } from "./cli-goal-capabilities.js";
6
+ import { managedSettingsPath } from "../settings-loader.cjs";
7
+ import { withFileLockAsync } from "../with-file-lock.js";
8
+ import { createProjectGoalConnection } from "../project-goal-connection.js";
9
+ import {
10
+ collectGoalExtensions,
11
+ projectGoalExtensionEvent,
12
+ } from "./goal-extensions.js";
13
+ import { parseGoalEventCursor } from "@chainlesschain/session-core/goal-evidence-contract";
14
+ const TYPES = ["risk.review.completed", "action.receipt.updated"];
15
+ const hash = (value) =>
16
+ createHash("sha256").update(JSON.stringify(value)).digest("hex");
17
+ const fail = (code) => {
18
+ throw Object.assign(new Error(code), { code });
19
+ };
20
+ export const _deps = { createProjectGoalConnection, projectGoalExtensionEvent };
21
+ const sameFile = (a, b) =>
22
+ a.dev === b.dev &&
23
+ a.ino === b.ino &&
24
+ a.size === b.size &&
25
+ a.mtimeMs === b.mtimeMs &&
26
+ a.ctimeMs === b.ctimeMs;
27
+ function readFile(file) {
28
+ const entry = fs.lstatSync(file);
29
+ if (
30
+ !entry.isFile() ||
31
+ entry.isSymbolicLink() ||
32
+ entry.nlink !== 1 ||
33
+ entry.size > 8192
34
+ )
35
+ fail("GOAL_EVENT_CHECKPOINT_INVALID");
36
+ const fd = fs.openSync(
37
+ file,
38
+ fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0),
39
+ );
40
+ try {
41
+ if (!sameFile(entry, fs.fstatSync(fd)))
42
+ fail("GOAL_EVENT_CHECKPOINT_CHANGED");
43
+ const bytes = Buffer.alloc(entry.size + 1),
44
+ n = fs.readSync(fd, bytes, 0, bytes.length, 0);
45
+ if (
46
+ n !== entry.size ||
47
+ !sameFile(entry, fs.fstatSync(fd)) ||
48
+ !sameFile(entry, fs.lstatSync(file))
49
+ )
50
+ fail("GOAL_EVENT_CHECKPOINT_CHANGED");
51
+ const body = bytes.subarray(0, n).toString("utf8");
52
+ return { record: JSON.parse(body), digest: hash(body) };
53
+ } finally {
54
+ fs.closeSync(fd);
55
+ }
56
+ }
57
+ /** Publish must acknowledge flushed output; a crash before checkpoint commit replays. */
58
+ export function replayCliGoalEvents(id, raw = {}, publish) {
59
+ const cwd = path.resolve(raw.cwd || process.cwd()),
60
+ env = Object.freeze({ ...(raw.env || process.env) });
61
+ const required = (value) => {
62
+ if (
63
+ typeof value !== "string" ||
64
+ !value ||
65
+ value.length > 4096 ||
66
+ /[\u0000-\u001f]/u.test(value)
67
+ )
68
+ fail("GOAL_EVENT_ARGUMENT_REQUIRED");
69
+ return value;
70
+ };
71
+ const connection = path.resolve(cwd, required(raw.connection)),
72
+ checkpoint = path.resolve(cwd, required(raw.checkpoint));
73
+ const name = required(raw.plugin),
74
+ scope = raw.scope,
75
+ expectedStore = raw.expectedStore;
76
+ const signal = raw.signal;
77
+ const checkpointMode = raw.checkpointMode ?? "auto";
78
+ if (!["auto", "new", "resume"].includes(checkpointMode))
79
+ fail("GOAL_EVENT_ARGUMENT_REQUIRED");
80
+ if (
81
+ !["user", "project", "local"].includes(scope) ||
82
+ typeof publish !== "function"
83
+ )
84
+ fail("GOAL_EVENT_ARGUMENT_REQUIRED");
85
+ const policyEnv = { ...env };
86
+ if (policyEnv.CC_MANAGED_SETTINGS)
87
+ policyEnv.CC_MANAGED_SETTINGS = path.resolve(
88
+ cwd,
89
+ required(policyEnv.CC_MANAGED_SETTINGS),
90
+ );
91
+ const policyFile = managedSettingsPath({
92
+ env: policyEnv,
93
+ managedSettingsFile:
94
+ raw.managedSettingsFile === undefined
95
+ ? undefined
96
+ : path.resolve(cwd, required(raw.managedSettingsFile)),
97
+ });
98
+ const options = Object.freeze({
99
+ cwd,
100
+ env,
101
+ managedSettingsFile: policyFile,
102
+ host: cliGoalExtensionHost(TYPES.map((type) => `event:${type}`)),
103
+ });
104
+ return (async () => {
105
+ const source = _deps.createProjectGoalConnection(connection);
106
+ if (!source.eventHistory || typeof source.readEventPage !== "function")
107
+ fail("GOAL_EVENT_HISTORY_CONNECTION_REQUIRED");
108
+ const goalRef = source.goalRef;
109
+ if (
110
+ goalRef.id !== id ||
111
+ (expectedStore !== undefined && expectedStore !== goalRef.storeId)
112
+ )
113
+ fail("CLI_GOAL_STORE_MISMATCH");
114
+ const selected = collectGoalExtensions(options).extensions.find(
115
+ (entry) =>
116
+ entry.name === name &&
117
+ entry.scope === scope &&
118
+ entry.declaration.events.some((type) => TYPES.includes(type)),
119
+ );
120
+ if (!selected) fail("CLI_GOAL_EVENT_PLUGIN_UNAVAILABLE");
121
+ const plugin = Object.freeze({
122
+ name: selected.name,
123
+ scope: selected.scope,
124
+ version: selected.version,
125
+ }),
126
+ declarationDigest = hash(selected.declaration);
127
+ function admission() {
128
+ if (signal?.aborted) fail("GOAL_EVENT_REPLAY_ABORTED");
129
+ const current = collectGoalExtensions(options).extensions.find(
130
+ (entry) =>
131
+ entry.name === name &&
132
+ entry.scope === scope &&
133
+ entry.version === plugin.version,
134
+ );
135
+ if (!current || hash(current.declaration) !== declarationDigest)
136
+ fail("CLI_GOAL_EVENT_ADMISSION_CHANGED");
137
+ }
138
+ admission();
139
+ const root = path.dirname(checkpoint),
140
+ realRoot = fs.realpathSync(root),
141
+ rootStat = fs.statSync(root);
142
+ const checkRoot = () => {
143
+ const stat = fs.statSync(root);
144
+ if (
145
+ fs.realpathSync(root) !== realRoot ||
146
+ !stat.isDirectory() ||
147
+ stat.dev !== rootStat.dev ||
148
+ stat.ino !== rootStat.ino
149
+ )
150
+ fail("GOAL_EVENT_CHECKPOINT_CHANGED");
151
+ };
152
+ const binding = {
153
+ schema: "chainlesschain.goal-event-checkpoint/v1",
154
+ goalRef,
155
+ plugin,
156
+ declarationDigest,
157
+ };
158
+ return withFileLockAsync(
159
+ checkpoint,
160
+ async (lock) => {
161
+ checkRoot();
162
+ admission();
163
+ const exists = fs.existsSync(checkpoint);
164
+ if (checkpointMode === "new" && exists)
165
+ fail("GOAL_EVENT_CHECKPOINT_EXISTS");
166
+ if (checkpointMode === "resume" && !exists)
167
+ fail("GOAL_EVENT_CHECKPOINT_MISSING");
168
+ if (!fs.existsSync(checkpoint)) {
169
+ const fd = fs.openSync(checkpoint, "wx", 0o600);
170
+ try {
171
+ fs.writeFileSync(fd, JSON.stringify({ ...binding, after: null }));
172
+ fs.fsyncSync(fd);
173
+ } finally {
174
+ fs.closeSync(fd);
175
+ }
176
+ }
177
+ const initial = readFile(checkpoint),
178
+ record = initial.record;
179
+ if (
180
+ !record ||
181
+ Object.keys(record).sort().join() !==
182
+ "after,declarationDigest,goalRef,plugin,schema" ||
183
+ hash({ ...record, after: null }) !== hash({ ...binding, after: null })
184
+ )
185
+ fail("GOAL_EVENT_CHECKPOINT_BINDING_CONFLICT");
186
+ if (record.after !== null) parseGoalEventCursor(record.after);
187
+ const after = record.after ?? undefined;
188
+ const page = await source.readEventPage(after, { signal });
189
+ admission();
190
+ const events = [];
191
+ for (const entry of page.entries) {
192
+ if (!selected.declaration.events.includes(entry.type)) continue;
193
+ const summary =
194
+ entry.type === "risk.review.completed"
195
+ ? `Historical risk check ${entry.sourceId}: ${entry.state}; risk task count ${entry.riskTaskCount ?? "unknown"}. This does not prove current risk or goal completion.`
196
+ : `Historical action receipt ${entry.sourceId}: ${entry.state} (${entry.actionType}). This does not prove goal completion.`;
197
+ const event = await _deps.projectGoalExtensionEvent(
198
+ plugin,
199
+ { type: entry.type, goalRef },
200
+ {
201
+ ...options,
202
+ authorize(context) {
203
+ admission();
204
+ return (
205
+ context.operation === "read-event" &&
206
+ context.type === entry.type &&
207
+ hash(context.goalRef) === hash(goalRef)
208
+ );
209
+ },
210
+ readEvent() {
211
+ return { summary, occurredAt: entry.recordedAt };
212
+ },
213
+ },
214
+ );
215
+ events.push({
216
+ id: entry.id,
217
+ sequence: entry.sequence,
218
+ sourceRef: entry.sourceRef,
219
+ recordedAt: entry.recordedAt,
220
+ event,
221
+ });
222
+ }
223
+ async function revalidate() {
224
+ admission();
225
+ checkRoot();
226
+ if (readFile(checkpoint).digest !== initial.digest)
227
+ fail("GOAL_EVENT_CHECKPOINT_CHANGED");
228
+ const current = await source.readEventPage(after, { signal });
229
+ if (
230
+ hash(current.entries.slice(0, page.entries.length)) !==
231
+ hash(page.entries) ||
232
+ (page.nextCursor === null && current.nextCursor !== null) ||
233
+ (page.nextCursor &&
234
+ parseGoalEventCursor(page.nextCursor).epoch !==
235
+ parseGoalEventCursor(current.nextCursor).epoch)
236
+ )
237
+ fail("GOAL_EVENT_HISTORY_CHANGED");
238
+ admission();
239
+ }
240
+ await revalidate();
241
+ const result = {
242
+ schema: "chainlesschain.goal-extension-history/v1",
243
+ goalRef,
244
+ plugin,
245
+ observedAt: new Date().toISOString(),
246
+ delivery: "at-least-once",
247
+ entries: events,
248
+ scannedCount: page.entries.length,
249
+ nextCursor: page.nextCursor,
250
+ hasMore: page.hasMore,
251
+ truncated: page.truncated,
252
+ retainedFrom: page.retainedFrom,
253
+ highWatermark: page.highWatermark,
254
+ };
255
+ if ((await publish(Object.freeze(result))) !== true)
256
+ fail("GOAL_EVENT_OUTPUT_NOT_ACKNOWLEDGED");
257
+ await revalidate();
258
+ lock.assertOwnership();
259
+ const temporary = `${checkpoint}.${randomUUID()}.tmp`;
260
+ try {
261
+ const fd = fs.openSync(temporary, "wx", 0o600);
262
+ try {
263
+ fs.writeFileSync(
264
+ fd,
265
+ JSON.stringify({ ...binding, after: page.nextCursor }),
266
+ );
267
+ fs.fsyncSync(fd);
268
+ } finally {
269
+ fs.closeSync(fd);
270
+ }
271
+ checkRoot();
272
+ lock.assertOwnership();
273
+ if (readFile(checkpoint).digest !== initial.digest)
274
+ fail("GOAL_EVENT_CHECKPOINT_CHANGED");
275
+ fs.renameSync(temporary, checkpoint);
276
+ } finally {
277
+ if (fs.existsSync(temporary)) fs.unlinkSync(temporary);
278
+ }
279
+ return { checkpoint, hasMore: page.hasMore };
280
+ },
281
+ { failIfUnavailable: true, timeoutMs: 2000 },
282
+ );
283
+ })();
284
+ }