keri-ts 2.1.0 → 2.2.0

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 (294) hide show
  1. package/README.md +30 -2
  2. package/esm/keri/npm/src/acdc/authoring.js +195 -0
  3. package/esm/keri/npm/src/acdc/custody.js +164 -0
  4. package/esm/keri/npm/src/acdc/dhking.js +244 -32
  5. package/esm/keri/npm/src/acdc/regbasing.js +10 -0
  6. package/esm/keri/npm/src/acdc/salting.js +16 -1
  7. package/esm/keri/npm/src/acdc/scheming.js +93 -54
  8. package/esm/keri/npm/src/acdc/unblinding.js +118 -95
  9. package/esm/keri/npm/src/app/bootstrap/authenticating.js +4 -6
  10. package/esm/keri/npm/src/app/bootstrap/oobi-resolving.js +41 -0
  11. package/esm/keri/npm/src/app/bootstrap/oobiery.js +4 -6
  12. package/esm/keri/npm/src/app/credentials/credential-revoking.js +1 -0
  13. package/esm/keri/npm/src/app/credentials/credential-workflows.js +2 -0
  14. package/esm/keri/npm/src/app/credentials/registry-settling.js +44 -0
  15. package/esm/keri/npm/src/app/credentials/root-replicating.js +68 -60
  16. package/esm/keri/npm/src/app/credentials/root-replicator.js +198 -0
  17. package/esm/keri/npm/src/app/credentials/tel-completion.js +36 -0
  18. package/esm/keri/npm/src/app/delivery/forwarding.js +16 -3
  19. package/esm/keri/npm/src/app/exchanges/exchanging.js +14 -4
  20. package/esm/keri/npm/src/app/identity/challenging.js +50 -0
  21. package/esm/keri/npm/src/app/identity/delegating.js +77 -77
  22. package/esm/keri/npm/src/app/identity/delegation-approval.js +333 -0
  23. package/esm/keri/npm/src/app/identity/event-completion.js +154 -0
  24. package/esm/keri/npm/src/app/identity/habbing.js +129 -6
  25. package/esm/keri/npm/src/app/identity/keeping.js +77 -4
  26. package/esm/keri/npm/src/app/ipex/group-ipex.js +327 -0
  27. package/esm/keri/npm/src/app/ipex/group-issuance.js +902 -0
  28. package/esm/keri/npm/src/app/ipex/ipex-admitting.js +120 -0
  29. package/esm/keri/npm/src/app/ipex/ipex-conversation.js +197 -0
  30. package/esm/keri/npm/src/app/ipex/ipex-discloser.js +591 -0
  31. package/esm/keri/npm/src/app/ipex/ipex-granting.js +93 -0
  32. package/esm/keri/npm/src/app/ipex/ipex-grouping.js +34 -11
  33. package/esm/keri/npm/src/app/ipex/issuance.js +888 -0
  34. package/esm/keri/npm/src/app/ipex/pending-presentations.js +46 -0
  35. package/esm/keri/npm/src/app/ipex/presentation-files.js +200 -0
  36. package/esm/keri/npm/src/app/ipex/presenting.js +401 -0
  37. package/esm/keri/npm/src/app/ipex/receive-issuance.js +86 -0
  38. package/esm/keri/npm/src/app/ipex/unblinding-workflow.js +660 -0
  39. package/esm/keri/npm/src/app/mailbox/ipex-arrival.js +103 -0
  40. package/esm/keri/npm/src/app/mailbox/kel-publisher.js +322 -0
  41. package/esm/keri/npm/src/app/mailbox/kel-publishing.js +65 -0
  42. package/esm/keri/npm/src/app/mailbox/kel-submission.js +24 -0
  43. package/esm/keri/npm/src/app/mailbox/mailbox-admin.js +208 -0
  44. package/esm/keri/npm/src/app/mailbox/mailbox-poller.js +212 -47
  45. package/esm/keri/npm/src/app/mailbox/mailboxing.js +10 -5
  46. package/esm/keri/npm/src/app/multisig/counseling.js +482 -0
  47. package/esm/keri/npm/src/app/multisig/group-members.js +18 -0
  48. package/esm/keri/npm/src/app/multisig/multisig-workflows.js +94 -139
  49. package/esm/keri/npm/src/app/multisig/proposals.js +281 -0
  50. package/esm/keri/npm/src/app/multisig/proposing.js +122 -0
  51. package/esm/keri/npm/src/app/notifications/signaling.js +5 -5
  52. package/esm/keri/npm/src/app/protocol/parsering.js +26 -1
  53. package/esm/keri/npm/src/app/protocol/protocol-processing.js +9 -3
  54. package/esm/keri/npm/src/app/queries/query-workflows.js +98 -0
  55. package/esm/keri/npm/src/app/runtime/agent-runtime.js +106 -55
  56. package/esm/keri/npm/src/app/runtime/cue-runtime.js +3 -6
  57. package/esm/keri/npm/src/app/runtime/delivery-outbox.js +87 -0
  58. package/esm/keri/npm/src/app/runtime/doing.js +17 -0
  59. package/esm/keri/npm/src/app/runtime/ipex-auxiliaries.js +207 -0
  60. package/esm/keri/npm/src/app/runtime/operation-owners.js +151 -0
  61. package/esm/keri/npm/src/app/runtime/retry-backoff.js +93 -0
  62. package/esm/keri/npm/src/app/runtime/workflow-contract.js +80 -0
  63. package/esm/keri/npm/src/app/transport/http-client.js +25 -6
  64. package/esm/keri/npm/src/app/witness/http-endpoints.js +4 -4
  65. package/esm/keri/npm/src/cli/common/event-completion.js +49 -0
  66. package/esm/keri/npm/src/cli/common/runtime-workers.js +23 -0
  67. package/esm/keri/npm/src/cli/credentials/ipex.js +157 -190
  68. package/esm/keri/npm/src/cli/credentials/vc.js +53 -72
  69. package/esm/keri/npm/src/cli/endpoints/did.js +12 -3
  70. package/esm/keri/npm/src/cli/endpoints/oobi.js +10 -13
  71. package/esm/keri/npm/src/cli/identity/challenge.js +8 -34
  72. package/esm/keri/npm/src/cli/identity/delegate.js +81 -258
  73. package/esm/keri/npm/src/cli/identity/incept.js +15 -32
  74. package/esm/keri/npm/src/cli/identity/interact.js +5 -17
  75. package/esm/keri/npm/src/cli/identity/rotate.js +8 -36
  76. package/esm/keri/npm/src/cli/index.js +3 -1
  77. package/esm/keri/npm/src/cli/messaging/group-join.js +79 -0
  78. package/esm/keri/npm/src/cli/messaging/group-wait.js +88 -0
  79. package/esm/keri/npm/src/cli/messaging/multisig.js +163 -284
  80. package/esm/keri/npm/src/cli/messaging/root-copy-lines.js +31 -0
  81. package/esm/keri/npm/src/core/kel/eventing.js +66 -10
  82. package/esm/keri/npm/src/core/mailbox/kram-policy.js +39 -6
  83. package/esm/keri/npm/src/core/mailbox/kraming.js +2 -0
  84. package/esm/keri/npm/src/core/records.js +23 -0
  85. package/esm/keri/npm/src/core/routing.js +13 -0
  86. package/esm/keri/npm/src/db/basing.js +34 -2
  87. package/esm/keri/npm/src/db/core/indexeddb-persistence.js +221 -0
  88. package/esm/keri/npm/src/db/indexeddb.js +98 -0
  89. package/esm/keri/npm/src/db/keeping.js +11 -2
  90. package/esm/keri/npm/src/db/store-lock.js +150 -0
  91. package/esm/keri/npm/src/npm/db-indexeddb.browser.js +2 -0
  92. package/esm/keri/npm/src/npm/db-indexeddb.js +3 -0
  93. package/esm/keri/npm/src/npm/db.browser.js +2 -0
  94. package/esm/keri/npm/src/npm/index.browser.js +9 -0
  95. package/esm/keri/npm/src/npm/runtime-web.browser.js +2 -0
  96. package/esm/keri/npm/src/npm/runtime-web.js +3 -0
  97. package/esm/keri/npm/src/npm/runtime.browser.js +2 -0
  98. package/esm/keri/npm/src/runtime/index.js +51 -4
  99. package/esm/keri/npm/src/runtime/web/output-barrier.js +47 -0
  100. package/esm/keri/npm/src/runtime/web/persistent-storage.js +37 -0
  101. package/esm/keri/npm/src/runtime/web.js +10 -0
  102. package/esm/keri/npm/src/vdr/tel-publication.js +16 -0
  103. package/esm/keri/npm/src/version.js +2 -2
  104. package/package.json +15 -2
  105. package/types/acdc/authoring.d.ts +54 -0
  106. package/types/acdc/authoring.d.ts.map +1 -0
  107. package/types/acdc/custody.d.ts +77 -0
  108. package/types/acdc/custody.d.ts.map +1 -0
  109. package/types/acdc/dhking.d.ts +94 -3
  110. package/types/acdc/dhking.d.ts.map +1 -1
  111. package/types/acdc/regbasing.d.ts +4 -0
  112. package/types/acdc/regbasing.d.ts.map +1 -1
  113. package/types/acdc/salting.d.ts +6 -1
  114. package/types/acdc/salting.d.ts.map +1 -1
  115. package/types/acdc/scheming.d.ts +2 -0
  116. package/types/acdc/scheming.d.ts.map +1 -1
  117. package/types/acdc/unblinding.d.ts +58 -19
  118. package/types/acdc/unblinding.d.ts.map +1 -1
  119. package/types/app/bootstrap/authenticating.d.ts +3 -1
  120. package/types/app/bootstrap/authenticating.d.ts.map +1 -1
  121. package/types/app/bootstrap/oobi-resolving.d.ts +92 -0
  122. package/types/app/bootstrap/oobi-resolving.d.ts.map +1 -0
  123. package/types/app/bootstrap/oobiery.d.ts +3 -1
  124. package/types/app/bootstrap/oobiery.d.ts.map +1 -1
  125. package/types/app/credentials/credential-revoking.d.ts +3 -0
  126. package/types/app/credentials/credential-revoking.d.ts.map +1 -1
  127. package/types/app/credentials/credential-workflows.d.ts +5 -0
  128. package/types/app/credentials/credential-workflows.d.ts.map +1 -1
  129. package/types/app/credentials/registry-settling.d.ts +85 -0
  130. package/types/app/credentials/registry-settling.d.ts.map +1 -0
  131. package/types/app/credentials/root-replicating.d.ts +56 -44
  132. package/types/app/credentials/root-replicating.d.ts.map +1 -1
  133. package/types/app/credentials/root-replicator.d.ts +105 -0
  134. package/types/app/credentials/root-replicator.d.ts.map +1 -0
  135. package/types/app/credentials/tel-completion.d.ts +55 -0
  136. package/types/app/credentials/tel-completion.d.ts.map +1 -0
  137. package/types/app/delivery/forwarding.d.ts +6 -0
  138. package/types/app/delivery/forwarding.d.ts.map +1 -1
  139. package/types/app/exchanges/exchanging.d.ts.map +1 -1
  140. package/types/app/identity/challenging.d.ts +54 -0
  141. package/types/app/identity/challenging.d.ts.map +1 -1
  142. package/types/app/identity/delegating.d.ts +21 -4
  143. package/types/app/identity/delegating.d.ts.map +1 -1
  144. package/types/app/identity/delegation-approval.d.ts +228 -0
  145. package/types/app/identity/delegation-approval.d.ts.map +1 -0
  146. package/types/app/identity/event-completion.d.ts +151 -0
  147. package/types/app/identity/event-completion.d.ts.map +1 -0
  148. package/types/app/identity/habbing.d.ts +67 -2
  149. package/types/app/identity/habbing.d.ts.map +1 -1
  150. package/types/app/identity/keeping.d.ts +49 -3
  151. package/types/app/identity/keeping.d.ts.map +1 -1
  152. package/types/app/ipex/group-ipex.d.ts +247 -0
  153. package/types/app/ipex/group-ipex.d.ts.map +1 -0
  154. package/types/app/ipex/group-issuance.d.ts +281 -0
  155. package/types/app/ipex/group-issuance.d.ts.map +1 -0
  156. package/types/app/ipex/ipex-admitting.d.ts +47 -0
  157. package/types/app/ipex/ipex-admitting.d.ts.map +1 -0
  158. package/types/app/ipex/ipex-conversation.d.ts +195 -0
  159. package/types/app/ipex/ipex-conversation.d.ts.map +1 -0
  160. package/types/app/ipex/ipex-discloser.d.ts +156 -0
  161. package/types/app/ipex/ipex-discloser.d.ts.map +1 -0
  162. package/types/app/ipex/ipex-granting.d.ts +145 -0
  163. package/types/app/ipex/ipex-granting.d.ts.map +1 -0
  164. package/types/app/ipex/ipex-grouping.d.ts +18 -0
  165. package/types/app/ipex/ipex-grouping.d.ts.map +1 -1
  166. package/types/app/ipex/issuance.d.ts +443 -0
  167. package/types/app/ipex/issuance.d.ts.map +1 -0
  168. package/types/app/ipex/pending-presentations.d.ts +25 -0
  169. package/types/app/ipex/pending-presentations.d.ts.map +1 -0
  170. package/types/app/ipex/presentation-files.d.ts +47 -0
  171. package/types/app/ipex/presentation-files.d.ts.map +1 -0
  172. package/types/app/ipex/presenting.d.ts +298 -0
  173. package/types/app/ipex/presenting.d.ts.map +1 -0
  174. package/types/app/ipex/receive-issuance.d.ts +99 -0
  175. package/types/app/ipex/receive-issuance.d.ts.map +1 -0
  176. package/types/app/ipex/unblinding-workflow.d.ts +255 -0
  177. package/types/app/ipex/unblinding-workflow.d.ts.map +1 -0
  178. package/types/app/mailbox/ipex-arrival.d.ts +104 -0
  179. package/types/app/mailbox/ipex-arrival.d.ts.map +1 -0
  180. package/types/app/mailbox/kel-publisher.d.ts +141 -0
  181. package/types/app/mailbox/kel-publisher.d.ts.map +1 -0
  182. package/types/app/mailbox/kel-publishing.d.ts +70 -0
  183. package/types/app/mailbox/kel-publishing.d.ts.map +1 -0
  184. package/types/app/mailbox/kel-submission.d.ts +18 -0
  185. package/types/app/mailbox/kel-submission.d.ts.map +1 -0
  186. package/types/app/mailbox/mailbox-admin.d.ts +113 -0
  187. package/types/app/mailbox/mailbox-admin.d.ts.map +1 -0
  188. package/types/app/mailbox/mailbox-poller.d.ts +92 -3
  189. package/types/app/mailbox/mailbox-poller.d.ts.map +1 -1
  190. package/types/app/mailbox/mailboxing.d.ts +3 -0
  191. package/types/app/mailbox/mailboxing.d.ts.map +1 -1
  192. package/types/app/multisig/counseling.d.ts +196 -0
  193. package/types/app/multisig/counseling.d.ts.map +1 -0
  194. package/types/app/multisig/group-members.d.ts +11 -0
  195. package/types/app/multisig/group-members.d.ts.map +1 -1
  196. package/types/app/multisig/grouping.d.ts +39 -20
  197. package/types/app/multisig/grouping.d.ts.map +1 -1
  198. package/types/app/multisig/multisig-workflows.d.ts +91 -22
  199. package/types/app/multisig/multisig-workflows.d.ts.map +1 -1
  200. package/types/app/multisig/proposals.d.ts +129 -0
  201. package/types/app/multisig/proposals.d.ts.map +1 -0
  202. package/types/app/multisig/proposing.d.ts +125 -0
  203. package/types/app/multisig/proposing.d.ts.map +1 -0
  204. package/types/app/notifications/signaling.d.ts +3 -1
  205. package/types/app/notifications/signaling.d.ts.map +1 -1
  206. package/types/app/protocol/parsering.d.ts +13 -0
  207. package/types/app/protocol/parsering.d.ts.map +1 -1
  208. package/types/app/protocol/protocol-processing.d.ts +7 -1
  209. package/types/app/protocol/protocol-processing.d.ts.map +1 -1
  210. package/types/app/queries/query-workflows.d.ts +98 -0
  211. package/types/app/queries/query-workflows.d.ts.map +1 -0
  212. package/types/app/runtime/agent-runtime.d.ts +72 -25
  213. package/types/app/runtime/agent-runtime.d.ts.map +1 -1
  214. package/types/app/runtime/cue-runtime.d.ts +2 -1
  215. package/types/app/runtime/cue-runtime.d.ts.map +1 -1
  216. package/types/app/runtime/delivery-outbox.d.ts +68 -0
  217. package/types/app/runtime/delivery-outbox.d.ts.map +1 -0
  218. package/types/app/runtime/doing.d.ts +27 -0
  219. package/types/app/runtime/doing.d.ts.map +1 -0
  220. package/types/app/runtime/ipex-auxiliaries.d.ts +74 -0
  221. package/types/app/runtime/ipex-auxiliaries.d.ts.map +1 -0
  222. package/types/app/runtime/operation-owners.d.ts +137 -0
  223. package/types/app/runtime/operation-owners.d.ts.map +1 -0
  224. package/types/app/runtime/retry-backoff.d.ts +49 -0
  225. package/types/app/runtime/retry-backoff.d.ts.map +1 -0
  226. package/types/app/runtime/workflow-contract.d.ts +79 -0
  227. package/types/app/runtime/workflow-contract.d.ts.map +1 -0
  228. package/types/app/transport/http-client.d.ts.map +1 -1
  229. package/types/cli/common/event-completion.d.ts +26 -0
  230. package/types/cli/common/event-completion.d.ts.map +1 -0
  231. package/types/cli/common/runtime-workers.d.ts +23 -0
  232. package/types/cli/common/runtime-workers.d.ts.map +1 -0
  233. package/types/cli/credentials/ipex.d.ts.map +1 -1
  234. package/types/cli/credentials/vc.d.ts +1 -1
  235. package/types/cli/credentials/vc.d.ts.map +1 -1
  236. package/types/cli/endpoints/did.d.ts.map +1 -1
  237. package/types/cli/endpoints/oobi.d.ts +3 -1
  238. package/types/cli/endpoints/oobi.d.ts.map +1 -1
  239. package/types/cli/identity/challenge.d.ts +1 -1
  240. package/types/cli/identity/challenge.d.ts.map +1 -1
  241. package/types/cli/identity/delegate.d.ts +14 -1
  242. package/types/cli/identity/delegate.d.ts.map +1 -1
  243. package/types/cli/identity/incept.d.ts.map +1 -1
  244. package/types/cli/identity/interact.d.ts.map +1 -1
  245. package/types/cli/identity/rotate.d.ts.map +1 -1
  246. package/types/cli/index.d.ts +3 -1
  247. package/types/cli/index.d.ts.map +1 -1
  248. package/types/cli/messaging/group-join.d.ts +53 -0
  249. package/types/cli/messaging/group-join.d.ts.map +1 -0
  250. package/types/cli/messaging/group-wait.d.ts +34 -0
  251. package/types/cli/messaging/group-wait.d.ts.map +1 -0
  252. package/types/cli/messaging/multisig.d.ts +28 -5
  253. package/types/cli/messaging/multisig.d.ts.map +1 -1
  254. package/types/cli/messaging/root-copy-lines.d.ts +39 -0
  255. package/types/cli/messaging/root-copy-lines.d.ts.map +1 -0
  256. package/types/core/cues.d.ts +11 -0
  257. package/types/core/cues.d.ts.map +1 -1
  258. package/types/core/kel/eventing.d.ts.map +1 -1
  259. package/types/core/mailbox/kram-policy.d.ts +14 -0
  260. package/types/core/mailbox/kram-policy.d.ts.map +1 -1
  261. package/types/core/mailbox/kraming.d.ts +1 -1
  262. package/types/core/mailbox/kraming.d.ts.map +1 -1
  263. package/types/core/records.d.ts +61 -0
  264. package/types/core/records.d.ts.map +1 -1
  265. package/types/core/routing.d.ts +6 -0
  266. package/types/core/routing.d.ts.map +1 -1
  267. package/types/db/basing.d.ts +8 -2
  268. package/types/db/basing.d.ts.map +1 -1
  269. package/types/db/core/indexeddb-persistence.d.ts +59 -0
  270. package/types/db/core/indexeddb-persistence.d.ts.map +1 -0
  271. package/types/db/indexeddb.d.ts +37 -0
  272. package/types/db/indexeddb.d.ts.map +1 -0
  273. package/types/db/keeping.d.ts +5 -2
  274. package/types/db/keeping.d.ts.map +1 -1
  275. package/types/db/store-lock.d.ts +72 -0
  276. package/types/db/store-lock.d.ts.map +1 -0
  277. package/types/npm/db-indexeddb.d.ts +4 -0
  278. package/types/npm/db-indexeddb.d.ts.map +1 -0
  279. package/types/npm/runtime-web.d.ts +4 -0
  280. package/types/npm/runtime-web.d.ts.map +1 -0
  281. package/types/runtime/index.d.ts +41 -6
  282. package/types/runtime/index.d.ts.map +1 -1
  283. package/types/runtime/web/output-barrier.d.ts +30 -0
  284. package/types/runtime/web/output-barrier.d.ts.map +1 -0
  285. package/types/runtime/web/persistent-storage.d.ts +36 -0
  286. package/types/runtime/web/persistent-storage.d.ts.map +1 -0
  287. package/types/runtime/web.d.ts +11 -0
  288. package/types/runtime/web.d.ts.map +1 -0
  289. package/types/vdr/tel-publication.d.ts +6 -0
  290. package/types/vdr/tel-publication.d.ts.map +1 -1
  291. package/types/version.d.ts +2 -2
  292. package/esm/keri/npm/src/cli/common/tel-completion.js +0 -24
  293. package/types/cli/common/tel-completion.d.ts +0 -8
  294. package/types/cli/common/tel-completion.d.ts.map +0 -1
@@ -90,7 +90,7 @@ function escrowContext(keys, serder) {
90
90
  return [keys, serder, pre, said, snh];
91
91
  }
92
92
  /** Persist the delegator event seal that authorizes one delegated event. */
93
- function pinAuthorizingSeal(hby, delegated, delegating) {
93
+ export function pinAuthorizingSeal(hby, delegated, delegating) {
94
94
  const pre = delegated.pre;
95
95
  const said = delegated.said;
96
96
  const sner = delegating.sner;
@@ -226,6 +226,72 @@ Object.defineProperty(DelegateRequestHandler, "resource", {
226
226
  writable: true,
227
227
  value: DELEGATE_REQUEST_ROUTE
228
228
  });
229
+ /** Enqueue one anchor query to the delegator's first sorted witness, when its endpoint is known. */
230
+ function enqueueAnchorQuery(deps, serder, delpre, witness, communicationHab) {
231
+ deps.querying.enqueue({
232
+ pre: delpre,
233
+ route: "logs",
234
+ q: { fn: "0", s: "0", a: eventAnchor(serder) },
235
+ signer: communicationHab.pre,
236
+ wits: [witness],
237
+ });
238
+ }
239
+ /**
240
+ * Publish the initial delegator-witness anchor query and seed the retry-pass
241
+ * bookkeeping so the first retry starts the backoff window instead of querying again.
242
+ */
243
+ export function queueDelegatorWitnessQuery(deps, serder, communicationHab) {
244
+ const id = workflowId(serder);
245
+ const delpre = delegatedWorkflowDelpre(deps.hby, serder);
246
+ if (!delpre) {
247
+ throw new ValidationError(`Delegation workflow ${id} is missing a delegator prefix.`);
248
+ }
249
+ const delegator = deps.hby.db.getKever(delpre);
250
+ if (!delegator) {
251
+ return;
252
+ }
253
+ const witness = firstSorted(delegator.wits);
254
+ if (!witness || !preferredWitnessQueryUrl(communicationHab, witness)) {
255
+ return;
256
+ }
257
+ enqueueAnchorQuery(deps, serder, delpre, witness, communicationHab);
258
+ deps.retryPasses.set(id, -1);
259
+ }
260
+ /** Retry bounded delegator-witness queries while waiting for the anchor; returns why the wait continues. */
261
+ export function retryDelegatorWitnessQuery(deps, serder, communicationHab) {
262
+ const id = workflowId(serder);
263
+ const delpre = delegatedWorkflowDelpre(deps.hby, serder);
264
+ if (!delpre) {
265
+ throw new ValidationError(`Delegation workflow ${id} is missing a delegator prefix.`);
266
+ }
267
+ const delegator = deps.hby.db.getKever(delpre);
268
+ if (!delegator) {
269
+ return "Delegator anchor has not been learned locally yet and the delegator KEL is not known locally.";
270
+ }
271
+ const witness = firstSorted(delegator.wits);
272
+ if (!witness) {
273
+ return "Delegator anchor has not been learned locally yet and no delegator witness is known locally.";
274
+ }
275
+ if (!preferredWitnessQueryUrl(communicationHab, witness)) {
276
+ return `Delegator anchor has not been learned locally yet and witness ${witness} has no HTTP endpoint known locally.`;
277
+ }
278
+ const pass = deps.retryPasses.get(id);
279
+ if (pass === undefined) {
280
+ enqueueAnchorQuery(deps, serder, delpre, witness, communicationHab);
281
+ deps.retryPasses.set(id, 0);
282
+ return `Delegator anchor has not been learned locally yet; queried delegator witness ${witness}.`;
283
+ }
284
+ const nextPass = pass + 1;
285
+ if (nextPass < DELEGATION_ANCHOR_QUERY_RETRY_PASSES) {
286
+ deps.retryPasses.set(id, nextPass);
287
+ const remaining = DELEGATION_ANCHOR_QUERY_RETRY_PASSES
288
+ - nextPass;
289
+ return `Delegator anchor has not been learned locally yet; next delegator witness query retry in ${remaining} pass(es).`;
290
+ }
291
+ enqueueAnchorQuery(deps, serder, delpre, witness, communicationHab);
292
+ deps.retryPasses.set(id, 0);
293
+ return `Delegator anchor has not been learned locally yet; queried delegator witness ${witness}.`;
294
+ }
229
295
  /**
230
296
  * Delegation workflow coordinator for delegated inception and rotation events.
231
297
  *
@@ -329,6 +395,13 @@ export class Anchorer {
329
395
  writable: true,
330
396
  value: new Map()
331
397
  });
398
+ /** Shared anchor-discovery inputs over this owner's collaborators and retry bookkeeping. */
399
+ Object.defineProperty(this, "anchorQuery", {
400
+ enumerable: true,
401
+ configurable: true,
402
+ writable: true,
403
+ value: void 0
404
+ });
332
405
  if (protocol && protocol.kvy.db !== hby.db) {
333
406
  throw new RangeError("Anchorer protocol processor must use the same database");
334
407
  }
@@ -336,6 +409,7 @@ export class Anchorer {
336
409
  this.protocol = protocol;
337
410
  this.postman = postman;
338
411
  this.querying = querying;
412
+ this.anchorQuery = { hby, querying, retryPasses: this.anchorQueryRetryPasses };
339
413
  this.http = http;
340
414
  this.clock = clock;
341
415
  }
@@ -531,7 +605,7 @@ export class Anchorer {
531
605
  // publish phase that forwards the delegation request and raw event bytes.
532
606
  // Keep that ordering here so delegate-side approval discovery does not
533
607
  // depend on a later escrow pass happening "soon enough" by accident.
534
- this.queueDelegatorWitnessQueryNow(serder, communicationHab);
608
+ queueDelegatorWitnessQuery(this.anchorQuery, serder, communicationHab);
535
609
  this.hby.db.dpwe.rem(keys);
536
610
  this.hby.db.dune.pin(keys, serder);
537
611
  return {
@@ -579,7 +653,7 @@ export class Anchorer {
579
653
  pre,
580
654
  said,
581
655
  phase: "waitingDelegatorAnchor",
582
- reason: this.retryDelegatorWitnessQuery(serder, communicationHab),
656
+ reason: retryDelegatorWitnessQuery(this.anchorQuery, serder, communicationHab),
583
657
  };
584
658
  }
585
659
  // The approving event is authoritative only after the delegator's sealing
@@ -634,80 +708,6 @@ export class Anchorer {
634
708
  }
635
709
  throw new ValidationError(`Delegated inception for ${hab.pre} requires --proxy <alias> to send delegation requests.`);
636
710
  }
637
- /** Publish the initial anchor query after successful sending and seed the existing retry-pass bookkeeping. */
638
- queueDelegatorWitnessQueryNow(serder, communicationHab) {
639
- const id = workflowId(serder);
640
- const delpre = delegatedWorkflowDelpre(this.hby, serder);
641
- if (!delpre) {
642
- throw new ValidationError(`Delegation workflow ${id} is missing a delegator prefix.`);
643
- }
644
- const delegator = this.hby.db.getKever(delpre);
645
- if (!delegator) {
646
- return;
647
- }
648
- const witness = firstSorted(delegator.wits);
649
- if (!witness || !preferredWitnessQueryUrl(communicationHab, witness)) {
650
- return;
651
- }
652
- this.querying.enqueue({
653
- pre: delpre,
654
- route: "logs",
655
- q: { fn: "0", s: "0", a: eventAnchor(serder) },
656
- signer: communicationHab.pre,
657
- wits: [witness],
658
- });
659
- // Mark that an immediate publication-time query was already sent so the
660
- // first unanchored escrow retry pass starts the backoff window instead of
661
- // immediately enqueueing the same query again.
662
- this.anchorQueryRetryPasses.set(id, -1);
663
- }
664
- /** Retry bounded delegator-witness queries while waiting for the anchor. */
665
- retryDelegatorWitnessQuery(serder, communicationHab) {
666
- const id = workflowId(serder);
667
- const delpre = delegatedWorkflowDelpre(this.hby, serder);
668
- if (!delpre) {
669
- throw new ValidationError(`Delegation workflow ${id} is missing a delegator prefix.`);
670
- }
671
- const delegator = this.hby.db.getKever(delpre);
672
- if (!delegator) {
673
- return "Delegator anchor has not been learned locally yet and the delegator KEL is not known locally.";
674
- }
675
- const witness = firstSorted(delegator.wits);
676
- if (!witness) {
677
- return "Delegator anchor has not been learned locally yet and no delegator witness is known locally.";
678
- }
679
- if (!preferredWitnessQueryUrl(communicationHab, witness)) {
680
- return `Delegator anchor has not been learned locally yet and witness ${witness} has no HTTP endpoint known locally.`;
681
- }
682
- const pass = this.anchorQueryRetryPasses.get(id);
683
- if (pass === undefined) {
684
- this.querying.enqueue({
685
- pre: delpre,
686
- route: "logs",
687
- q: { fn: "0", s: "0", a: eventAnchor(serder) },
688
- signer: communicationHab.pre,
689
- wits: [witness],
690
- });
691
- this.anchorQueryRetryPasses.set(id, 0);
692
- return `Delegator anchor has not been learned locally yet; queried delegator witness ${witness}.`;
693
- }
694
- const nextPass = pass + 1;
695
- if (nextPass < DELEGATION_ANCHOR_QUERY_RETRY_PASSES) {
696
- this.anchorQueryRetryPasses.set(id, nextPass);
697
- const remaining = DELEGATION_ANCHOR_QUERY_RETRY_PASSES
698
- - nextPass;
699
- return `Delegator anchor has not been learned locally yet; next delegator witness query retry in ${remaining} pass(es).`;
700
- }
701
- this.querying.enqueue({
702
- pre: delpre,
703
- route: "logs",
704
- q: { fn: "0", s: "0", a: eventAnchor(serder) },
705
- signer: communicationHab.pre,
706
- wits: [witness],
707
- });
708
- this.anchorQueryRetryPasses.set(id, 0);
709
- return `Delegator anchor has not been learned locally yet; queried delegator witness ${witness}.`;
710
- }
711
711
  /** Retire only this event's process-local signer and query bookkeeping. */
712
712
  clearWorkflowPins(serder) {
713
713
  const id = workflowId(serder);
@@ -0,0 +1,333 @@
1
+ /**
2
+ * Delegator-side approval of pending delegated events, owned by the library so
3
+ * that every host (CLI, wallet, service) runs one implementation.
4
+ *
5
+ * A delegate's `dip` or `drt` reaches the delegator's `delegables.` escrow. The
6
+ * delegator approves it by anchoring the event seal in its own KEL, then lets
7
+ * the delegate's event reach local state. Operations follow the workflow
8
+ * contract (`app/runtime/workflow-contract.ts`):
9
+ * - `listPendingDelegations` and `delegationApprovalStatus` are pure reads of
10
+ * durable state.
11
+ * - `approveDelegation` is the only operation that signs. It returns after the
12
+ * anchor is committed locally and the follow-up requests are sent.
13
+ * - `waitForPendingDelegation`, `waitForDelegationAnchor` and `waitForDelegationApproval`
14
+ * only observe, each on the one thing asked for; the host's runtime workers make the
15
+ * request and the delegate's event arrive.
16
+ * - `dismissDelegation` removes the local controller notice only.
17
+ *
18
+ * Idempotence evidence: the anchor is the seal `{i, s, d}` of the delegated event
19
+ * inside the delegator's own KEL. That KEL entry is durable the moment signing
20
+ * commits, so a repeated approval after a crash finds it and never anchors twice.
21
+ * Witness receipts are not part of the test on purpose: the sealing-event lookup
22
+ * of `Baser` requires them, which would re-anchor in the window between signing
23
+ * and receipting.
24
+ *
25
+ * - A group delegator's anchor is a group interaction: `proposeGroupDelegationApproval`
26
+ * proposes it, the other members approve it with `approveGroupProposal`, and the
27
+ * Counselor receipts it. `requestDelegatedEvent` then asks for the delegate's event.
28
+ *
29
+ * Boundaries:
30
+ * - Dependencies are declared collaborators, not an AgentRuntime (ADR-0013).
31
+ * - `approveDelegation` serves single-signature delegators and rejects a group.
32
+ * - Witness authorization codes arrive resolved. Prompting is a host concern.
33
+ */
34
+ import { Diger } from "cesr-ts";
35
+ import { ValidationError } from "../../core/errors.js";
36
+ import { dgKey } from "../../db/core/keys.js";
37
+ import { proposeGroupInteraction } from "../multisig/proposing.js";
38
+ import { observeUntil } from "../runtime/workflow-contract.js";
39
+ import { DELEGATE_REQUEST_ROUTE } from "./delegating.js";
40
+ import { isLocalGroupHab } from "./endpoint-roleing.js";
41
+ import { receiptEvent } from "./event-completion.js";
42
+ /** How long the CLI-compatible wait for the delegate's event lasts. */
43
+ export const DELEGATION_APPROVAL_TIMEOUT_MS = 30_000;
44
+ /** Delegator prefix for escrowed `dip` or `drt` material, if it is derivable. */
45
+ function delegationSourcePrefix(db, serder, pre) {
46
+ if (serder.delpre)
47
+ return serder.delpre;
48
+ if (serder.ilk !== "drt")
49
+ return undefined;
50
+ const delegatedPre = serder.pre ?? pre;
51
+ return db.getKever(delegatedPre)?.delpre ?? db.getState(delegatedPre)?.di;
52
+ }
53
+ /** Project a delegated event into the seal the approving event embeds. */
54
+ export function delegationSeal(serder) {
55
+ if (!serder.pre || !serder.snh || !serder.said) {
56
+ throw new ValidationError("Delegated event is missing pre, sn, or said.");
57
+ }
58
+ return { i: serder.pre, s: serder.snh, d: serder.said };
59
+ }
60
+ function requestFromEscrow(db, serder, pre) {
61
+ const delegator = delegationSourcePrefix(db, serder, pre);
62
+ if (!delegator)
63
+ return null;
64
+ const seal = delegationSeal(serder);
65
+ return {
66
+ delegate: seal.i,
67
+ sn: serder.sn ?? 0,
68
+ said: seal.d,
69
+ ilk: serder.ilk === "drt" ? "drt" : "dip",
70
+ delegator,
71
+ };
72
+ }
73
+ /** Find the delegator's KEL event that seals the request, whether or not it is receipted. */
74
+ function findAnchor(db, request) {
75
+ for (const [, said] of db.getKelItemIter(request.delegator)) {
76
+ const serder = db.getEvtSerder(request.delegator, said);
77
+ for (const seal of serder?.seals ?? []) {
78
+ const { i, s, d } = (seal ?? {});
79
+ if (i === request.delegate && d === request.said && typeof s === "string" && parseInt(s, 16) === request.sn) {
80
+ return serder;
81
+ }
82
+ }
83
+ }
84
+ return null;
85
+ }
86
+ function anchorOf(serder) {
87
+ return { sn: serder.sn ?? 0, said: serder.said };
88
+ }
89
+ function inEscrow(db, request) {
90
+ return db.delegables.get([request.delegate]).includes(request.said);
91
+ }
92
+ /** True once the delegated event has left escrow and reached local state. */
93
+ export function delegateCommitted(db, delegate, sn) {
94
+ const kever = db.getKever(delegate);
95
+ return kever !== undefined && kever !== null && kever.sn >= sn;
96
+ }
97
+ /** Witnesses of the delegate after the delegated event: `backs` of a `dip`, current witnesses otherwise. */
98
+ export function delegateWitnesses(db, serder) {
99
+ if ((serder.sn ?? 0) === 0)
100
+ return [...serder.backs];
101
+ if (!serder.pre)
102
+ return [];
103
+ return [...(db.getKever(serder.pre)?.wits ?? [])];
104
+ }
105
+ /** Build the logs query that pulls the delegated event's replay from one delegate witness. */
106
+ export function delegateWitnessLogsQuery(signer, serder, witness) {
107
+ if (!serder.pre || !serder.snh) {
108
+ throw new ValidationError("Delegated event is missing pre or sn for query.");
109
+ }
110
+ return {
111
+ pre: serder.pre,
112
+ signer,
113
+ src: witness,
114
+ route: "logs",
115
+ q: { fn: "0", s: serder.snh },
116
+ wits: [witness],
117
+ };
118
+ }
119
+ /** Record the approving seal for the delegated event and let the delegator's escrow promote it. */
120
+ export function pinApprovalSeal(hby, hab, delegated, approving) {
121
+ if (!approving.sner || !approving.said || !delegated.pre || !delegated.said) {
122
+ throw new ValidationError("Approving event material is incomplete.");
123
+ }
124
+ hby.db.aess.pin(dgKey(delegated.pre, delegated.said), [approving.sner, new Diger({ qb64: approving.said })]);
125
+ hab.kvy.processEscrowDelegables();
126
+ }
127
+ /**
128
+ * List delegated events awaiting approval, oldest sequence first. Reads every
129
+ * `delegables.` row, with no cap, and skips rows whose delegated event already
130
+ * reached local state. Pure.
131
+ */
132
+ export function listPendingDelegations(deps, { delegator } = {}) {
133
+ const { db } = deps;
134
+ const pending = [];
135
+ for (const [keys, said] of db.delegables.getTopItemIter()) {
136
+ const pre = keys[0];
137
+ const serder = pre ? db.getEvtSerder(pre, said) : null;
138
+ const request = serder && pre ? requestFromEscrow(db, serder, pre) : null;
139
+ if (!request || (delegator !== undefined && request.delegator !== delegator))
140
+ continue;
141
+ if (delegateCommitted(db, request.delegate, request.sn))
142
+ continue;
143
+ pending.push({ ...request, anchored: findAnchor(db, request) !== null });
144
+ }
145
+ return pending.sort((left, right) => left.sn - right.sn);
146
+ }
147
+ /** Read the durable status of one request; mutates nothing. */
148
+ export function delegationApprovalStatus(deps, request) {
149
+ const { db } = deps;
150
+ const committed = delegateCommitted(db, request.delegate, request.sn);
151
+ if (!committed && !inEscrow(db, request))
152
+ return { state: "unknown", reason: "no-local-event" };
153
+ const anchor = findAnchor(db, request);
154
+ if (!anchor)
155
+ return { state: "awaiting-approval" };
156
+ if (committed)
157
+ return { state: "approved", anchor: anchorOf(anchor) };
158
+ return { state: "anchored", anchor: anchorOf(anchor), receipted: db.fullyWitnessed(anchor) };
159
+ }
160
+ /**
161
+ * Local step after anchoring: record the approving seal and process the
162
+ * `delegables.` escrow. Idempotent, sends nothing and signs nothing. It does
163
+ * nothing until the anchor exists and, for a delegate with witnesses, until the
164
+ * delegate's event has arrived through the witness replay, because pinning
165
+ * earlier would make local state look approved while incomplete.
166
+ */
167
+ export function settleDelegationApproval(deps, request) {
168
+ const { hby } = deps;
169
+ const hab = hby.habs.get(request.delegator);
170
+ const delegated = hby.db.getEvtSerder(request.delegate, request.said);
171
+ const anchor = findAnchor(hby.db, request);
172
+ if (!hab || !delegated || !anchor)
173
+ return;
174
+ const witnessed = delegateWitnesses(hby.db, delegated).length > 0;
175
+ if (witnessed && !delegateCommitted(hby.db, request.delegate, request.sn))
176
+ return;
177
+ pinApprovalSeal(hby, hab, delegated, anchor);
178
+ }
179
+ /**
180
+ * Approve one delegated event: anchor its seal in the delegator's KEL unless
181
+ * the KEL already holds it, obtain witness receipts for the anchoring event and
182
+ * ask the delegate's witness for the delegated event. Returns without waiting
183
+ * for the delegate; use {@link waitForDelegationApproval}.
184
+ *
185
+ * Repeating the call after a failure resumes at the first step not yet done.
186
+ *
187
+ * @throws ValidationError for a request not held locally, a delegator that is not a local single-signature identifier, or an anchor that cannot be read back.
188
+ */
189
+ export function* approveDelegation(deps, request, { event, auths }) {
190
+ const { hby } = deps;
191
+ const delegated = hby.db.getEvtSerder(request.delegate, request.said);
192
+ const status = delegationApprovalStatus(hby, request);
193
+ if (status.state === "unknown" || !delegated) {
194
+ throw new ValidationError(`No delegated ${request.ilk} ${request.said} is held for ${request.delegate}.`);
195
+ }
196
+ const hab = hby.habs.get(request.delegator);
197
+ if (!hab || isLocalGroupHab(hby, hab)) {
198
+ throw new ValidationError(`${request.delegator} is not a local single-signature delegator.`);
199
+ }
200
+ if (!hab.kever)
201
+ throw new ValidationError(`Missing accepted key state for ${hab.pre}.`);
202
+ let anchor = findAnchor(hby.db, request);
203
+ if (!anchor) {
204
+ const data = [delegationSeal(delegated)];
205
+ if (event === "ixn")
206
+ hab.interact({ data });
207
+ else
208
+ hab.rotate({ data });
209
+ anchor = findAnchor(hby.db, request);
210
+ if (!anchor)
211
+ throw new ValidationError("Approving event material is incomplete.");
212
+ }
213
+ yield* receiptEvent(deps, hab.pre, { sn: anchorOf(anchor).sn, auths });
214
+ requestDelegatedEvent(deps, request, hab.pre);
215
+ return delegationApprovalStatus(hby, request);
216
+ }
217
+ /**
218
+ * After the anchor exists: record the approving seal and ask the delegate's witness
219
+ * for the delegated event unless it already arrived. `signer` is the local identifier
220
+ * that sends the query: the delegator, or a group delegator's local member. Idempotent.
221
+ */
222
+ export function requestDelegatedEvent(deps, request, signer) {
223
+ const { hby } = deps;
224
+ const delegated = hby.db.getEvtSerder(request.delegate, request.said);
225
+ if (!delegated) {
226
+ throw new ValidationError(`No delegated ${request.ilk} ${request.said} is held for ${request.delegate}.`);
227
+ }
228
+ settleDelegationApproval(deps, request);
229
+ const [witness] = delegateWitnesses(hby.db, delegated).sort();
230
+ if (!delegateCommitted(hby.db, request.delegate, request.sn)) {
231
+ // A delegate with no witness is asked through the same logs route with the usual attester choice.
232
+ deps.querying.enqueue(witness
233
+ ? delegateWitnessLogsQuery(signer, delegated, witness)
234
+ : { pre: request.delegate, signer, route: "logs", q: { fn: "0", s: delegated.snh } });
235
+ }
236
+ deps.changes?.push({ kind: "delegation", id: `${request.delegate}:${request.sn}` });
237
+ }
238
+ /**
239
+ * Propose a group delegator's anchoring interaction for one request: the group
240
+ * interaction that seals the delegated event, published to the other members.
241
+ * Returns without waiting; the other members approve it with `approveGroupProposal`
242
+ * and the Counselor receipts it, so read progress with `delegationApprovalStatus`
243
+ * and `counselor.status(result.event)`.
244
+ *
245
+ * Idempotent: null when the group's KEL already holds the anchor, so a repeat
246
+ * never anchors twice.
247
+ *
248
+ * @throws ValidationError for a request not held locally or a delegator that is not a local group.
249
+ */
250
+ export function* proposeGroupDelegationApproval(deps, request, inputs) {
251
+ const { hby } = deps;
252
+ const delegated = hby.db.getEvtSerder(request.delegate, request.said);
253
+ if (delegationApprovalStatus(hby, request).state === "unknown" || !delegated) {
254
+ throw new ValidationError(`No delegated ${request.ilk} ${request.said} is held for ${request.delegate}.`);
255
+ }
256
+ const group = hby.habs.get(request.delegator);
257
+ if (!group || !isLocalGroupHab(hby, group)) {
258
+ throw new ValidationError(`${request.delegator} is not a local group delegator.`);
259
+ }
260
+ if (findAnchor(hby.db, request))
261
+ return null;
262
+ return yield* proposeGroupInteraction(deps, group, [delegationSeal(delegated)], inputs);
263
+ }
264
+ /**
265
+ * Observe until at least one delegated event awaits the delegator, or the budget
266
+ * expires. Waits on the delegator's own `delegables.` escrow only: unrelated
267
+ * group proposals, queries or OOBIs never hold it open. Starts no worker; the
268
+ * host's runtime brings the request in through its mailbox or witness polling.
269
+ *
270
+ * `timed-out` means no request reached local state in the budget, which is also
271
+ * what a request that was already approved and committed looks like.
272
+ */
273
+ export function* waitForPendingDelegation(deps, filter = {}, { timeoutMs = DELEGATION_APPROVAL_TIMEOUT_MS } = {}) {
274
+ const done = yield* observeUntil(() => listPendingDelegations(deps, filter).length > 0, { timeoutMs });
275
+ const status = listPendingDelegations(deps, filter);
276
+ if (!done && status.length === 0)
277
+ return { outcome: "timed-out", status, timeoutMs };
278
+ for (const request of status)
279
+ deps.changes?.push({ kind: "delegation", id: `${request.delegate}:${request.sn}` });
280
+ return { outcome: "settled", status };
281
+ }
282
+ /**
283
+ * Observe a group delegator's request until its anchor is in the group's KEL and
284
+ * receipted by witnesses, or the delegate's event is already accepted. This is
285
+ * the point where {@link requestDelegatedEvent} can ask for the delegate's event.
286
+ */
287
+ export function* waitForDelegationAnchor(deps, request, { timeoutMs = DELEGATION_APPROVAL_TIMEOUT_MS } = {}) {
288
+ const ready = (status) => status.state === "approved" || (status.state === "anchored" && status.receipted);
289
+ const done = yield* observeUntil(() => ready(delegationApprovalStatus(deps, request)), { timeoutMs });
290
+ const status = delegationApprovalStatus(deps, request);
291
+ if (!done && !ready(status))
292
+ return { outcome: "timed-out", status, timeoutMs };
293
+ return { outcome: "settled", status };
294
+ }
295
+ /**
296
+ * Observe the request until it is `approved` or the budget expires. Starts no
297
+ * worker; the host's runtime brings the delegate's event in.
298
+ */
299
+ export function* waitForDelegationApproval(deps, request, { timeoutMs = DELEGATION_APPROVAL_TIMEOUT_MS } = {}) {
300
+ const read = () => {
301
+ deps.settle?.();
302
+ return delegationApprovalStatus(deps, request);
303
+ };
304
+ const done = yield* observeUntil(() => read().state === "approved", { timeoutMs });
305
+ const status = read();
306
+ if (!done || status.state !== "approved")
307
+ return { outcome: "timed-out", status, timeoutMs };
308
+ deps.changes?.push({ kind: "delegation", id: `${request.delegate}:${request.sn}` });
309
+ return { outcome: "settled", status };
310
+ }
311
+ /**
312
+ * Remove the controller notices (`/delegate/request`) that announced this
313
+ * request. Local only: the delegated event stays in escrow and the delegate is
314
+ * told nothing, so the request can still be approved or listed later.
315
+ *
316
+ * @returns Number of notices removed.
317
+ */
318
+ export function dismissDelegation(deps, request) {
319
+ const stale = [];
320
+ for (let start = 0;; start += 25) {
321
+ const page = deps.notifier.list(start, 25);
322
+ for (const note of page) {
323
+ const attrs = note.pad.a;
324
+ const evt = attrs["ked"];
325
+ if (attrs["r"] === DELEGATE_REQUEST_ROUTE && attrs["delpre"] === request.delegator && evt?.d === request.said) {
326
+ stale.push(note.pad.i);
327
+ }
328
+ }
329
+ if (page.length < 25)
330
+ break;
331
+ }
332
+ return stale.filter((rid) => deps.notifier.remove(rid)).length;
333
+ }