dsh-live-teams 0.1.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 (69) hide show
  1. package/LICENSE +176 -0
  2. package/NOTICE +11 -0
  3. package/README.md +85 -0
  4. package/cordis.patch.yml +25 -0
  5. package/lib/binding.d.ts +18 -0
  6. package/lib/binding.js +42 -0
  7. package/lib/changed-paths.d.ts +26 -0
  8. package/lib/changed-paths.js +69 -0
  9. package/lib/client.js +6753 -0
  10. package/lib/command-queue.d.ts +60 -0
  11. package/lib/command-queue.js +185 -0
  12. package/lib/compatibility.js +109 -0
  13. package/lib/context-provider.d.ts +110 -0
  14. package/lib/context-provider.js +249 -0
  15. package/lib/dispatch.d.ts +174 -0
  16. package/lib/dispatch.js +624 -0
  17. package/lib/errors.d.ts +36 -0
  18. package/lib/errors.js +103 -0
  19. package/lib/git-artifacts.d.ts +50 -0
  20. package/lib/git-artifacts.js +242 -0
  21. package/lib/index.d.ts +14 -0
  22. package/lib/index.js +14 -0
  23. package/lib/mailbox.d.ts +274 -0
  24. package/lib/mailbox.js +721 -0
  25. package/lib/member-tools.d.ts +57 -0
  26. package/lib/member-tools.js +1265 -0
  27. package/lib/migrations.d.ts +17 -0
  28. package/lib/migrations.js +47 -0
  29. package/lib/plugin.d.ts +106 -0
  30. package/lib/plugin.js +1003 -0
  31. package/lib/roles.d.ts +35 -0
  32. package/lib/roles.js +284 -0
  33. package/lib/routes.d.ts +586 -0
  34. package/lib/routes.js +2816 -0
  35. package/lib/scope.d.ts +62 -0
  36. package/lib/scope.js +133 -0
  37. package/lib/session-bridge.d.ts +76 -0
  38. package/lib/session-bridge.js +147 -0
  39. package/lib/session-title.js +35 -0
  40. package/lib/storage.d.ts +9 -0
  41. package/lib/storage.js +65 -0
  42. package/lib/task-store.d.ts +729 -0
  43. package/lib/task-store.js +2205 -0
  44. package/lib/team-store.d.ts +216 -0
  45. package/lib/team-store.js +765 -0
  46. package/lib/tree-snapshot.d.ts +28 -0
  47. package/lib/tree-snapshot.js +80 -0
  48. package/lib/types/client/TeamView.d.ts +26 -0
  49. package/lib/types/client/TeamView.dom.test.d.ts +1 -0
  50. package/lib/types/client/api.d.ts +522 -0
  51. package/lib/types/client/api.test.d.ts +1 -0
  52. package/lib/types/client/attention.d.ts +65 -0
  53. package/lib/types/client/attention.test.d.ts +1 -0
  54. package/lib/types/client/index.d.ts +31 -0
  55. package/lib/types/client/locales.d.ts +577 -0
  56. package/lib/types/client/member-name.d.ts +14 -0
  57. package/lib/types/client/member-name.test.d.ts +1 -0
  58. package/lib/types/client/roster.d.ts +26 -0
  59. package/lib/types/client/roster.test.d.ts +1 -0
  60. package/lib/types/client/styles.d.ts +3 -0
  61. package/package.json +104 -0
  62. package/roles/builder.md +40 -0
  63. package/roles/delegate.md +36 -0
  64. package/roles/lead.md +46 -0
  65. package/roles/oracle.md +36 -0
  66. package/roles/researcher.md +37 -0
  67. package/roles/reviewer.md +45 -0
  68. package/roles/scout.md +36 -0
  69. package/roles/verifier.md +36 -0
@@ -0,0 +1,60 @@
1
+ import { ErrorCode } from "./errors.js";
2
+ //#region src/command-queue.d.ts
3
+ export type CommandState = 'pending' | 'leased' | 'admitted' | 'applied' | 'failed';
4
+ export interface CommandRecord {
5
+ commandId: string;
6
+ idempotencyKey: string;
7
+ causationId: string | undefined;
8
+ sessionId: string;
9
+ bindingGeneration: number;
10
+ mode: 'queue' | 'steer';
11
+ reason: string;
12
+ content: readonly unknown[];
13
+ requestId: string;
14
+ state: CommandState;
15
+ receipt: string | undefined;
16
+ failureCode: ErrorCode | undefined;
17
+ failureDetail: string | undefined;
18
+ revision: number;
19
+ }
20
+ type QueueOptions = {
21
+ workspacePath: string;
22
+ teamId: string;
23
+ deliver: (record: CommandRecord) => Promise<{
24
+ accepted?: true;
25
+ } | void>;
26
+ bindingGenerationOf?: ((sessionId: string) => Promise<number | undefined> | number | undefined) | undefined;
27
+ now?: (() => number) | undefined;
28
+ };
29
+ type AdmissionRequest = {
30
+ idempotencyKey: string;
31
+ sessionId: string;
32
+ bindingGeneration: number;
33
+ mode: 'queue' | 'steer';
34
+ content: readonly unknown[];
35
+ reason?: string;
36
+ causationId?: string;
37
+ };
38
+ export declare class SessionCommandQueue {
39
+ private readonly workspacePath;
40
+ private readonly teamId;
41
+ private readonly deliver;
42
+ private readonly bindingGenerationOf;
43
+ private readonly now;
44
+ private readonly file;
45
+ constructor(options: QueueOptions);
46
+ list(): Promise<CommandRecord[]>;
47
+ listPending(teamId?: string): Promise<CommandRecord[]>;
48
+ admit(request: AdmissionRequest): Promise<{
49
+ record: CommandRecord;
50
+ duplicate: boolean;
51
+ }>;
52
+ dispatch(commandId: string): Promise<CommandRecord>;
53
+ retry(commandId: string): Promise<CommandRecord>;
54
+ get(commandId: string): Promise<CommandRecord | undefined>;
55
+ private transition;
56
+ private mutate;
57
+ private read;
58
+ }
59
+ export declare function createCommandQueue(options: QueueOptions): SessionCommandQueue;
60
+ //#endregion
@@ -0,0 +1,185 @@
1
+ import { BINDING_STALE_MESSAGE, ERROR_CODES, LiveTeamsError } from "./errors.js";
2
+ import { atomicWriteJson, clone, fileExists, readJson, withProcessLock } from "./storage.js";
3
+ import path from "node:path";
4
+ import { randomUUID } from "node:crypto";
5
+ //#region src/command-queue.ts
6
+ const DELIVERED_STATES = /* @__PURE__ */ new Set(["admitted", "applied"]);
7
+ const AWAITING_DELIVERY_STATES = /* @__PURE__ */ new Set(["pending", "leased"]);
8
+ function text(value) {
9
+ return typeof value === "string" && value.length > 0 ? value : void 0;
10
+ }
11
+ function findByIdempotencyKey(records, key) {
12
+ return records.find((record) => record.idempotencyKey === key);
13
+ }
14
+ var SessionCommandQueue = class {
15
+ workspacePath;
16
+ teamId;
17
+ deliver;
18
+ bindingGenerationOf;
19
+ now;
20
+ file;
21
+ constructor(options) {
22
+ if (text(options?.workspacePath) === void 0) throw new LiveTeamsError(ERROR_CODES.CAPABILITY_UNAVAILABLE, "command queue requires a workspacePath");
23
+ if (text(options?.teamId) === void 0) throw new LiveTeamsError(ERROR_CODES.CAPABILITY_UNAVAILABLE, "command queue requires a teamId");
24
+ if (typeof options?.deliver !== "function") throw new LiveTeamsError(ERROR_CODES.CAPABILITY_UNAVAILABLE, "command queue requires a delivery port");
25
+ this.workspacePath = path.resolve(options.workspacePath);
26
+ this.teamId = options.teamId;
27
+ this.deliver = options.deliver;
28
+ this.bindingGenerationOf = options.bindingGenerationOf;
29
+ this.now = options.now ?? Date.now;
30
+ this.now;
31
+ this.file = path.join(this.workspacePath, ".dsh-live-teams", "teams", this.teamId, "commands.json");
32
+ }
33
+ async list() {
34
+ return this.read();
35
+ }
36
+ async listPending(teamId) {
37
+ if (teamId !== void 0 && teamId !== this.teamId) return [];
38
+ return (await this.read()).filter((record) => AWAITING_DELIVERY_STATES.has(record.state));
39
+ }
40
+ async admit(request) {
41
+ const idempotencyKey = text(request?.idempotencyKey);
42
+ const sessionId = text(request?.sessionId);
43
+ if (idempotencyKey === void 0) throw new LiveTeamsError(ERROR_CODES.CAPABILITY_UNAVAILABLE, "admit requires an idempotencyKey");
44
+ if (sessionId === void 0) throw new LiveTeamsError(ERROR_CODES.CAPABILITY_UNAVAILABLE, "admit requires a sessionId");
45
+ if (request.mode !== "queue" && request.mode !== "steer") throw new LiveTeamsError(ERROR_CODES.DELIVERY_REJECTED, `Unsupported delivery mode "${String(request.mode)}"`);
46
+ if (!Number.isInteger(request.bindingGeneration)) throw new LiveTeamsError(ERROR_CODES.CAPABILITY_UNAVAILABLE, "admit requires a bindingGeneration");
47
+ return this.mutate((records) => {
48
+ const existing = findByIdempotencyKey(records, idempotencyKey);
49
+ if (existing !== void 0) return {
50
+ record: clone(existing),
51
+ duplicate: true
52
+ };
53
+ const record = {
54
+ commandId: randomUUID(),
55
+ idempotencyKey,
56
+ causationId: text(request.causationId),
57
+ sessionId,
58
+ bindingGeneration: request.bindingGeneration,
59
+ mode: request.mode,
60
+ reason: text(request.reason) ?? "unspecified",
61
+ content: clone(request.content ?? []),
62
+ requestId: `${this.teamId}:${idempotencyKey}`,
63
+ state: "pending",
64
+ receipt: void 0,
65
+ failureCode: void 0,
66
+ failureDetail: void 0,
67
+ revision: 1
68
+ };
69
+ records.push(record);
70
+ return {
71
+ record: clone(record),
72
+ duplicate: false
73
+ };
74
+ });
75
+ }
76
+ async dispatch(commandId) {
77
+ const reserved = await this.transition(commandId, (record) => {
78
+ if (DELIVERED_STATES.has(record.state)) return {
79
+ next: record,
80
+ stop: true
81
+ };
82
+ record.state = "leased";
83
+ record.revision += 1;
84
+ return {
85
+ next: record,
86
+ stop: false,
87
+ call: true
88
+ };
89
+ });
90
+ if (reserved.call !== true) return reserved.record;
91
+ let outcome;
92
+ try {
93
+ const current = typeof this.bindingGenerationOf === "function" ? await this.bindingGenerationOf(reserved.record.sessionId) : reserved.record.bindingGeneration;
94
+ if (current !== void 0 && current !== reserved.record.bindingGeneration) throw new LiveTeamsError(ERROR_CODES.BINDING_STALE, BINDING_STALE_MESSAGE, { auditDetail: `admitted for generation ${reserved.record.bindingGeneration}, current ${current}` });
95
+ await this.deliver(clone(reserved.record));
96
+ outcome = {
97
+ state: "admitted",
98
+ receipt: "accepted"
99
+ };
100
+ } catch (error) {
101
+ const domain = error instanceof LiveTeamsError ? error : new LiveTeamsError(ERROR_CODES.DELIVERY_REJECTED, error instanceof Error ? error.message : String(error), { cause: error });
102
+ outcome = {
103
+ state: "failed",
104
+ failureCode: domain.code,
105
+ failureDetail: domain.auditDetail ?? domain.message
106
+ };
107
+ }
108
+ return (await this.transition(commandId, (record) => {
109
+ record.state = outcome.state;
110
+ record.receipt = outcome.receipt;
111
+ record.failureCode = outcome.failureCode;
112
+ record.failureDetail = outcome.failureDetail;
113
+ record.revision += 1;
114
+ return {
115
+ next: record,
116
+ stop: true
117
+ };
118
+ })).record;
119
+ }
120
+ async retry(commandId) {
121
+ const prepared = await this.transition(commandId, (record) => {
122
+ record.state = "pending";
123
+ record.receipt = void 0;
124
+ record.failureCode = void 0;
125
+ record.failureDetail = void 0;
126
+ record.revision += 1;
127
+ return {
128
+ next: record,
129
+ stop: true
130
+ };
131
+ });
132
+ const before = (await this.read()).length;
133
+ const dispatched = await this.dispatch(prepared.record.commandId);
134
+ if (before !== (await this.read()).length) throw new LiveTeamsError(ERROR_CODES.STATE_CORRUPT, "retry changed the number of durable commands");
135
+ return dispatched;
136
+ }
137
+ async get(commandId) {
138
+ const record = (await this.read()).find((entry) => entry.commandId === commandId);
139
+ return record === void 0 ? void 0 : clone(record);
140
+ }
141
+ async transition(commandId, change) {
142
+ return this.mutate((records) => {
143
+ const index = records.findIndex((entry) => entry.commandId === commandId);
144
+ if (index < 0) throw new LiveTeamsError(ERROR_CODES.DELIVERY_REJECTED, `unknown command "${commandId}"`);
145
+ const outcome = change(records[index]);
146
+ records[index] = outcome.next;
147
+ return {
148
+ record: clone(outcome.next),
149
+ call: outcome.call === true
150
+ };
151
+ });
152
+ }
153
+ async mutate(change) {
154
+ return withProcessLock(this.file, async () => {
155
+ const records = await this.read();
156
+ const result = change(records);
157
+ await atomicWriteJson(this.file, {
158
+ schemaVersion: 1,
159
+ teamId: this.teamId,
160
+ commands: records
161
+ });
162
+ return result;
163
+ });
164
+ }
165
+ async read() {
166
+ if (!await fileExists(this.file)) return [];
167
+ let parsed;
168
+ try {
169
+ parsed = await readJson(this.file);
170
+ } catch (error) {
171
+ throw new LiveTeamsError(ERROR_CODES.STATE_CORRUPT, "command state is not readable JSON", {
172
+ cause: error,
173
+ auditDetail: `failed to read ${this.file}`
174
+ });
175
+ }
176
+ const commands = parsed !== null && typeof parsed === "object" ? parsed.commands : void 0;
177
+ if (!Array.isArray(commands)) throw new LiveTeamsError(ERROR_CODES.STATE_CORRUPT, "command state has no command list", { auditDetail: `invalid shape in ${this.file}` });
178
+ return commands;
179
+ }
180
+ };
181
+ function createCommandQueue(options) {
182
+ return new SessionCommandQueue(options);
183
+ }
184
+ //#endregion
185
+ export { SessionCommandQueue, createCommandQueue };
@@ -0,0 +1,109 @@
1
+ import { createRequire } from "node:module";
2
+ //#region src/compatibility.ts
3
+ /**
4
+ * Read a host service the way the plugin itself does.
5
+ *
6
+ * Cordis **throws** when a service that was not declared in `inject` is read as a property, so a
7
+ * compatibility check that pokes at `ctx.tools` does not report a missing service — it crashes the
8
+ * plugin it was meant to protect. The safe reader is `ctx.get(name)`; the property access stays as a
9
+ * fallback for a plain object, wrapped so that a throwing host reads as "not available".
10
+ */
11
+ function service(host, name) {
12
+ try {
13
+ const get = host.get;
14
+ if (typeof get === "function") return get.call(host, name);
15
+ } catch {
16
+ return;
17
+ }
18
+ try {
19
+ return host[name];
20
+ } catch {
21
+ return;
22
+ }
23
+ }
24
+ function hasFunction(host, name, member) {
25
+ const value = service(host, name);
26
+ if (value === void 0 || value === null) return false;
27
+ if (member === void 0) return true;
28
+ try {
29
+ return typeof value[member] === "function";
30
+ } catch {
31
+ return false;
32
+ }
33
+ }
34
+ /**
35
+ * The host surfaces this plugin calls, and whether it can live without them.
36
+ *
37
+ * Keep this list honest: it is the contract with the host. Adding a call to a new service without
38
+ * adding it here means the next DSH release can remove it silently.
39
+ */
40
+ const HOST_CAPABILITIES = Object.freeze([
41
+ {
42
+ name: "sessionController.create",
43
+ required: true,
44
+ present: (host) => hasFunction(host, "sessionController", "create")
45
+ },
46
+ {
47
+ name: "agents.get",
48
+ required: true,
49
+ present: (host) => hasFunction(host, "agents", "get")
50
+ },
51
+ {
52
+ name: "systemPrompt",
53
+ required: true,
54
+ present: (host) => service(host, "systemPrompt") !== void 0 && service(host, "systemPrompt") !== null
55
+ },
56
+ {
57
+ name: "workspaceRegistry",
58
+ required: true,
59
+ present: (host) => service(host, "workspaceRegistry") !== void 0 && service(host, "workspaceRegistry") !== null
60
+ },
61
+ {
62
+ name: "tools.register",
63
+ required: false,
64
+ present: (host) => hasFunction(host, "tools", "register")
65
+ },
66
+ {
67
+ name: "webServer.register",
68
+ required: false,
69
+ present: (host) => hasFunction(host, "webServer", "register")
70
+ }
71
+ ]);
72
+ /** The version of the host packages this plugin is running against, when it can be read. */
73
+ function hostVersion() {
74
+ try {
75
+ const require = createRequire(import.meta.url);
76
+ for (const name of ["@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-session"]) try {
77
+ const manifest = require(`${name}/package.json`);
78
+ if (typeof manifest.version === "string") return manifest.version;
79
+ } catch {}
80
+ } catch {}
81
+ }
82
+ /** The versions this plugin declares support for, read from its own manifest. */
83
+ function declaredReleases() {
84
+ try {
85
+ const manifest = createRequire(import.meta.url)("../package.json");
86
+ return Object.keys(manifest.dsh?.compatibility?.dshReleases ?? {});
87
+ } catch {
88
+ return [];
89
+ }
90
+ }
91
+ function checkCompatibility(host, options = {}) {
92
+ const capabilities = options.capabilities ?? HOST_CAPABILITIES;
93
+ const missing = capabilities.filter((capability) => capability.required && !capability.present(host)).map((capability) => capability.name);
94
+ const absent = capabilities.filter((capability) => !capability.required && !capability.present(host)).map((capability) => capability.name);
95
+ const version = options.hostVersion ?? hostVersion();
96
+ const declared = options.declared ?? declaredReleases();
97
+ const undeclared = version !== void 0 && declared.length > 0 && !declared.includes(version);
98
+ const verdict = missing.length > 0 ? "incompatible" : absent.length > 0 || undeclared ? "degraded" : "compatible";
99
+ const detail = missing.length > 0 ? `DSH ${version ?? "of unknown version"} does not offer ${missing.join(", ")}; the team stays off until the plugin is updated` : absent.length > 0 ? `running without ${absent.join(", ")}; the team works, the surfaces they serve do not` : undeclared ? `DSH ${version ?? "of unknown version"} is not in this plugin's declared releases (${declared.join(", ")}); everything the plugin calls is present` : `DSH ${version ?? "of unknown version"} offers everything this plugin calls`;
100
+ return {
101
+ verdict,
102
+ ...version === void 0 ? {} : { hostVersion: version },
103
+ missing,
104
+ absent,
105
+ detail
106
+ };
107
+ }
108
+ //#endregion
109
+ export { HOST_CAPABILITIES, checkCompatibility, declaredReleases, hostVersion };
@@ -0,0 +1,110 @@
1
+ import { MemberRecord, TeamState } from "./team-store.js";
2
+ import { TeamTask } from "./task-store.js";
3
+ import { LiveTeamsRole } from "./roles.js";
4
+ //#region src/context-provider.d.ts
5
+ export declare const MEMBERSHIP_CONTEXT_NAME = "live-teams:membership";
6
+ export declare const MEMBERSHIP_CONTEXT_ORDER = 125;
7
+ export declare function sessionIdOf(assemblyContext: unknown): string | undefined;
8
+ export type MembershipMember = Pick<MemberRecord, 'memberId' | 'displayName' | 'role' | 'availability' | 'bindingGeneration' | 'contractRevision' | 'note'> & {
9
+ sessionId?: string;
10
+ roles?: readonly string[];
11
+ [key: string]: unknown;
12
+ };
13
+ export type RoleRegistrySync = {
14
+ resolve: (value: string) => Pick<LiveTeamsRole, 'body'> | undefined;
15
+ };
16
+ export type MembershipScope = {
17
+ teamId: string;
18
+ /** Set when a human wrote into this member's Session mid-attempt (B31). */
19
+ humanIntervenedAt?: number;
20
+ teamName?: string;
21
+ activeAttemptId?: string;
22
+ activeTask?: TeamTask;
23
+ tasks?: readonly TeamTask[];
24
+ formalToolsAvailable?: boolean;
25
+ roleRegistry?: RoleRegistrySync;
26
+ teamState?: TeamState;
27
+ workspacePath?: string;
28
+ live?: (member: MemberRecord) => boolean;
29
+ /**
30
+ * Comments addressed to this member that it has not acknowledged. A comment is stored with
31
+ * `wakePolicy: never`, so nothing wakes the Session for it — without this line the recipient never
32
+ * learns it was written (external review, finding 13).
33
+ */
34
+ comments?: readonly {
35
+ from: string;
36
+ content: string;
37
+ at: number;
38
+ }[];
39
+ };
40
+ export declare function renderMembership(member: MembershipMember, scope: MembershipScope): string;
41
+ type SyncStore = {
42
+ memberBySessionSync: (teamId: string, sessionId: string) => MembershipMember | undefined;
43
+ teamStateSync?: (teamId: string) => TeamState | undefined;
44
+ attemptSync?: (teamId: string, taskId: string) => {
45
+ humanIntervenedAt?: number;
46
+ humanIntervenedContractRevision?: number;
47
+ generation: number;
48
+ } | undefined;
49
+ activeTaskForMemberSync?: (teamId: string, memberId: string) => TeamTask | undefined;
50
+ tasksSync?: (teamId: string) => readonly TeamTask[] | undefined;
51
+ };
52
+ export type MembershipContextBinding = {
53
+ readonly store: SyncStore;
54
+ readonly teamId: string;
55
+ readonly teamName?: string | (() => string);
56
+ readonly workspacePath?: string;
57
+ readonly enabled?: () => boolean;
58
+ readonly roleRegistry?: RoleRegistrySync;
59
+ readonly live?: (member: MemberRecord) => boolean;
60
+ readonly activeTaskForMemberSync?: (teamId: string, memberId: string) => TeamTask | undefined;
61
+ readonly tasksSync?: (teamId: string) => readonly TeamTask[] | undefined;
62
+ readonly attemptSync?: (teamId: string, taskId: string) => {
63
+ humanIntervenedAt?: number;
64
+ humanIntervenedContractRevision?: number;
65
+ generation: number;
66
+ } | undefined;
67
+ /** Unread comments for a member, read synchronously because the context is assembled that way. */
68
+ readonly commentsSync?: (teamId: string, memberId: string) => readonly {
69
+ from: string;
70
+ content: string;
71
+ at: number;
72
+ }[];
73
+ };
74
+ export type MembershipContextOptions = {
75
+ readonly store?: SyncStore;
76
+ readonly teamId?: string;
77
+ readonly teamName?: string | (() => string);
78
+ readonly workspacePath?: string;
79
+ readonly onError?: (error: unknown) => void;
80
+ readonly formalToolsAvailable?: boolean;
81
+ readonly roleRegistry?: RoleRegistrySync;
82
+ readonly resolveSession?: (sessionId: string) => MembershipContextBinding | undefined;
83
+ readonly enabled?: () => boolean;
84
+ readonly live?: (member: MemberRecord) => boolean;
85
+ readonly activeTaskForMemberSync?: (teamId: string, memberId: string) => TeamTask | undefined;
86
+ readonly tasksSync?: (teamId: string) => readonly TeamTask[] | undefined;
87
+ readonly attemptSync?: (teamId: string, taskId: string) => {
88
+ humanIntervenedAt?: number;
89
+ humanIntervenedContractRevision?: number;
90
+ generation: number;
91
+ } | undefined;
92
+ readonly commentsSync?: (teamId: string, memberId: string) => readonly {
93
+ from: string;
94
+ content: string;
95
+ at: number;
96
+ }[];
97
+ };
98
+ export type MembershipContextProvider = {
99
+ name: string;
100
+ order: number;
101
+ teamId: string;
102
+ text: (assemblyContext: unknown) => string;
103
+ definition: () => {
104
+ name: string;
105
+ order: number;
106
+ text: (assemblyContext: unknown) => string;
107
+ };
108
+ };
109
+ export declare function createMembershipContextProvider(options: MembershipContextOptions): MembershipContextProvider;
110
+ //#endregion