@dxos/client-services 0.8.4-main.9be5663bfe → 0.8.4-main.abd8ff62ef

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 (163) hide show
  1. package/dist/lib/browser/{chunk-CK3KJB3B.mjs → chunk-KW4WMU5R.mjs} +1310 -3084
  2. package/dist/lib/browser/chunk-KW4WMU5R.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-NQSC7HOE.mjs → chunk-XJRPB3GA.mjs} +1 -1
  4. package/dist/lib/browser/index.mjs +100 -197
  5. package/dist/lib/browser/index.mjs.map +3 -3
  6. package/dist/lib/browser/meta.json +1 -1
  7. package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs +1 -6
  8. package/dist/lib/browser/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +2 -2
  9. package/dist/lib/browser/packlets/diagnostics/diagnostics-broadcast.mjs +1 -1
  10. package/dist/lib/browser/packlets/locks/browser.mjs +9 -49
  11. package/dist/lib/browser/packlets/locks/browser.mjs.map +2 -2
  12. package/dist/lib/browser/packlets/locks/node.mjs +4 -22
  13. package/dist/lib/browser/packlets/locks/node.mjs.map +2 -2
  14. package/dist/lib/browser/testing/index.mjs +7 -27
  15. package/dist/lib/browser/testing/index.mjs.map +2 -2
  16. package/dist/lib/node-esm/{chunk-PKEGMOQ4.mjs → chunk-2DT3MZRL.mjs} +1 -1
  17. package/dist/lib/node-esm/{chunk-WHBWCIEN.mjs → chunk-NDMKP2CH.mjs} +1310 -3084
  18. package/dist/lib/node-esm/chunk-NDMKP2CH.mjs.map +7 -0
  19. package/dist/lib/node-esm/index.mjs +100 -197
  20. package/dist/lib/node-esm/index.mjs.map +3 -3
  21. package/dist/lib/node-esm/meta.json +1 -1
  22. package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs +1 -6
  23. package/dist/lib/node-esm/packlets/diagnostics/browser-diagnostics-broadcast.mjs.map +2 -2
  24. package/dist/lib/node-esm/packlets/diagnostics/diagnostics-broadcast.mjs +1 -1
  25. package/dist/lib/node-esm/packlets/locks/browser.mjs +9 -49
  26. package/dist/lib/node-esm/packlets/locks/browser.mjs.map +2 -2
  27. package/dist/lib/node-esm/packlets/locks/node.mjs +4 -22
  28. package/dist/lib/node-esm/packlets/locks/node.mjs.map +2 -2
  29. package/dist/lib/node-esm/testing/index.mjs +7 -27
  30. package/dist/lib/node-esm/testing/index.mjs.map +2 -2
  31. package/dist/types/src/packlets/agents/edge-agent-manager.d.ts.map +1 -1
  32. package/dist/types/src/packlets/agents/edge-agent-service.d.ts +2 -1
  33. package/dist/types/src/packlets/agents/edge-agent-service.d.ts.map +1 -1
  34. package/dist/types/src/packlets/devices/devices-service.d.ts.map +1 -1
  35. package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
  36. package/dist/types/src/packlets/devtools/feeds.d.ts.map +1 -1
  37. package/dist/types/src/packlets/devtools/keys.d.ts.map +1 -1
  38. package/dist/types/src/packlets/devtools/metadata.d.ts.map +1 -1
  39. package/dist/types/src/packlets/devtools/network.d.ts.map +1 -1
  40. package/dist/types/src/packlets/devtools/spaces.d.ts.map +1 -1
  41. package/dist/types/src/packlets/diagnostics/browser-diagnostics-broadcast.d.ts.map +1 -1
  42. package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts.map +1 -1
  43. package/dist/types/src/packlets/diagnostics/diagnostics-collector.d.ts.map +1 -1
  44. package/dist/types/src/packlets/diagnostics/diagnostics.d.ts +2 -3
  45. package/dist/types/src/packlets/diagnostics/diagnostics.d.ts.map +1 -1
  46. package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
  47. package/dist/types/src/packlets/identity/contacts-service.d.ts.map +1 -1
  48. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  49. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +2 -2
  50. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts.map +1 -1
  51. package/dist/types/src/packlets/identity/identity-service.d.ts +6 -5
  52. package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
  53. package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
  54. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +2 -1
  55. package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -1
  56. package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts +1 -1
  57. package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts.map +1 -1
  58. package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts.map +1 -1
  59. package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -1
  60. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +5 -1
  61. package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -1
  62. package/dist/types/src/packlets/invitations/invitation-state.d.ts.map +1 -1
  63. package/dist/types/src/packlets/invitations/invitation-topology.d.ts.map +1 -1
  64. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  65. package/dist/types/src/packlets/invitations/invitations-manager.d.ts +1 -1
  66. package/dist/types/src/packlets/invitations/invitations-manager.d.ts.map +1 -1
  67. package/dist/types/src/packlets/invitations/invitations-service.d.ts +3 -3
  68. package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
  69. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +2 -1
  70. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
  71. package/dist/types/src/packlets/invitations/utils.d.ts.map +1 -1
  72. package/dist/types/src/packlets/locks/browser.d.ts.map +1 -1
  73. package/dist/types/src/packlets/locks/node.d.ts.map +1 -1
  74. package/dist/types/src/packlets/logging/logging-service.d.ts.map +1 -1
  75. package/dist/types/src/packlets/network/network-service.d.ts +5 -4
  76. package/dist/types/src/packlets/network/network-service.d.ts.map +1 -1
  77. package/dist/types/src/packlets/services/client-rpc-server.d.ts +3 -3
  78. package/dist/types/src/packlets/services/client-rpc-server.d.ts.map +1 -1
  79. package/dist/types/src/packlets/services/feed-syncer.d.ts +1 -1
  80. package/dist/types/src/packlets/services/feed-syncer.d.ts.map +1 -1
  81. package/dist/types/src/packlets/services/service-context.d.ts +1 -2
  82. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  83. package/dist/types/src/packlets/services/service-host.d.ts +1 -2
  84. package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
  85. package/dist/types/src/packlets/services/service-registry.d.ts.map +1 -1
  86. package/dist/types/src/packlets/services/util.d.ts.map +1 -1
  87. package/dist/types/src/packlets/space-export/archive-format.d.ts +9 -0
  88. package/dist/types/src/packlets/space-export/archive-format.d.ts.map +1 -0
  89. package/dist/types/src/packlets/space-export/index.d.ts +4 -1
  90. package/dist/types/src/packlets/space-export/index.d.ts.map +1 -1
  91. package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts +23 -0
  92. package/dist/types/src/packlets/space-export/serialized-space-reader.d.ts.map +1 -0
  93. package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts +36 -0
  94. package/dist/types/src/packlets/space-export/serialized-space-writer.d.ts.map +1 -0
  95. package/dist/types/src/packlets/space-export/space-archive-reader.d.ts.map +1 -1
  96. package/dist/types/src/packlets/space-export/space-archive-writer.d.ts +1 -1
  97. package/dist/types/src/packlets/space-export/space-archive-writer.d.ts.map +1 -1
  98. package/dist/types/src/packlets/spaces/automerge-space-state.d.ts.map +1 -1
  99. package/dist/types/src/packlets/spaces/data-space-manager.d.ts +1 -2
  100. package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
  101. package/dist/types/src/packlets/spaces/data-space.d.ts +2 -1
  102. package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
  103. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
  104. package/dist/types/src/packlets/spaces/epoch-migrations.d.ts.map +1 -1
  105. package/dist/types/src/packlets/spaces/genesis.d.ts.map +1 -1
  106. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +1 -4
  107. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
  108. package/dist/types/src/packlets/spaces/spaces-service.d.ts +9 -6
  109. package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
  110. package/dist/types/src/packlets/storage/level.d.ts.map +1 -1
  111. package/dist/types/src/packlets/storage/profile-archive.d.ts.map +1 -1
  112. package/dist/types/src/packlets/storage/storage.d.ts.map +1 -1
  113. package/dist/types/src/packlets/storage/util.d.ts.map +1 -1
  114. package/dist/types/src/packlets/system/system-service.d.ts +1 -1
  115. package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
  116. package/dist/types/src/packlets/testing/credential-utils.d.ts.map +1 -1
  117. package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
  118. package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
  119. package/dist/types/src/packlets/worker/worker-runtime.d.ts +11 -1
  120. package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
  121. package/dist/types/src/packlets/worker/worker-session.d.ts +0 -2
  122. package/dist/types/src/packlets/worker/worker-session.d.ts.map +1 -1
  123. package/dist/types/src/testing/setup.d.ts.map +1 -1
  124. package/dist/types/src/version.d.ts +1 -1
  125. package/dist/types/tsconfig.tsbuildinfo +1 -1
  126. package/package.json +39 -46
  127. package/src/packlets/agents/edge-agent-service.ts +3 -2
  128. package/src/packlets/diagnostics/diagnostics.ts +1 -2
  129. package/src/packlets/identity/identity-manager.ts +2 -4
  130. package/src/packlets/identity/identity-service.ts +12 -9
  131. package/src/packlets/identity/identity.ts +2 -2
  132. package/src/packlets/invitations/device-invitation-protocol.ts +3 -1
  133. package/src/packlets/invitations/edge-invitation-handler.ts +5 -2
  134. package/src/packlets/invitations/invitation-host-extension.ts +7 -10
  135. package/src/packlets/invitations/invitation-protocol.ts +5 -1
  136. package/src/packlets/invitations/invitation-state.ts +1 -15
  137. package/src/packlets/invitations/invitations-handler.ts +64 -12
  138. package/src/packlets/invitations/invitations-manager.ts +6 -4
  139. package/src/packlets/invitations/invitations-service.ts +6 -6
  140. package/src/packlets/invitations/space-invitation-protocol.ts +3 -3
  141. package/src/packlets/logging/logging-service.ts +15 -15
  142. package/src/packlets/network/network-service.ts +9 -8
  143. package/src/packlets/services/client-rpc-server.ts +15 -12
  144. package/src/packlets/services/service-context.ts +12 -15
  145. package/src/packlets/services/service-host.ts +8 -19
  146. package/src/packlets/space-export/archive-format.ts +42 -0
  147. package/src/packlets/space-export/index.ts +4 -1
  148. package/src/packlets/space-export/serialized-space-reader.ts +111 -0
  149. package/src/packlets/space-export/serialized-space-writer.ts +253 -0
  150. package/src/packlets/space-export/space-archive-writer.ts +2 -1
  151. package/src/packlets/space-export/space-archive.test.ts +175 -1
  152. package/src/packlets/spaces/data-space-manager.ts +17 -19
  153. package/src/packlets/spaces/data-space.ts +9 -7
  154. package/src/packlets/spaces/edge-feed-replicator.ts +1 -0
  155. package/src/packlets/spaces/spaces-service.test.ts +9 -4
  156. package/src/packlets/spaces/spaces-service.ts +91 -16
  157. package/src/packlets/worker/worker-runtime.ts +38 -4
  158. package/src/packlets/worker/worker-session.ts +4 -10
  159. package/src/version.ts +1 -1
  160. package/dist/lib/browser/chunk-CK3KJB3B.mjs.map +0 -7
  161. package/dist/lib/node-esm/chunk-WHBWCIEN.mjs.map +0 -7
  162. /package/dist/lib/browser/{chunk-NQSC7HOE.mjs.map → chunk-XJRPB3GA.mjs.map} +0 -0
  163. /package/dist/lib/node-esm/{chunk-PKEGMOQ4.mjs.map → chunk-2DT3MZRL.mjs.map} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/client-services",
3
- "version": "0.8.4-main.9be5663bfe",
3
+ "version": "0.8.4-main.abd8ff62ef",
4
4
  "description": "DXOS client services implementation",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -47,13 +47,6 @@
47
47
  }
48
48
  },
49
49
  "types": "dist/types/src/index.d.ts",
50
- "typesVersions": {
51
- "*": {
52
- "testing": [
53
- "dist/types/src/testing/index.d.ts"
54
- ]
55
- }
56
- },
57
50
  "files": [
58
51
  "dist",
59
52
  "src"
@@ -68,49 +61,49 @@
68
61
  "cbor-x": "^1.5.4",
69
62
  "effect": "3.20.0",
70
63
  "platform": "^1.3.6",
71
- "@dxos/async": "0.8.4-main.9be5663bfe",
72
- "@dxos/client-protocol": "0.8.4-main.9be5663bfe",
73
- "@dxos/codec-protobuf": "0.8.4-main.9be5663bfe",
74
- "@dxos/credentials": "0.8.4-main.9be5663bfe",
75
- "@dxos/context": "0.8.4-main.9be5663bfe",
76
- "@dxos/config": "0.8.4-main.9be5663bfe",
77
- "@dxos/crypto": "0.8.4-main.9be5663bfe",
78
- "@dxos/debug": "0.8.4-main.9be5663bfe",
79
- "@dxos/echo": "0.8.4-main.9be5663bfe",
80
- "@dxos/echo-db": "0.8.4-main.9be5663bfe",
81
- "@dxos/edge-client": "0.8.4-main.9be5663bfe",
82
- "@dxos/effect": "0.8.4-main.9be5663bfe",
83
- "@dxos/echo-protocol": "0.8.4-main.9be5663bfe",
84
- "@dxos/feed": "0.8.4-main.9be5663bfe",
85
- "@dxos/echo-pipeline": "0.8.4-main.9be5663bfe",
86
- "@dxos/feed-store": "0.8.4-main.9be5663bfe",
87
- "@dxos/keyring": "0.8.4-main.9be5663bfe",
88
- "@dxos/invariant": "0.8.4-main.9be5663bfe",
89
- "@dxos/keys": "0.8.4-main.9be5663bfe",
90
- "@dxos/kv-store": "0.8.4-main.9be5663bfe",
91
- "@dxos/lock-file": "0.8.4-main.9be5663bfe",
92
- "@dxos/log": "0.8.4-main.9be5663bfe",
93
- "@dxos/messaging": "0.8.4-main.9be5663bfe",
94
- "@dxos/node-std": "0.8.4-main.9be5663bfe",
95
- "@dxos/network-manager": "0.8.4-main.9be5663bfe",
96
- "@dxos/protocols": "0.8.4-main.9be5663bfe",
97
- "@dxos/random-access-storage": "0.8.4-main.9be5663bfe",
98
- "@dxos/rpc": "0.8.4-main.9be5663bfe",
99
- "@dxos/sql-sqlite": "0.8.4-main.9be5663bfe",
100
- "@dxos/teleport-extension-gossip": "0.8.4-main.9be5663bfe",
101
- "@dxos/teleport": "0.8.4-main.9be5663bfe",
102
- "@dxos/timeframe": "0.8.4-main.9be5663bfe",
103
- "@dxos/tracing": "0.8.4-main.9be5663bfe",
104
- "@dxos/util": "0.8.4-main.9be5663bfe",
105
- "@dxos/websocket-rpc": "0.8.4-main.9be5663bfe",
106
- "@dxos/teleport-extension-object-sync": "0.8.4-main.9be5663bfe"
64
+ "@dxos/async": "0.8.4-main.abd8ff62ef",
65
+ "@dxos/client-protocol": "0.8.4-main.abd8ff62ef",
66
+ "@dxos/codec-protobuf": "0.8.4-main.abd8ff62ef",
67
+ "@dxos/credentials": "0.8.4-main.abd8ff62ef",
68
+ "@dxos/context": "0.8.4-main.abd8ff62ef",
69
+ "@dxos/crypto": "0.8.4-main.abd8ff62ef",
70
+ "@dxos/debug": "0.8.4-main.abd8ff62ef",
71
+ "@dxos/echo": "0.8.4-main.abd8ff62ef",
72
+ "@dxos/echo-db": "0.8.4-main.abd8ff62ef",
73
+ "@dxos/echo-pipeline": "0.8.4-main.abd8ff62ef",
74
+ "@dxos/config": "0.8.4-main.abd8ff62ef",
75
+ "@dxos/effect": "0.8.4-main.abd8ff62ef",
76
+ "@dxos/edge-client": "0.8.4-main.abd8ff62ef",
77
+ "@dxos/feed": "0.8.4-main.abd8ff62ef",
78
+ "@dxos/feed-store": "0.8.4-main.abd8ff62ef",
79
+ "@dxos/invariant": "0.8.4-main.abd8ff62ef",
80
+ "@dxos/keyring": "0.8.4-main.abd8ff62ef",
81
+ "@dxos/keys": "0.8.4-main.abd8ff62ef",
82
+ "@dxos/kv-store": "0.8.4-main.abd8ff62ef",
83
+ "@dxos/lock-file": "0.8.4-main.abd8ff62ef",
84
+ "@dxos/log": "0.8.4-main.abd8ff62ef",
85
+ "@dxos/messaging": "0.8.4-main.abd8ff62ef",
86
+ "@dxos/node-std": "0.8.4-main.abd8ff62ef",
87
+ "@dxos/protocols": "0.8.4-main.abd8ff62ef",
88
+ "@dxos/random-access-storage": "0.8.4-main.abd8ff62ef",
89
+ "@dxos/echo-protocol": "0.8.4-main.abd8ff62ef",
90
+ "@dxos/rpc": "0.8.4-main.abd8ff62ef",
91
+ "@dxos/sql-sqlite": "0.8.4-main.abd8ff62ef",
92
+ "@dxos/teleport": "0.8.4-main.abd8ff62ef",
93
+ "@dxos/teleport-extension-gossip": "0.8.4-main.abd8ff62ef",
94
+ "@dxos/teleport-extension-object-sync": "0.8.4-main.abd8ff62ef",
95
+ "@dxos/tracing": "0.8.4-main.abd8ff62ef",
96
+ "@dxos/timeframe": "0.8.4-main.abd8ff62ef",
97
+ "@dxos/network-manager": "0.8.4-main.abd8ff62ef",
98
+ "@dxos/util": "0.8.4-main.abd8ff62ef",
99
+ "@dxos/websocket-rpc": "0.8.4-main.abd8ff62ef"
107
100
  },
108
101
  "devDependencies": {
109
102
  "@types/platform": "^1.3.4",
110
103
  "@types/readable-stream": "^2.3.9",
111
104
  "get-port-please": "^3.1.1",
112
- "@dxos/test-utils": "0.8.4-main.9be5663bfe",
113
- "@dxos/signal": "0.8.4-main.9be5663bfe"
105
+ "@dxos/signal": "0.8.4-main.abd8ff62ef",
106
+ "@dxos/test-utils": "0.8.4-main.abd8ff62ef"
114
107
  },
115
108
  "publishConfig": {
116
109
  "access": "public"
@@ -2,6 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import { type RequestOptions } from '@dxos/codec-protobuf';
5
6
  import { Stream } from '@dxos/codec-protobuf/stream';
6
7
  import { Context } from '@dxos/context';
7
8
  import { type EdgeConnection } from '@dxos/edge-client';
@@ -44,8 +45,8 @@ export class EdgeAgentServiceImpl implements EdgeAgentService {
44
45
  });
45
46
  }
46
47
 
47
- async createAgent(): Promise<void> {
48
- return (await this._agentManagerProvider()).createAgent(Context.default());
48
+ async createAgent(_request: void, options?: RequestOptions): Promise<void> {
49
+ return (await this._agentManagerProvider()).createAgent(options?.ctx ?? Context.default());
49
50
  }
50
51
 
51
52
  queryAgentStatus(): Stream<QueryAgentStatusResponse> {
@@ -23,7 +23,7 @@ import {
23
23
  import { type SubscribeToFeedsResponse } from '@dxos/protocols/proto/dxos/devtools/host';
24
24
  import { type SwarmInfo } from '@dxos/protocols/proto/dxos/devtools/swarm';
25
25
  import { type Epoch } from '@dxos/protocols/proto/dxos/halo/credentials';
26
- import { type Resource, type Span } from '@dxos/protocols/proto/dxos/tracing';
26
+ import { type Resource } from '@dxos/protocols/proto/dxos/tracing';
27
27
  import { TRACE_PROCESSOR } from '@dxos/tracing';
28
28
 
29
29
  import { DXOS_VERSION } from '../../version';
@@ -62,7 +62,6 @@ export type Diagnostics = {
62
62
 
63
63
  export type TraceDiagnostic = {
64
64
  resources: Record<string, Resource>;
65
- spans: Span[];
66
65
  logs: LogEntry[];
67
66
  };
68
67
 
@@ -13,7 +13,6 @@ import { invariant } from '@dxos/invariant';
13
13
  import { type Keyring } from '@dxos/keyring';
14
14
  import { PublicKey } from '@dxos/keys';
15
15
  import { log } from '@dxos/log';
16
- import { trace } from '@dxos/protocols';
17
16
  import { Device, DeviceKind } from '@dxos/protocols/proto/dxos/client/services';
18
17
  import { type Runtime } from '@dxos/protocols/proto/dxos/config';
19
18
  import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
@@ -112,8 +111,7 @@ export class IdentityManager {
112
111
 
113
112
  @Trace.span({ showInBrowserTimeline: true })
114
113
  async open(ctx: Context): Promise<void> {
115
- const traceId = PublicKey.random().toHex();
116
- log.trace('dxos.halo.identity-manager.open', trace.begin({ id: traceId }));
114
+ log('opening identity manager');
117
115
 
118
116
  const identityRecord = this._metadataStore.getIdentityRecord();
119
117
  log('identity record', { identityRecord });
@@ -128,7 +126,7 @@ export class IdentityManager {
128
126
 
129
127
  this.stateUpdate.emit();
130
128
  }
131
- log.trace('dxos.halo.identity-manager.open', trace.end({ id: traceId }));
129
+ log('opened identity manager');
132
130
  }
133
131
 
134
132
  async close(ctx: Context): Promise<void> {
@@ -2,6 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import { type RequestOptions } from '@dxos/codec-protobuf';
5
6
  import { Stream } from '@dxos/codec-protobuf/stream';
6
7
  import { Context, Resource } from '@dxos/context';
7
8
  import { createCredential, signPresentation } from '@dxos/credentials';
@@ -27,14 +28,15 @@ export class IdentityServiceImpl extends Resource implements IdentityService {
27
28
  private readonly _identityManager: IdentityManager,
28
29
  private readonly _recoveryManager: EdgeIdentityRecoveryManager,
29
30
  private readonly _keyring: Keyring,
30
- private readonly _createIdentity: (params: CreateIdentityOptions) => Promise<Identity>,
31
+ private readonly _createIdentity: (params: CreateIdentityOptions, ctx?: Context) => Promise<Identity>,
31
32
  private readonly _onProfileUpdate?: (profile: ProfileDocument | undefined) => Promise<void>,
32
33
  ) {
33
34
  super();
34
35
  }
35
36
 
36
- async createIdentity(request: CreateIdentityRequest): Promise<IdentityProto> {
37
- await this._createIdentity({ profile: request.profile, deviceProfile: request.deviceProfile });
37
+ async createIdentity(request: CreateIdentityRequest, options?: RequestOptions): Promise<IdentityProto> {
38
+ const ctx = options?.ctx ?? Context.default();
39
+ await this._createIdentity({ profile: request.profile, deviceProfile: request.deviceProfile }, ctx);
38
40
  return this._getIdentity()!;
39
41
  }
40
42
 
@@ -71,17 +73,18 @@ export class IdentityServiceImpl extends Resource implements IdentityService {
71
73
  return this._recoveryManager.createRecoveryCredential(request);
72
74
  }
73
75
 
74
- async requestRecoveryChallenge() {
75
- return this._recoveryManager.requestRecoveryChallenge(Context.default());
76
+ async requestRecoveryChallenge(_request: void, options?: RequestOptions) {
77
+ return this._recoveryManager.requestRecoveryChallenge(options?.ctx ?? Context.default());
76
78
  }
77
79
 
78
- async recoverIdentity(request: RecoverIdentityRequest): Promise<IdentityProto> {
80
+ async recoverIdentity(request: RecoverIdentityRequest, options?: RequestOptions): Promise<IdentityProto> {
81
+ const ctx = options?.ctx ?? Context.default();
79
82
  if (request.recoveryCode) {
80
- await this._recoveryManager.recoverIdentity(Context.default(), { recoveryCode: request.recoveryCode });
83
+ await this._recoveryManager.recoverIdentity(ctx, { recoveryCode: request.recoveryCode });
81
84
  } else if (request.external) {
82
- await this._recoveryManager.recoverIdentityWithExternalSignature(Context.default(), request.external);
85
+ await this._recoveryManager.recoverIdentityWithExternalSignature(ctx, request.external);
83
86
  } else if (request.token) {
84
- await this._recoveryManager.recoverIdentityWithToken(Context.default(), { token: request.token });
87
+ await this._recoveryManager.recoverIdentityWithToken(ctx, { token: request.token });
85
88
  } else {
86
89
  throw new Error('Invalid request.');
87
90
  }
@@ -104,7 +104,7 @@ export class Identity {
104
104
  return this._deviceStateMachine.authorizedDeviceKeys;
105
105
  }
106
106
 
107
- @trace.span()
107
+ @trace.span({ op: 'lifecycle' })
108
108
  async open(ctx: DxosContext): Promise<void> {
109
109
  await this._presence?.open();
110
110
  await this.space.spaceState.addCredentialProcessor(this._deviceStateMachine);
@@ -120,7 +120,7 @@ export class Identity {
120
120
  await this._edgeFeedReplicator?.open();
121
121
  }
122
122
 
123
- @trace.span()
123
+ @trace.span({ op: 'lifecycle' })
124
124
  async close(ctx: DxosContext): Promise<void> {
125
125
  await this._presence?.close();
126
126
  await this.authVerifier.close();
@@ -2,6 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import { type Context } from '@dxos/context';
5
6
  import { getCredentialAssertion } from '@dxos/credentials';
6
7
  import { invariant } from '@dxos/invariant';
7
8
  import { type Keyring } from '@dxos/keyring';
@@ -96,7 +97,7 @@ export class DeviceInvitationProtocol implements InvitationProtocol {
96
97
  };
97
98
  }
98
99
 
99
- async accept(response: AdmissionResponse, request: AdmissionRequest): Promise<Partial<Invitation>> {
100
+ async accept(_ctx: Context, response: AdmissionResponse, request: AdmissionRequest): Promise<Partial<Invitation>> {
100
101
  invariant(response.device);
101
102
  const { identityKey, haloSpaceKey, genesisFeedKey, controlTimeframe } = response.device;
102
103
 
@@ -104,6 +105,7 @@ export class DeviceInvitationProtocol implements InvitationProtocol {
104
105
  const { deviceKey, controlFeedKey, dataFeedKey, profile } = request.device;
105
106
 
106
107
  // TODO(wittjosiah): When multiple identities are supported, verify identity doesn't already exist before accepting.
108
+ // ctx is unused here because _acceptIdentity uses ServiceContext's lifecycle ctx internally.
107
109
 
108
110
  await this._acceptIdentity({
109
111
  identityKey,
@@ -23,13 +23,14 @@ import {
23
23
  type AdmissionResponse,
24
24
  type SpaceAdmissionRequest,
25
25
  } from '@dxos/protocols/proto/dxos/halo/invitations';
26
+ import { trace } from '@dxos/tracing';
26
27
 
27
28
  import { type InvitationProtocol } from './invitation-protocol';
28
29
  import { type FlowLockHolder, type GuardedInvitationState } from './invitation-state';
29
30
  import { tryAcquireBeforeContextDisposed } from './utils';
30
31
 
31
32
  export interface EdgeInvitationHandlerCallbacks {
32
- onInvitationSuccess(response: AdmissionResponse, request: AdmissionRequest): Promise<void>;
33
+ onInvitationSuccess(ctx: Context, response: AdmissionResponse, request: AdmissionRequest): Promise<void>;
33
34
  }
34
35
 
35
36
  export const MAX_RETRIES_PER_INVITATION = 5;
@@ -41,6 +42,7 @@ export type EdgeInvitationConfig = {
41
42
  retryJitter?: number;
42
43
  };
43
44
 
45
+ @trace.resource()
44
46
  export class EdgeInvitationHandler implements FlowLockHolder {
45
47
  private _flowLock: MutexGuard | undefined;
46
48
 
@@ -113,6 +115,7 @@ export class EdgeInvitationHandler implements FlowLockHolder {
113
115
  scheduleMicroTask(ctx, tryHandleInvitation);
114
116
  }
115
117
 
118
+ @trace.span({ op: 'invitation.edge' })
116
119
  private async _handleSpaceInvitationFlow(
117
120
  ctx: Context,
118
121
  guardedState: GuardedInvitationState,
@@ -132,7 +135,7 @@ export class EdgeInvitationHandler implements FlowLockHolder {
132
135
  });
133
136
 
134
137
  const admissionResponse = await this._mapToAdmissionResponse(response);
135
- await this._callbacks.onInvitationSuccess(admissionResponse, { space: admissionRequest });
138
+ await this._callbacks.onInvitationSuccess(ctx, admissionResponse, { space: admissionRequest });
136
139
  } catch (error) {
137
140
  guardedState.set(this, Invitation.State.ERROR);
138
141
  throw error;
@@ -8,7 +8,7 @@ import { randomBytes, verify } from '@dxos/crypto';
8
8
  import { InvariantViolation, invariant } from '@dxos/invariant';
9
9
  import { PublicKey } from '@dxos/keys';
10
10
  import { log } from '@dxos/log';
11
- import { InvalidInvitationExtensionRoleError, trace } from '@dxos/protocols';
11
+ import { InvalidInvitationExtensionRoleError } from '@dxos/protocols';
12
12
  import { schema } from '@dxos/protocols/proto';
13
13
  import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
14
14
  import { type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
@@ -110,8 +110,7 @@ export class InvitationHostExtension
110
110
 
111
111
  introduce: async (request) => {
112
112
  const { profile, invitationId } = request;
113
- const traceId = PublicKey.random().toHex();
114
- log.trace('dxos.sdk.invitation-handler.host.introduce', trace.begin({ id: traceId }));
113
+ log('introducing host invitation');
115
114
 
116
115
  const invitation = this._requireActiveInvitation();
117
116
  this._assertInvitationState(Invitation.State.CONNECTED);
@@ -131,7 +130,7 @@ export class InvitationHostExtension
131
130
  this._challenge =
132
131
  invitation.authMethod === Invitation.AuthMethod.KNOWN_PUBLIC_KEY ? randomBytes(32) : undefined;
133
132
 
134
- log.trace('dxos.sdk.invitation-handler.host.introduce', trace.end({ id: traceId }));
133
+ log('introduced host invitation');
135
134
  return {
136
135
  authMethod: invitation.authMethod,
137
136
  challenge: this._challenge,
@@ -139,8 +138,7 @@ export class InvitationHostExtension
139
138
  },
140
139
 
141
140
  authenticate: async ({ authCode: code, signedChallenge }) => {
142
- const traceId = PublicKey.random().toHex();
143
- log.trace('dxos.sdk.invitation-handler.host.authenticate', trace.begin({ id: traceId }));
141
+ log('authenticating host invitation');
144
142
 
145
143
  const invitation = this._requireActiveInvitation();
146
144
  log.verbose('received authentication request', { authCode: code });
@@ -201,13 +199,12 @@ export class InvitationHostExtension
201
199
  return { status };
202
200
  }
203
201
 
204
- log.trace('dxos.sdk.invitation-handler.host.authenticate', trace.end({ id: traceId, data: { status } }));
202
+ log('authenticated host invitation', { status });
205
203
  return { status };
206
204
  },
207
205
 
208
206
  admit: async (request) => {
209
- const traceId = PublicKey.random().toHex();
210
- log.trace('dxos.sdk.invitation-handler.host.admit', trace.begin({ id: traceId }));
207
+ log('admitting guest');
211
208
  const invitation = this._requireActiveInvitation();
212
209
 
213
210
  try {
@@ -221,7 +218,7 @@ export class InvitationHostExtension
221
218
 
222
219
  const response = await this._callbacks.admit(request);
223
220
 
224
- log.trace('dxos.sdk.invitation-handler.host.admit', trace.end({ id: traceId }));
221
+ log('admitted guest');
225
222
  return response;
226
223
  } catch (err: any) {
227
224
  this._callbacks.onError(err);
@@ -2,6 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import { type Context } from '@dxos/context';
5
6
  import { type PublicKey } from '@dxos/keys';
6
7
  import type { Invitation } from '@dxos/protocols/proto/dxos/client/services';
7
8
  import type { DeviceProfileDocument, ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
@@ -71,6 +72,9 @@ export interface InvitationProtocol {
71
72
 
72
73
  /**
73
74
  * Redeem the admission credential.
75
+ * @param ctx - Caller context used for tracing and cancellation. Forwarded to downstream
76
+ * internal methods (e.g. `DataSpaceManager.acceptSpace`) so their spans become children of
77
+ * the invitation flow span.
74
78
  */
75
- accept(response: AdmissionResponse, request: AdmissionRequest): Promise<Partial<Invitation>>;
79
+ accept(ctx: Context, response: AdmissionResponse, request: AdmissionRequest): Promise<Partial<Invitation>>;
76
80
  }
@@ -31,12 +31,6 @@ export const createGuardedInvitationState = (
31
31
  invitation: Invitation,
32
32
  stream: PushStream<Invitation>,
33
33
  ): GuardedInvitationState => {
34
- // the mutex guards invitation flow on host and guest side, making sure only one flow is currently active
35
- // deadlocks seem very unlikely because hosts don't initiate multiple connections
36
- // even if this somehow happens that there are 2 guests (A, B) and 2 hosts (1, 2) and:
37
- // A has lock for flow with 1, B has lock for flow with 2
38
- // 1 has lock for flow with B, 2 has lock for flow with A
39
- // there'll be a 10-second introduction timeout after which connection will be closed and deadlock broken
40
34
  const mutex = new Mutex();
41
35
  let lastActiveLockHolder: FlowLockHolder | null = null;
42
36
  let currentInvitation = { ...invitation };
@@ -44,9 +38,6 @@ export const createGuardedInvitationState = (
44
38
  if (ctx.disposed || (lockHolder !== null && mutex.isLocked() && !lockHolder.hasFlowLock())) {
45
39
  return false;
46
40
  }
47
- // don't allow transitions from a terminal state unless a new extension acquired mutex
48
- // handles a case when error occurs (e.g. connection is closed) after we completed the flow
49
- // successfully or already reported another error
50
41
  return lockHolder == null || lastActiveLockHolder !== lockHolder || isNonTerminalState(currentInvitation.state);
51
42
  };
52
43
  return {
@@ -54,7 +45,6 @@ export const createGuardedInvitationState = (
54
45
  get current() {
55
46
  return currentInvitation;
56
47
  },
57
- // disposing context prevents any further state updates
58
48
  complete: (newState: Partial<Invitation>) => {
59
49
  logStateUpdate(currentInvitation, undefined, invitation.state);
60
50
  currentInvitation = { ...currentInvitation, ...newState };
@@ -94,11 +84,7 @@ const logStateUpdate = (invitation: Invitation, actor: any, newState: Invitation
94
84
  error: error?.message,
95
85
  errorStack: error?.stack,
96
86
  };
97
- if (isNonTerminalState(newState)) {
98
- log.verbose('dxos.sdk.invitations-handler.state.update', logContext);
99
- } else {
100
- log.info('dxos.sdk.invitations-handler.state.update', logContext);
101
- }
87
+ log.verbose('dxos.sdk.invitations-handler.state.update', logContext);
102
88
  };
103
89
 
104
90
  const isNonTerminalState = (currentState: Invitation.State): boolean => {
@@ -11,7 +11,7 @@ import { invariant } from '@dxos/invariant';
11
11
  import { PublicKey } from '@dxos/keys';
12
12
  import { log } from '@dxos/log';
13
13
  import { type SwarmConnection, type SwarmNetworkManager, createTeleportProtocolFactory } from '@dxos/network-manager';
14
- import { InvalidInvitationError, InvalidInvitationExtensionRoleError, trace } from '@dxos/protocols';
14
+ import { InvalidInvitationError, InvalidInvitationExtensionRoleError } from '@dxos/protocols';
15
15
  import { type AdmissionKeypair, Invitation } from '@dxos/protocols/proto/dxos/client/services';
16
16
  import { type DeviceProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
17
17
  import { AuthenticationResponse, type IntroductionResponse } from '@dxos/protocols/proto/dxos/halo/invitations';
@@ -64,6 +64,7 @@ export type InvitationConnectionProps = {
64
64
  * TODO: the flow logic should either be contained in invitations-handler or in extensions, not be split across
65
65
  * TODO: potentially re-evaluate host-side API to allow multiple concurrent connection, so that mutex can be removed
66
66
  */
67
+ @_trace.resource()
67
68
  export class InvitationsHandler {
68
69
  /**
69
70
  * @internal
@@ -87,6 +88,33 @@ export class InvitationsHandler {
87
88
  type: invitation.type,
88
89
  });
89
90
  metrics.increment('dxos.invitation.host');
91
+
92
+ const hostSpanId = `invitation-host-${invitation.invitationId}`;
93
+ // Reassign ctx to the child context so downstream `@trace.span` calls stay in the same trace.
94
+ // Link child -> parent disposal so `ctx.dispose()` inside this flow still completes the
95
+ // invitation stream (the caller's ctx owns `stream.complete()` via onDispose).
96
+ // Do NOT await `invitationCtx.dispose()` here: `derive()` registers a parent -> child
97
+ // dispose hook, so awaiting the parent dispose would re-enter the still-pending child
98
+ // dispose promise and deadlock.
99
+ const invitationCtx = ctx;
100
+ ctx =
101
+ _trace.spanStart({
102
+ id: hostSpanId,
103
+ instance: this,
104
+ methodName: 'handleInvitationFlow',
105
+ parentCtx: ctx,
106
+ op: 'invitation.host',
107
+ attributes: {
108
+ 'ctx.dxos.invitation.id': invitation.invitationId,
109
+ 'ctx.dxos.invitation.kind': Invitation.Kind[invitation.kind],
110
+ },
111
+ }) ?? ctx;
112
+ if (ctx !== invitationCtx) {
113
+ ctx.onDispose(() => {
114
+ void invitationCtx.dispose();
115
+ });
116
+ }
117
+ ctx.onDispose(() => _trace.spanEnd(hostSpanId));
90
118
  const guardedState = createGuardedInvitationState(ctx, invitation, stream);
91
119
  // Called for every connecting peer.
92
120
  const createExtension = (): InvitationHostExtension => {
@@ -132,15 +160,14 @@ export class InvitationsHandler {
132
160
  });
133
161
 
134
162
  scheduleTask(connectionCtx, async () => {
135
- const traceId = PublicKey.random().toHex();
136
163
  try {
137
- log.trace('dxos.sdk.invitations-handler.host.onOpen', trace.begin({ id: traceId }));
164
+ log('opening host invitation handler');
138
165
  log.verbose('connected', { ...protocol.toJSON() });
139
166
  const deviceKey = await extension.completedTrigger.wait({ timeout: invitation.timeout });
140
167
  log.verbose('admitted guest', { guest: deviceKey, ...protocol.toJSON() });
141
168
  guardedState.set(extension, Invitation.State.SUCCESS);
142
169
  metrics.increment('dxos.invitation.success');
143
- log.trace('dxos.sdk.invitations-handler.host.onOpen', trace.end({ id: traceId }));
170
+ log('host invitation handler opened');
144
171
  admitted = true;
145
172
 
146
173
  if (!invitation.multiUse) {
@@ -159,7 +186,6 @@ export class InvitationsHandler {
159
186
  log.error('failed', err);
160
187
  }
161
188
  }
162
- log.trace('dxos.sdk.invitations-handler.host.onOpen', trace.error({ id: traceId, error: err }));
163
189
  // Close connection
164
190
  extensionsCtx.close(err);
165
191
  }
@@ -232,6 +258,34 @@ export class InvitationsHandler {
232
258
  });
233
259
  const { timeout = INVITATION_TIMEOUT } = invitation;
234
260
 
261
+ const guestSpanId = `invitation-guest-${invitation.invitationId}`;
262
+ // Reassign ctx to the child context returned by spanStart so downstream calls
263
+ // (`edgeInvitationHandler.handle`, `_joinSwarm`, etc.) inherit this span as their
264
+ // parent rather than starting a new root trace.
265
+ // Link child -> parent disposal so `ctx.dispose()` inside this flow still completes the
266
+ // invitation stream. See note in `handleInvitationFlow`: must not await the parent
267
+ // dispose here (`derive()` registers a parent -> child dispose hook, so awaiting would
268
+ // re-enter the still-pending child dispose promise and deadlock).
269
+ const invitationCtx = ctx;
270
+ ctx =
271
+ _trace.spanStart({
272
+ id: guestSpanId,
273
+ instance: this,
274
+ methodName: 'acceptInvitation',
275
+ parentCtx: ctx,
276
+ op: 'invitation.guest',
277
+ attributes: {
278
+ 'ctx.dxos.invitation.id': invitation.invitationId,
279
+ 'ctx.dxos.invitation.kind': Invitation.Kind[invitation.kind],
280
+ },
281
+ }) ?? ctx;
282
+ if (ctx !== invitationCtx) {
283
+ ctx.onDispose(() => {
284
+ void invitationCtx.dispose();
285
+ });
286
+ }
287
+ ctx.onDispose(() => _trace.spanEnd(guestSpanId));
288
+
235
289
  if (deviceProfile) {
236
290
  invariant(invitation.kind === Invitation.Kind.DEVICE, 'deviceProfile provided for non-device invitation');
237
291
  }
@@ -279,9 +333,8 @@ export class InvitationsHandler {
279
333
  });
280
334
 
281
335
  scheduleTask(connectionCtx, async () => {
282
- const traceId = PublicKey.random().toHex();
283
336
  try {
284
- log.trace('dxos.sdk.invitations-handler.guest.onOpen', trace.begin({ id: traceId }));
337
+ log('opening guest invitation handler');
285
338
 
286
339
  scheduleTask(
287
340
  connectionCtx,
@@ -345,7 +398,7 @@ export class InvitationsHandler {
345
398
  admitted = true;
346
399
 
347
400
  // 4. Record credential in our HALO.
348
- const result = await protocol.accept(admissionResponse, admissionRequest);
401
+ const result = await protocol.accept(ctx, admissionResponse, admissionRequest);
349
402
 
350
403
  // 5. Success.
351
404
  log.verbose('dxos.sdk.invitations-handler.guest.admitted-by-host', {
@@ -357,7 +410,7 @@ export class InvitationsHandler {
357
410
  ...result,
358
411
  state: Invitation.State.SUCCESS,
359
412
  });
360
- log.trace('dxos.sdk.invitations-handler.guest.onOpen', trace.end({ id: traceId }));
413
+ log('guest invitation handler opened');
361
414
  } catch (err: any) {
362
415
  if (err instanceof TimeoutError) {
363
416
  log.verbose('timeout', { ...protocol.toJSON() });
@@ -367,7 +420,6 @@ export class InvitationsHandler {
367
420
  guardedState.error(extension, err);
368
421
  }
369
422
  extensionCtx.close(err);
370
- log.trace('dxos.sdk.invitations-handler.guest.onOpen', trace.error({ id: traceId, error: err }));
371
423
  }
372
424
  });
373
425
  },
@@ -389,8 +441,8 @@ export class InvitationsHandler {
389
441
  };
390
442
 
391
443
  const edgeInvitationHandler = new EdgeInvitationHandler(this._connectionProps?.edgeInvitations, this._edgeClient, {
392
- onInvitationSuccess: async (admissionResponse, admissionRequest) => {
393
- const result = await protocol.accept(admissionResponse, admissionRequest);
444
+ onInvitationSuccess: async (edgeCtx, admissionResponse, admissionRequest) => {
445
+ const result = await protocol.accept(edgeCtx, admissionResponse, admissionRequest);
394
446
  log.info('admitted by edge', { ...protocol.toJSON() });
395
447
  guardedState.complete({ ...guardedState.current, ...result, state: Invitation.State.SUCCESS });
396
448
  },
@@ -50,7 +50,7 @@ export class InvitationsManager {
50
50
  private readonly _metadataStore: MetadataStore,
51
51
  ) {}
52
52
 
53
- @trace.span({ showInBrowserTimeline: true })
53
+ @trace.span({ showInBrowserTimeline: true, op: 'lifecycle' })
54
54
  async createInvitation(
55
55
  ctx: Context,
56
56
  options: Partial<Invitation> & Pick<Invitation, 'kind'>,
@@ -125,7 +125,7 @@ export class InvitationsManager {
125
125
  }
126
126
  }
127
127
 
128
- acceptInvitation(_ctx: Context, request: AcceptInvitationRequest): AuthenticatingInvitation {
128
+ acceptInvitation(ctx: Context, request: AcceptInvitationRequest): AuthenticatingInvitation {
129
129
  const options = request.invitation;
130
130
  const existingInvitation = this._acceptInvitations.get(options.invitationId);
131
131
  if (existingInvitation) {
@@ -138,7 +138,7 @@ export class InvitationsManager {
138
138
  invitation,
139
139
  stream,
140
140
  otpEnteredTrigger,
141
- } = this._createObservableAcceptingInvitation(handler, options);
141
+ } = this._createObservableAcceptingInvitation(ctx, handler, options);
142
142
  this._invitationsHandler.acceptInvitation(
143
143
  invitationCtx,
144
144
  stream,
@@ -281,6 +281,7 @@ export class InvitationsManager {
281
281
  }
282
282
 
283
283
  private _createObservableAcceptingInvitation(
284
+ parentCtx: Context,
284
285
  handler: InvitationProtocol,
285
286
  initialState: Invitation,
286
287
  ): {
@@ -291,7 +292,8 @@ export class InvitationsManager {
291
292
  } {
292
293
  const otpEnteredTrigger = new Trigger<string>();
293
294
  const stream = new PushStream<Invitation>();
294
- const ctx = new Context({
295
+ // Derive from caller ctx so `TRACE_SPAN_ATTRIBUTE` propagates via the parent chain.
296
+ const ctx = parentCtx.derive({
295
297
  onError: (err) => {
296
298
  if (err instanceof TimeoutError) {
297
299
  log('timeout', { ...handler.toJSON() });