keri-ts 0.6.0 → 0.7.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 (79) hide show
  1. package/esm/cesr/mod.js +1 -0
  2. package/esm/cesr/src/core/parser-frame-parser.js +3 -0
  3. package/esm/cesr/src/primitives/matter.js +7 -0
  4. package/esm/cesr/src/tables/counter.tables.generated.js +1 -5
  5. package/esm/cesr/src/tables/hard-code-tables.js +39 -0
  6. package/esm/cesr/src/tables/indexer.tables.generated.js +1 -9
  7. package/esm/cesr/src/tables/matter.tables.generated.js +1 -14
  8. package/esm/cesr/src/version.js +7 -4
  9. package/esm/keri/src/app/agent-runtime.js +11 -5
  10. package/esm/keri/src/app/authenticating.js +15 -4
  11. package/esm/keri/src/app/cli/delegate.js +28 -0
  12. package/esm/keri/src/app/cli/notifications.js +4 -0
  13. package/esm/keri/src/app/delegating.js +21 -0
  14. package/esm/keri/src/app/forwarding.js +59 -13
  15. package/esm/keri/src/app/habbing.js +1 -0
  16. package/esm/keri/src/app/httping.js +12 -7
  17. package/esm/keri/src/app/keeping.js +3 -3
  18. package/esm/keri/src/app/mailbox-sse.js +11 -9
  19. package/esm/keri/src/app/notifying.js +11 -0
  20. package/esm/keri/src/app/oobiery.js +15 -4
  21. package/esm/keri/src/app/query-transport.js +3 -0
  22. package/esm/keri/src/app/runtime-services.js +22 -0
  23. package/esm/keri/src/app/version.js +7 -4
  24. package/esm/keri/src/core/eventing.js +40 -0
  25. package/esm/keri/src/core/kever.js +9 -0
  26. package/esm/keri/src/core/protocol-eventing.js +15 -0
  27. package/esm/keri/src/db/noting.js +4 -0
  28. package/esm/keri/src/runtime/index.js +1 -0
  29. package/package.json +2 -2
  30. package/types/cesr/mod.d.ts +1 -0
  31. package/types/cesr/mod.d.ts.map +1 -1
  32. package/types/cesr/src/core/parser-frame-parser.d.ts.map +1 -1
  33. package/types/cesr/src/primitives/matter.d.ts.map +1 -1
  34. package/types/cesr/src/tables/counter.tables.generated.d.ts +1 -1
  35. package/types/cesr/src/tables/counter.tables.generated.d.ts.map +1 -1
  36. package/types/cesr/src/tables/hard-code-tables.d.ts +15 -0
  37. package/types/cesr/src/tables/hard-code-tables.d.ts.map +1 -0
  38. package/types/cesr/src/tables/indexer.tables.generated.d.ts +1 -1
  39. package/types/cesr/src/tables/indexer.tables.generated.d.ts.map +1 -1
  40. package/types/cesr/src/tables/matter.tables.generated.d.ts +1 -1
  41. package/types/cesr/src/tables/matter.tables.generated.d.ts.map +1 -1
  42. package/types/cesr/src/version.d.ts +7 -4
  43. package/types/cesr/src/version.d.ts.map +1 -1
  44. package/types/keri/src/app/agent-runtime.d.ts +5 -1
  45. package/types/keri/src/app/agent-runtime.d.ts.map +1 -1
  46. package/types/keri/src/app/authenticating.d.ts +7 -1
  47. package/types/keri/src/app/authenticating.d.ts.map +1 -1
  48. package/types/keri/src/app/cli/delegate.d.ts +14 -0
  49. package/types/keri/src/app/cli/delegate.d.ts.map +1 -1
  50. package/types/keri/src/app/cli/notifications.d.ts +10 -0
  51. package/types/keri/src/app/cli/notifications.d.ts.map +1 -1
  52. package/types/keri/src/app/delegating.d.ts +23 -0
  53. package/types/keri/src/app/delegating.d.ts.map +1 -1
  54. package/types/keri/src/app/forwarding.d.ts +39 -9
  55. package/types/keri/src/app/forwarding.d.ts.map +1 -1
  56. package/types/keri/src/app/habbing.d.ts.map +1 -1
  57. package/types/keri/src/app/httping.d.ts +10 -4
  58. package/types/keri/src/app/httping.d.ts.map +1 -1
  59. package/types/keri/src/app/keeping.d.ts +2 -2
  60. package/types/keri/src/app/mailbox-sse.d.ts +8 -4
  61. package/types/keri/src/app/mailbox-sse.d.ts.map +1 -1
  62. package/types/keri/src/app/notifying.d.ts +23 -0
  63. package/types/keri/src/app/notifying.d.ts.map +1 -1
  64. package/types/keri/src/app/oobiery.d.ts +7 -3
  65. package/types/keri/src/app/oobiery.d.ts.map +1 -1
  66. package/types/keri/src/app/query-transport.d.ts.map +1 -1
  67. package/types/keri/src/app/runtime-services.d.ts +26 -0
  68. package/types/keri/src/app/runtime-services.d.ts.map +1 -0
  69. package/types/keri/src/app/version.d.ts +7 -4
  70. package/types/keri/src/app/version.d.ts.map +1 -1
  71. package/types/keri/src/core/eventing.d.ts +30 -0
  72. package/types/keri/src/core/eventing.d.ts.map +1 -1
  73. package/types/keri/src/core/kever.d.ts.map +1 -1
  74. package/types/keri/src/core/protocol-eventing.d.ts +8 -0
  75. package/types/keri/src/core/protocol-eventing.d.ts.map +1 -1
  76. package/types/keri/src/db/noting.d.ts +2 -0
  77. package/types/keri/src/db/noting.d.ts.map +1 -1
  78. package/types/keri/src/runtime/index.d.ts +1 -0
  79. package/types/keri/src/runtime/index.d.ts.map +1 -1
@@ -25,6 +25,7 @@ import { closeResponseBody, fetchResponseHandle, fetchResponseHandleOrNull } fro
25
25
  import { parseMailboxSse, readMailboxSseBody } from "./mailbox-sse.js";
26
26
  import { directDeliveryEndpoints, firstSortedEndpoint, getOutboxer, mailboxDeliveryEndpoints, mailboxPollEndpoints, mailboxTopicKey, } from "./mailboxing.js";
27
27
  import { Organizer } from "./organizing.js";
28
+ import { defaultRuntimeServices } from "./runtime-services.js";
28
29
  import { runtimeTurn } from "./runtime-turn.js";
29
30
  /**
30
31
  * Mailbox-first postman for outbound EXN transport.
@@ -37,7 +38,7 @@ import { runtimeTurn } from "./runtime-turn.js";
37
38
  * - persist sender-side mailbox retry state per mailbox endpoint
38
39
  */
39
40
  export class Poster {
40
- constructor(hby, { mailboxer, outboxer, } = {}) {
41
+ constructor(hby, options = {}) {
41
42
  Object.defineProperty(this, "hby", {
42
43
  enumerable: true,
43
44
  configurable: true,
@@ -62,10 +63,18 @@ export class Poster {
62
63
  writable: true,
63
64
  value: void 0
64
65
  });
66
+ Object.defineProperty(this, "services", {
67
+ enumerable: true,
68
+ configurable: true,
69
+ writable: true,
70
+ value: void 0
71
+ });
72
+ const { mailboxer, outboxer, services = defaultRuntimeServices } = options;
65
73
  this.hby = hby;
66
74
  this.mailboxer = mailboxer ?? null;
67
75
  this.outboxer = outboxer ?? getOutboxer(hby);
68
76
  this.organizer = new Organizer(hby);
77
+ this.services = services;
69
78
  }
70
79
  /**
71
80
  * Resolve one CLI or user recipient input into the actual destination prefix.
@@ -164,7 +173,7 @@ export class Poster {
164
173
  const directEndpoints = directDeliveryEndpoints(hab, recipient);
165
174
  if (directEndpoints.length > 0) {
166
175
  for (const endpoint of directEndpoints) {
167
- yield* postCesrMessage(endpoint.url, message, this.hby.cesrBodyMode, endpoint.eid);
176
+ yield* postCesrMessage(endpoint.url, message, this.hby.cesrBodyMode, endpoint.eid, this.services);
168
177
  deliveries.push(endpoint.url);
169
178
  }
170
179
  return { serder, deliveries, queued };
@@ -241,7 +250,7 @@ export class Poster {
241
250
  const directEndpoints = directDeliveryEndpoints(hab, recipient);
242
251
  if (directEndpoints.length > 0) {
243
252
  for (const endpoint of directEndpoints) {
244
- yield* postCesrMessage(endpoint.url, args.message, this.hby.cesrBodyMode, endpoint.eid);
253
+ yield* postCesrMessage(endpoint.url, args.message, this.hby.cesrBodyMode, endpoint.eid, this.services);
245
254
  deliveries.push(endpoint.url);
246
255
  }
247
256
  return { deliveries, queued };
@@ -329,7 +338,7 @@ export class Poster {
329
338
  topic,
330
339
  message,
331
340
  });
332
- yield* postCesrMessage(endpoint.url, concatBytes(introduce(hab, endpoint.eid), forwarded), this.hby.cesrBodyMode, endpoint.eid);
341
+ yield* postCesrMessage(endpoint.url, concatBytes(introduce(hab, endpoint.eid), forwarded), this.hby.cesrBodyMode, endpoint.eid, this.services);
333
342
  }
334
343
  /** Require provider mailbox storage for local mailbox-target delivery. */
335
344
  requireMailboxer() {
@@ -418,6 +427,9 @@ Object.defineProperty(ForwardHandler, "resource", {
418
427
  value: "/fwd"
419
428
  });
420
429
  function hostCanStoreForwardRecipient(hby, recipient, recipientKever, hostAid) {
430
+ // A local host may store forwarded traffic only when it is an authorized
431
+ // witness/mailbox/agent for the recipient. This keeps mailbox storage from
432
+ // becoming a generic unauthenticated relay.
421
433
  if (recipientKever.wits.includes(hostAid)) {
422
434
  return true;
423
435
  }
@@ -430,6 +442,8 @@ function hostCanStoreForwardRecipient(hby, recipient, recipientKever, hostAid) {
430
442
  return false;
431
443
  }
432
444
  function hasLocalStoreForwardHost(hby, recipient, recipientKever) {
445
+ // Check all local prefixes because a multi-role runtime can host a mailbox
446
+ // AID that is distinct from the controller currently processing the EXN.
433
447
  for (const hostAid of hby.db.prefixes) {
434
448
  if (hostCanStoreForwardRecipient(hby, recipient, recipientKever, hostAid)) {
435
449
  return true;
@@ -446,7 +460,7 @@ function hasLocalStoreForwardHost(hby, recipient, recipientKever) {
446
460
  * - ingests retrieved payloads back through the shared `Reactor`
447
461
  */
448
462
  export class MailboxPoller {
449
- constructor(hby, mailboxDirector, { timeouts, } = {}) {
463
+ constructor(hby, mailboxDirector, options = {}) {
450
464
  Object.defineProperty(this, "hby", {
451
465
  enumerable: true,
452
466
  configurable: true,
@@ -465,15 +479,30 @@ export class MailboxPoller {
465
479
  writable: true,
466
480
  value: void 0
467
481
  });
482
+ Object.defineProperty(this, "services", {
483
+ enumerable: true,
484
+ configurable: true,
485
+ writable: true,
486
+ value: void 0
487
+ });
488
+ Object.defineProperty(this, "pollTransport", {
489
+ enumerable: true,
490
+ configurable: true,
491
+ writable: true,
492
+ value: void 0
493
+ });
468
494
  Object.defineProperty(this, "localCursors", {
469
495
  enumerable: true,
470
496
  configurable: true,
471
497
  writable: true,
472
498
  value: new Map()
473
499
  });
500
+ const { timeouts, services = defaultRuntimeServices, pollTransport = defaultMailboxPollTransport, } = options;
474
501
  this.hby = hby;
475
502
  this.mailboxDirector = mailboxDirector;
476
503
  this.timeoutPolicy = normalizeMailboxPollingTimeoutPolicy(timeouts);
504
+ this.services = services;
505
+ this.pollTransport = pollTransport;
477
506
  }
478
507
  /** Configure mailbox polling state for future expansion; currently a no-op. */
479
508
  configure(_args = {}) {
@@ -498,7 +527,7 @@ export class MailboxPoller {
498
527
  return [];
499
528
  }
500
529
  const batches = [];
501
- const deadline = Date.now() + positiveTimeoutMs(budgetMs, this.timeoutPolicy.commandLocalBudgetMs);
530
+ const deadline = this.services.clock.now() + positiveTimeoutMs(budgetMs, this.timeoutPolicy.commandLocalBudgetMs);
502
531
  for (const pre of this.hby.prefixes) {
503
532
  const local = this.readLocalMailbox(pre, topics);
504
533
  if (local.length > 0) {
@@ -508,7 +537,7 @@ export class MailboxPoller {
508
537
  for (const hab of this.hby.habs.values()) {
509
538
  const remoteEndpoints = mailboxPollEndpoints(this.hby, hab);
510
539
  for (const endpoint of remoteEndpoints) {
511
- const remainingBudgetMs = deadline - Date.now();
540
+ const remainingBudgetMs = deadline - this.services.clock.now();
512
541
  if (remainingBudgetMs <= 0) {
513
542
  return batches;
514
543
  }
@@ -588,7 +617,14 @@ export class MailboxPoller {
588
617
  */
589
618
  *pollRemoteEndpointOnce(hab, endpoint, budgetMs) {
590
619
  const cursor = this.mailboxDirector.remoteQueryCursor(hab.pre, endpoint.eid);
591
- const messages = yield* fetchMailboxMessages(hab, endpoint.eid, endpoint.url, cursor, this.hby.cesrBodyMode, mailboxFetchTimeoutPolicy(this.timeoutPolicy, budgetMs));
620
+ const messages = yield* this.pollTransport.poll({
621
+ hab,
622
+ endpoint,
623
+ topics: cursor,
624
+ bodyMode: this.hby.cesrBodyMode,
625
+ timeouts: mailboxFetchTimeoutPolicy(this.timeoutPolicy, budgetMs),
626
+ services: this.services,
627
+ });
592
628
  if (messages.length === 0) {
593
629
  return null;
594
630
  }
@@ -766,6 +802,8 @@ function extractForwardedMessage(serder, attachments) {
766
802
  if (!evt || typeof evt !== "object" || Array.isArray(evt)) {
767
803
  return null;
768
804
  }
805
+ // Rebuild the embedded event from SAD plus only the attachment material
806
+ // pathed to `/e/evt`; other pathed groups belong to the outer `/fwd` message.
769
807
  const embedded = new SerderKERI({ sad: evt });
770
808
  const messageParts = [embedded.raw];
771
809
  for (const attachment of attachments) {
@@ -794,15 +832,20 @@ function forwardedAttachmentForPath(raw, path) {
794
832
  }
795
833
  return raw.slice(offset + pather.fullSize);
796
834
  }
835
+ const defaultMailboxPollTransport = {
836
+ *poll(args) {
837
+ return yield* fetchMailboxMessages(args.hab, args.endpoint.eid, args.endpoint.url, args.topics, args.bodyMode, args.timeouts, args.services);
838
+ },
839
+ };
797
840
  /**
798
841
  * Query one remote mailbox endpoint and parse any returned mailbox SSE events.
799
842
  *
800
843
  * The query cursor values are "next wanted" ordinals, matching `mbx`
801
844
  * semantics.
802
845
  */
803
- function* fetchMailboxMessages(hab, src, url, topics, bodyMode, timeouts) {
846
+ function* fetchMailboxMessages(hab, src, url, topics, bodyMode, timeouts, services = defaultRuntimeServices) {
804
847
  const query = hab.query(hab.pre, src, { topics }, "mbx");
805
- const handle = yield* fetchMailboxQueryResponse(url, query, bodyMode, src, timeouts.requestOpenTimeoutMs);
848
+ const handle = yield* fetchMailboxQueryResponse(url, query, bodyMode, src, timeouts.requestOpenTimeoutMs, services);
806
849
  if (!handle) {
807
850
  return [];
808
851
  }
@@ -814,6 +857,7 @@ function* fetchMailboxMessages(hab, src, url, topics, bodyMode, timeouts) {
814
857
  const text = yield* readMailboxSseBody(response, controller, {
815
858
  idleTimeoutMs: timeouts.readIdleTimeoutMs,
816
859
  maxDurationMs: timeouts.maxReadDurationMs,
860
+ services,
817
861
  });
818
862
  return parseMailboxSse(text);
819
863
  }
@@ -827,7 +871,7 @@ function* fetchMailboxMessages(hab, src, url, topics, bodyMode, timeouts) {
827
871
  *
828
872
  * Message parsing and SSE body policy remain with `fetchMailboxMessages(...)`.
829
873
  */
830
- function* fetchMailboxQueryResponse(url, query, bodyMode, destination, timeoutMs) {
874
+ function* fetchMailboxQueryResponse(url, query, bodyMode, destination, timeoutMs, services = defaultRuntimeServices) {
831
875
  const request = buildCesrRequest(query, {
832
876
  bodyMode,
833
877
  destination,
@@ -836,7 +880,7 @@ function* fetchMailboxQueryResponse(url, query, bodyMode, destination, timeoutMs
836
880
  method: "POST",
837
881
  headers: request.headers,
838
882
  body: request.body,
839
- }, { timeoutMs });
883
+ }, { timeoutMs, services });
840
884
  }
841
885
  function normalizeMailboxPollingTimeoutPolicy(overrides) {
842
886
  const defaults = MailboxPoller.DefaultTimeoutPolicy;
@@ -870,7 +914,7 @@ function positiveTimeoutMs(value, fallback) {
870
914
  * Body-mode policy:
871
915
  * - send the whole provided payload in the request body
872
916
  */
873
- export function* postCesrMessage(url, body, bodyMode, destination) {
917
+ export function* postCesrMessage(url, body, bodyMode, destination, services = defaultRuntimeServices) {
874
918
  const requests = bodyMode === "header" ? splitCesrStream(body) : [body];
875
919
  for (const currentBody of requests) {
876
920
  const request = buildCesrRequest(currentBody, {
@@ -881,6 +925,8 @@ export function* postCesrMessage(url, body, bodyMode, destination) {
881
925
  method: "POST",
882
926
  headers: request.headers,
883
927
  body: request.body,
928
+ }, {
929
+ services,
884
930
  });
885
931
  yield* closeResponseBody(response);
886
932
  if (!response.ok) {
@@ -409,6 +409,7 @@ export class Hab {
409
409
  algo,
410
410
  salt,
411
411
  tier,
412
+ temp: this.ks.temp,
412
413
  });
413
414
  if (!transferable && verfers.length === 1) {
414
415
  prefixCode = verfers[0].code;
@@ -1,4 +1,5 @@
1
1
  import { action } from "effection";
2
+ import { defaultRuntimeServices } from "./runtime-services.js";
2
3
  /**
3
4
  * Fetch one HTTP response under Effection cancellation control.
4
5
  *
@@ -8,8 +9,9 @@ import { action } from "effection";
8
9
  * - hand the caller both the `Response` and the controller when body policy
9
10
  * needs later abort access, such as mailbox SSE long-poll reads
10
11
  */
11
- export function* fetchResponseHandle(url, init = {}) {
12
- const handle = yield* fetchResponseHandleInternal(url, init);
12
+ export function* fetchResponseHandle(url, init = {}, options = {}) {
13
+ const { services = defaultRuntimeServices } = options;
14
+ const handle = yield* fetchResponseHandleInternal(url, init, { services });
13
15
  if (!handle) {
14
16
  throw new Error("HTTP response handle unexpectedly resolved to null.");
15
17
  }
@@ -21,10 +23,12 @@ export function* fetchResponseHandle(url, init = {}) {
21
23
  * This is for callers such as mailbox polling that treat request timeout as a
22
24
  * normal "no response yet" outcome instead of as an exception.
23
25
  */
24
- export function* fetchResponseHandleOrNull(url, init = {}, { timeoutMs, } = {}) {
26
+ export function* fetchResponseHandleOrNull(url, init = {}, options = {}) {
27
+ const { timeoutMs, services = defaultRuntimeServices } = options;
25
28
  return yield* fetchResponseHandleInternal(url, init, {
26
29
  timeoutMs,
27
30
  nullOnAbort: true,
31
+ services,
28
32
  });
29
33
  }
30
34
  /**
@@ -43,21 +47,22 @@ export function* closeResponseBody(response) {
43
47
  return () => { };
44
48
  });
45
49
  }
46
- function* fetchResponseHandleInternal(url, init, { timeoutMs, nullOnAbort = false, } = {}) {
50
+ function* fetchResponseHandleInternal(url, init, options = {}) {
51
+ const { timeoutMs, nullOnAbort = false, services = defaultRuntimeServices, } = options;
47
52
  return yield* action((resolve, reject) => {
48
53
  const controller = new AbortController();
49
54
  let settled = false;
50
55
  let timedOut = false;
51
- const timeoutId = timeoutMs === undefined ? undefined : setTimeout(() => {
56
+ const timeoutId = timeoutMs === undefined ? undefined : services.clock.setTimeout(() => {
52
57
  timedOut = true;
53
58
  controller.abort();
54
59
  }, timeoutMs);
55
60
  const clearTimer = () => {
56
61
  if (timeoutId !== undefined) {
57
- clearTimeout(timeoutId);
62
+ services.clock.clearTimeout(timeoutId);
58
63
  }
59
64
  };
60
- fetch(url, { ...init, signal: controller.signal }).then((response) => {
65
+ services.http.fetch(url, { ...init, signal: controller.signal }).then((response) => {
61
66
  settled = true;
62
67
  clearTimer();
63
68
  resolve({ response, controller });
@@ -551,9 +551,9 @@ export class Manager {
551
551
  ridx: lot.ridx,
552
552
  kidx: lot.kidx + offset,
553
553
  transferable: verfer.transferable,
554
- // Persisted keeper parameters do not retain temp/stretch mode, so
555
- // derived signing must use the normal persisted-sequence behavior.
556
- temp: false,
554
+ // Temp keepers create temp-derived keys, so path re-derivation must
555
+ // mirror the active keeper mode. Durable keepers remain production-cost.
556
+ temp: this.ks.temp,
557
557
  })[0];
558
558
  if (!signer || signer.verfer.qb64 !== pub) {
559
559
  throw new Error(`Derived signer mismatch for pre=${pre} ri=${lot.ridx} kidx=${lot.kidx + offset}.`);
@@ -1,4 +1,5 @@
1
1
  import { action } from "effection";
2
+ import { defaultRuntimeServices } from "./runtime-services.js";
2
3
  /** Shared encoder for mailbox SSE parsing. */
3
4
  const textEncoder = new TextEncoder();
4
5
  const DEFAULT_READ_IDLE_TIMEOUT_MS = 500;
@@ -10,7 +11,8 @@ const DEFAULT_MAX_READ_DURATION_MS = 30_000;
10
11
  * callers use an explicit read budget and idle timeout instead of waiting for
11
12
  * `response.text()` to resolve.
12
13
  */
13
- export function* readMailboxSseBody(response, controller, { idleTimeoutMs = DEFAULT_READ_IDLE_TIMEOUT_MS, maxDurationMs = DEFAULT_MAX_READ_DURATION_MS, } = {}) {
14
+ export function* readMailboxSseBody(response, controller, options = {}) {
15
+ const { idleTimeoutMs = DEFAULT_READ_IDLE_TIMEOUT_MS, maxDurationMs = DEFAULT_MAX_READ_DURATION_MS, services = defaultRuntimeServices, } = options;
14
16
  const body = response.body;
15
17
  if (!body) {
16
18
  return "";
@@ -18,14 +20,14 @@ export function* readMailboxSseBody(response, controller, { idleTimeoutMs = DEFA
18
20
  const reader = body.getReader();
19
21
  const decoder = new TextDecoder();
20
22
  let text = "";
21
- const deadline = Date.now() + maxDurationMs;
23
+ const deadline = services.clock.now() + maxDurationMs;
22
24
  const timedOut = Symbol("timedOut");
23
25
  try {
24
- while (Date.now() < deadline) {
25
- const remaining = Math.max(1, Math.min(idleTimeoutMs, deadline - Date.now()));
26
+ while (services.clock.now() < deadline) {
27
+ const remaining = Math.max(1, Math.min(idleTimeoutMs, deadline - services.clock.now()));
26
28
  const next = yield* action((resolve, reject) => {
27
29
  let settled = false;
28
- const timeoutId = setTimeout(() => {
30
+ const timeoutId = services.clock.setTimeout(() => {
29
31
  settled = true;
30
32
  resolve(timedOut);
31
33
  }, remaining);
@@ -34,23 +36,23 @@ export function* readMailboxSseBody(response, controller, { idleTimeoutMs = DEFA
34
36
  return;
35
37
  }
36
38
  settled = true;
37
- clearTimeout(timeoutId);
39
+ services.clock.clearTimeout(timeoutId);
38
40
  resolve(result);
39
41
  }).catch((error) => {
40
42
  if (settled) {
41
43
  return;
42
44
  }
43
45
  settled = true;
44
- clearTimeout(timeoutId);
46
+ services.clock.clearTimeout(timeoutId);
45
47
  reject(error);
46
48
  });
47
49
  return () => {
48
- clearTimeout(timeoutId);
50
+ services.clock.clearTimeout(timeoutId);
49
51
  };
50
52
  });
51
53
  if (next === timedOut) {
52
54
  if (parseMailboxSse(text).length > 0
53
- || Date.now() + idleTimeoutMs >= deadline) {
55
+ || services.clock.now() + idleTimeoutMs >= deadline) {
54
56
  controller.abort();
55
57
  break;
56
58
  }
@@ -14,6 +14,7 @@ export function notice(attrs, options = {}) {
14
14
  },
15
15
  });
16
16
  }
17
+ /** Derive sidecar-open options from the owning `Habery` path/layout settings. */
17
18
  export function noterOptionsForHabery(hby, options = {}) {
18
19
  return {
19
20
  name: hby.name,
@@ -26,11 +27,16 @@ export function noterOptionsForHabery(hby, options = {}) {
26
27
  ...options,
27
28
  };
28
29
  }
30
+ /** Open the notification sidecar that belongs to one `Habery`. */
29
31
  export function* openNoterForHabery(hby, options = {}) {
30
32
  return yield* createNoter(noterOptionsForHabery(hby, options));
31
33
  }
32
34
  /**
33
35
  * Signed durable controller notifications with transient `/notification` pings.
36
+ *
37
+ * The detached signature is verified on every read/mutation path so operators
38
+ * can trust CLI output even though notification payloads are intentionally
39
+ * application-specific maps.
34
40
  */
35
41
  export class Notifier {
36
42
  constructor(hby, { noter, signaler, }) {
@@ -56,6 +62,7 @@ export class Notifier {
56
62
  this.noter = noter;
57
63
  this.signaler = signaler ?? new Signaler();
58
64
  }
65
+ /** Add one unread signed notice and emit a transient add signal. */
59
66
  add(attrs) {
60
67
  const signator = this.requireSignator();
61
68
  const note = notice(attrs);
@@ -66,12 +73,15 @@ export class Notifier {
66
73
  this.emitSignal("add", note);
67
74
  return true;
68
75
  }
76
+ /** List verified notices in durable datetime order. */
69
77
  list(start = 0, limit = 25) {
70
78
  return this.noter.listNotices(start, limit).map(([note, cigar]) => this.requireVerifiedNotice(note, cigar));
71
79
  }
80
+ /** Count durable notices without verifying each payload. */
72
81
  count() {
73
82
  return this.noter.countNotices();
74
83
  }
84
+ /** Mark one notice read and re-sign its updated pad. */
75
85
  markRead(rid) {
76
86
  const [note] = this.requireNoticePair(rid);
77
87
  if (note.read) {
@@ -86,6 +96,7 @@ export class Notifier {
86
96
  this.emitSignal("mar", note);
87
97
  return true;
88
98
  }
99
+ /** Remove one verified notice and emit a transient remove signal. */
89
100
  remove(rid) {
90
101
  const [note] = this.requireNoticePair(rid);
91
102
  if (!this.noter.removeNotice(rid)) {
@@ -8,6 +8,7 @@ import { Roles } from "../core/roles.js";
8
8
  import { acceptReplyDecision, unverifiedReplyDecision } from "../core/routing.js";
9
9
  import { closeResponseBody, fetchResponseHandle } from "./httping.js";
10
10
  import { persistResolvedContact } from "./organizing.js";
11
+ import { defaultRuntimeServices } from "./runtime-services.js";
11
12
  import { runtimeTurn } from "./runtime-turn.js";
12
13
  export const OOBI_REQUEST_ROUTE = "/oobis";
13
14
  /**
@@ -24,7 +25,7 @@ export const OOBI_REQUEST_ROUTE = "/oobis";
24
25
  * leak back to the `AgentRuntime` root
25
26
  */
26
27
  export class Oobiery {
27
- constructor(hby, reactor, { cues } = {}) {
28
+ constructor(hby, reactor, options = {}) {
28
29
  Object.defineProperty(this, "hby", {
29
30
  enumerable: true,
30
31
  configurable: true,
@@ -43,9 +44,17 @@ export class Oobiery {
43
44
  writable: true,
44
45
  value: void 0
45
46
  });
47
+ Object.defineProperty(this, "services", {
48
+ enumerable: true,
49
+ configurable: true,
50
+ writable: true,
51
+ value: void 0
52
+ });
53
+ const { cues, services = defaultRuntimeServices } = options;
46
54
  this.hby = hby;
47
55
  this.reactor = reactor;
48
56
  this.cues = cues ?? new Deck();
57
+ this.services = services;
49
58
  }
50
59
  /** Register `/introduce` on the shared reply router. */
51
60
  registerReplyRoutes(router = this.reactor.router) {
@@ -149,7 +158,7 @@ export class Oobiery {
149
158
  said: meta.said ?? null,
150
159
  };
151
160
  this.pinQueueStore(kind, url, queuedRecord);
152
- const response = yield* fetchOobiResponse(url);
161
+ const response = yield* fetchOobiResponse(url, this.services);
153
162
  if (!response.ok) {
154
163
  yield* closeResponseBody(response);
155
164
  this.remQueueStore(kind, url);
@@ -532,8 +541,10 @@ export { OOBI_MAILBOX_TOPIC };
532
541
  * This is the real promise-adaptation boundary for remote OOBI retrieval. The
533
542
  * surrounding runtime stays operation-native.
534
543
  */
535
- function* fetchOobiResponse(url) {
536
- const { response } = yield* fetchResponseHandle(url);
544
+ function* fetchOobiResponse(url, services = defaultRuntimeServices) {
545
+ const { response } = yield* fetchResponseHandle(url, {}, {
546
+ services,
547
+ });
537
548
  return response;
538
549
  }
539
550
  /**
@@ -62,6 +62,8 @@ function* postQueryMessage(runtime, url, body, bodyMode, destination) {
62
62
  method: "POST",
63
63
  headers: request.headers,
64
64
  body: request.body,
65
+ }, {
66
+ services: runtime.services,
65
67
  });
66
68
  if (!response.ok) {
67
69
  throw new ValidationError(`Query delivery to ${url} failed with HTTP ${response.status}.`);
@@ -72,6 +74,7 @@ function* postQueryMessage(runtime, url, body, bodyMode, destination) {
72
74
  const text = yield* readMailboxSseBody(response, controller, {
73
75
  idleTimeoutMs: 500,
74
76
  maxDurationMs: 5_000,
77
+ services: runtime.services,
75
78
  });
76
79
  for (const message of parseMailboxSse(text)) {
77
80
  if (message.msg.length > 0) {
@@ -0,0 +1,22 @@
1
+ /** Production clock backed by the JavaScript runtime. */
2
+ export const defaultRuntimeClock = Object.freeze({
3
+ now: () => Date.now(),
4
+ setTimeout: (callback, ms) => setTimeout(callback, ms),
5
+ clearTimeout: (timer) => clearTimeout(timer),
6
+ });
7
+ /** Production HTTP client backed by global `fetch`. */
8
+ export const defaultRuntimeHttpClient = Object.freeze({
9
+ fetch: (url, init) => fetch(url, init),
10
+ });
11
+ /** Production runtime services. */
12
+ export const defaultRuntimeServices = Object.freeze({
13
+ clock: defaultRuntimeClock,
14
+ http: defaultRuntimeHttpClient,
15
+ });
16
+ /** Resolve partial runtime service overrides against production defaults. */
17
+ export function resolveRuntimeServices(services = {}) {
18
+ return Object.freeze({
19
+ clock: services.clock ?? defaultRuntimeClock,
20
+ http: services.http ?? defaultRuntimeHttpClient,
21
+ });
22
+ }
@@ -1,11 +1,14 @@
1
1
  /**
2
- * Generated by scripts/generate_versions.ts.
3
- * Do not edit by hand.
2
+ * Template used by scripts/generate_versions.ts for package version modules.
3
+ *
4
+ * The generator token-replaces the string placeholders below, then formats and
5
+ * compares the rendered output during version checks. Do not edit generated
6
+ * `src/version.ts` files by hand; edit this template instead.
4
7
  */
5
8
  /** Package semantic version copied from the owning package manifest. */
6
- export const PACKAGE_VERSION = "0.6.0";
9
+ export const PACKAGE_VERSION = "0.7.0";
7
10
  /** Optional build metadata stamp injected by release/CI workflows. */
8
- export const BUILD_METADATA = "build.9.11f27f1a6176caac7b15d7fba862023af598592f";
11
+ export const BUILD_METADATA = "build.12.6aeea4790568f27066636c48ec39d2f098e3c26d";
9
12
  /** User-facing version string with build metadata appended when present. */
10
13
  export const DISPLAY_VERSION = BUILD_METADATA
11
14
  ? `${PACKAGE_VERSION}+${BUILD_METADATA}`
@@ -172,6 +172,10 @@ export class Kevery {
172
172
  else if (sn !== null && !this.db.fullyWitnessed(kever.serder)) {
173
173
  return escrowQuery("notFullyWitnessed");
174
174
  }
175
+ // Reopened validators may have accepted a delegated event before its
176
+ // `.aess` source-seal hint was learned. Repair that hint before replay
177
+ // so downstream receivers get the same delegation attachment KERIpy
178
+ // would clone from persisted delegating-event state.
175
179
  this.repairReplaySourceSeal(kever);
176
180
  const msgs = [...this.db.clonePreIter(pre, fn)];
177
181
  if (kever.delpre) {
@@ -557,6 +561,9 @@ export class Kevery {
557
561
  return this.makeEscrowDecision("duplicitous", init, `Likely duplicitous inception for ${pre}; existing SAID=${kever.said}, got ${said}.`);
558
562
  }
559
563
  const duplicateSaid = this.db.kels.getLast(pre, sn);
564
+ // A duplicate accepted event may still carry late source-seal or receipt
565
+ // attachments. Preserve those through the duplicate path instead of
566
+ // rejecting the whole replay as already-seen.
560
567
  if (duplicateSaid && duplicateSaid === said && sn <= kever.sn) {
561
568
  return this.buildDuplicateDecision(kever, init);
562
569
  }
@@ -929,6 +936,9 @@ export class Kevery {
929
936
  emitAcceptanceCues(kever, serder, local) {
930
937
  const habord = this.db.getHab(kever.pre);
931
938
  if (habord?.mid) {
939
+ // Group habitats can be learned through remote traffic after reopen.
940
+ // Keep the live group-prefix indexes synchronized with durable habitat
941
+ // records before protected-party cue policy runs.
932
942
  this.db.prefixes.add(kever.pre);
933
943
  this.db.groups.add(kever.pre);
934
944
  }
@@ -1941,6 +1951,13 @@ export class Kevery {
1941
1951
  duplicate: "sameSaid",
1942
1952
  };
1943
1953
  }
1954
+ /**
1955
+ * Decide whether a duplicate event's attached source seal is safe to store.
1956
+ *
1957
+ * Duplicate replay is intentionally narrow: only an existing delegated
1958
+ * establishment event may learn a late source seal, and the seal must name
1959
+ * the exact delegator event already known to anchor this delegate event.
1960
+ */
1944
1961
  validDuplicateSourceSeal(kever, serder, sourceSeal) {
1945
1962
  const pre = serder.pre;
1946
1963
  const said = serder.said;
@@ -1956,6 +1973,14 @@ export class Kevery {
1956
1973
  return anchoring?.snh === sourceSeal.s.numh
1957
1974
  && anchoring.said === sourceSeal.d.qb64;
1958
1975
  }
1976
+ /**
1977
+ * Backfill `.aess` for an accepted delegated establishment before replay.
1978
+ *
1979
+ * KERIpy clones delegation attachments from durable state during replay.
1980
+ * `keri-ts` keeps replay construction decoupled, so this helper repairs the
1981
+ * accepted source-seal index from the delegator's sealing event when the
1982
+ * event body was accepted before the hint was persisted.
1983
+ */
1959
1984
  repairReplaySourceSeal(kever) {
1960
1985
  const serder = kever.serder;
1961
1986
  const pre = serder.pre;
@@ -1981,6 +2006,14 @@ export class Kevery {
1981
2006
  new Diger({ qb64: anchoring.said }),
1982
2007
  ]);
1983
2008
  }
2009
+ /**
2010
+ * Repair accepted delegated events after a delegator event is accepted.
2011
+ *
2012
+ * When the newly accepted event contains delegation anchors, any already
2013
+ * stored delegate establishment it names can learn the corresponding
2014
+ * source-seal hint immediately. This keeps later duplicate/replay handling
2015
+ * deterministic without rerunning full event acceptance.
2016
+ */
1984
2017
  repairAnchoredDelegationSourceSeals(serder) {
1985
2018
  const delpre = serder.pre;
1986
2019
  const said = serder.said;
@@ -2011,6 +2044,13 @@ export class Kevery {
2011
2044
  }
2012
2045
  }
2013
2046
  }
2047
+ /**
2048
+ * Repair source-seal hints for escrowed delegated establishment events.
2049
+ *
2050
+ * Escrow reprocessing may encounter a delegate event after the approving
2051
+ * delegator event was already accepted. This helper converts that discovered
2052
+ * anchor into the same `.aess` hint carried by live source-seal attachments.
2053
+ */
2014
2054
  repairEscrowDelegationSourceSeal(serder) {
2015
2055
  const pre = serder.pre;
2016
2056
  const said = serder.said;
@@ -1037,6 +1037,9 @@ export class Kever {
1037
1037
  // for escrow. Misfit checks come first so locally protected events do not
1038
1038
  // leak into a more permissive partial-signature or partial-delegation
1039
1039
  // class.
1040
+ // A local delegator reviewing someone else's delegated establishment is a
1041
+ // protected-party case, but it must enter `delegables` until the local
1042
+ // approval interaction/rotation is actually attached as a source seal.
1040
1043
  const localDelegatorApprovalCandidate = (input.serder.ilk === Ilks.dip || input.serder.ilk === Ilks.drt)
1041
1044
  && this.locallyDelegated(delpre)
1042
1045
  && !this.locallyOwned();
@@ -1158,6 +1161,9 @@ export class Kever {
1158
1161
  // A local delegator without an attached approval seal is not a generic
1159
1162
  // partial-delegation case. It is specifically waiting for local
1160
1163
  // out-of-band approval to be attached and reprocessed.
1164
+ // Local approval is represented by a real, already accepted delegator
1165
+ // event. The attached source seal is only authoritative when it resolves
1166
+ // to that accepted event and that event's anchors name this delegate event.
1161
1167
  const sourceSealApprovesEvent = input.sourceSeal
1162
1168
  ? this.lookupAcceptedDelegatingEvent(delpre, input.sourceSeal, input.serder) !== null
1163
1169
  : false;
@@ -1403,6 +1409,9 @@ export class Kever {
1403
1409
  if (!candidate || !candidate.said || candidate.sn === null) {
1404
1410
  return null;
1405
1411
  }
1412
+ // Accepted first-seen state is the primary signal, but restored KELs may
1413
+ // have authoritative sequence membership before first-seen metadata has
1414
+ // been reconstructed. Accept either durable signal for approval lookup.
1406
1415
  const accepted = !!this.db.fons.get(dgKey(delpre, candidate.said))
1407
1416
  || this.db.kels.getLast(delpre, candidate.sn) === candidate.said;
1408
1417
  if (!accepted)