@opengeni/worker-bundle 1.0.0-canary.0 → 1.0.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 (244) hide show
  1. package/dist/activities/agent-run-admission.d.ts +3 -1
  2. package/dist/activities/agent-turn/admission.d.ts +24 -3
  3. package/dist/activities/agent-turn/agent-build.d.ts +4 -0
  4. package/dist/activities/agent-turn/claude-usage-observer.d.ts +58 -0
  5. package/dist/activities/agent-turn/code-search.d.ts +60 -0
  6. package/dist/activities/agent-turn/codex-capacity.d.ts +13 -0
  7. package/dist/activities/agent-turn/codex-plan-entitlement.d.ts +105 -0
  8. package/dist/activities/agent-turn/codex.d.ts +3 -2
  9. package/dist/activities/agent-turn/compaction-prep.d.ts +21 -5
  10. package/dist/activities/agent-turn/credential-leases.d.ts +19 -33
  11. package/dist/activities/agent-turn/credential-restriction.d.ts +3 -0
  12. package/dist/activities/agent-turn/errors.d.ts +82 -11
  13. package/dist/activities/agent-turn/failure-settlement.d.ts +6 -3
  14. package/dist/activities/agent-turn/final-reply.d.ts +18 -0
  15. package/dist/activities/agent-turn/finalization-monitor.d.ts +17 -0
  16. package/dist/activities/agent-turn/finalization.d.ts +4 -2
  17. package/dist/activities/agent-turn/github-repository-bindings.d.ts +2 -0
  18. package/dist/activities/agent-turn/governance-model.d.ts +9 -2
  19. package/dist/activities/agent-turn/history-sink.d.ts +2 -0
  20. package/dist/activities/agent-turn/history.d.ts +2 -0
  21. package/dist/activities/agent-turn/input-wait-reply.d.ts +41 -0
  22. package/dist/activities/agent-turn/media-artifacts.d.ts +5 -3
  23. package/dist/activities/agent-turn/model-call-admission.d.ts +15 -0
  24. package/dist/activities/agent-turn/model-usage.d.ts +6 -0
  25. package/dist/activities/agent-turn/model-wire-dispatch.d.ts +15 -0
  26. package/dist/activities/agent-turn/optional-repositories.d.ts +59 -0
  27. package/dist/activities/agent-turn/provider-dispatch-barrier.d.ts +12 -1
  28. package/dist/activities/agent-turn/quiescence.d.ts +7 -7
  29. package/dist/activities/agent-turn/recovery-warning.d.ts +9 -0
  30. package/dist/activities/agent-turn/refresh-credentials.d.ts +6 -0
  31. package/dist/activities/agent-turn/run-credentials.d.ts +8 -3
  32. package/dist/activities/agent-turn/sandbox-establish.d.ts +2 -1
  33. package/dist/activities/agent-turn/sandbox-provision.d.ts +3 -1
  34. package/dist/activities/agent-turn/sandbox-route.d.ts +3 -20
  35. package/dist/activities/agent-turn/sandbox-runtime.d.ts +2 -2
  36. package/dist/activities/agent-turn/session-title.d.ts +34 -2
  37. package/dist/activities/agent-turn/skill-checkout.d.ts +25 -6
  38. package/dist/activities/agent-turn/skill-read.d.ts +70 -2
  39. package/dist/activities/agent-turn/skill-remove.d.ts +14 -0
  40. package/dist/activities/agent-turn/skill-search.d.ts +3 -1
  41. package/dist/activities/agent-turn/skill-tools.d.ts +15 -2
  42. package/dist/activities/agent-turn/skill-transfer.d.ts +18 -6
  43. package/dist/activities/agent-turn/stream-attempt.d.ts +7 -3
  44. package/dist/activities/agent-turn/subscription-lease.d.ts +38 -0
  45. package/dist/activities/agent-turn/tool-environment.d.ts +56 -6
  46. package/dist/activities/agent-turn/tool-policy.d.ts +43 -0
  47. package/dist/activities/agent-turn/turn-context.d.ts +29 -0
  48. package/dist/activities/agent-turn/turn-sandbox-access.d.ts +3 -0
  49. package/dist/activities/agent-turn/xai-capacity.d.ts +2 -1
  50. package/dist/activities/automations.d.ts +3 -1
  51. package/dist/activities/browser-deadline-checkpoint.d.ts +29 -0
  52. package/dist/activities/codemode-dispatcher.d.ts +9 -2
  53. package/dist/activities/codex-capacity.d.ts +18 -2
  54. package/dist/activities/codex-rotation.d.ts +7 -1
  55. package/dist/activities/context-compaction.d.ts +16 -2
  56. package/dist/activities/gmail-files.d.ts +32 -0
  57. package/dist/activities/goals.d.ts +8 -0
  58. package/dist/activities/knowledge-indexing.d.ts +19 -0
  59. package/dist/activities/mcp-account-routes.d.ts +3 -0
  60. package/dist/activities/mcp-credentials.d.ts +12 -13
  61. package/dist/activities/mcp-operation-authority.d.ts +0 -5
  62. package/dist/activities/model-call-fact-reconciler.d.ts +34 -0
  63. package/dist/activities/open-suffix-resume.d.ts +13 -0
  64. package/dist/activities/parent-wake.d.ts +6 -12
  65. package/dist/activities/retained-screenshots.d.ts +7 -2
  66. package/dist/activities/rig-verification.d.ts +2 -3
  67. package/dist/activities/run-credential-renewal.d.ts +1 -0
  68. package/dist/activities/run-credentials.d.ts +15 -3
  69. package/dist/activities/sandbox-images.d.ts +1 -1
  70. package/dist/activities/sandbox-lease.d.ts +47 -9
  71. package/dist/activities/scheduled-tasks.d.ts +4 -35
  72. package/dist/activities/session-state.d.ts +70 -3
  73. package/dist/activities/types.d.ts +55 -6
  74. package/dist/activities/workspace-capture.d.ts +2 -0
  75. package/dist/activities/workspace-credential-provider.d.ts +46 -0
  76. package/dist/{activities-control-HMFGSKC5.js → activities-control-BNPTRTOE.js} +1896 -418
  77. package/dist/activities-control-BNPTRTOE.js.map +1 -0
  78. package/dist/activities-control.d.ts +182 -4
  79. package/dist/{activities-turn-AP67JADS.js → activities-turn-5LGZOSVU.js} +4476 -1327
  80. package/dist/activities-turn-5LGZOSVU.js.map +1 -0
  81. package/dist/activities.d.ts +182 -4
  82. package/dist/artifact-materializer-entry.js +5 -2
  83. package/dist/artifact-materializer-entry.js.map +1 -1
  84. package/dist/artifact-outbox-entry.js +5 -2
  85. package/dist/artifact-outbox-entry.js.map +1 -1
  86. package/dist/browser-deadline-checkpoint-contract.d.ts +2 -0
  87. package/dist/{chunk-A4XQWVZK.js → chunk-3ZTRWDMN.js} +307 -4
  88. package/dist/chunk-3ZTRWDMN.js.map +1 -0
  89. package/dist/{chunk-RAMNYSAW.js → chunk-4ER3YXOT.js} +22 -4
  90. package/dist/chunk-4ER3YXOT.js.map +1 -0
  91. package/dist/{chunk-CYITU5UH.js → chunk-LJX7Z2KP.js} +1445 -343
  92. package/dist/chunk-LJX7Z2KP.js.map +1 -0
  93. package/dist/editable-artifact-materializer-service.d.ts +2 -2
  94. package/dist/editable-artifact-outbox-service.d.ts +2 -2
  95. package/dist/host-export-pump.d.ts +3 -1
  96. package/dist/host-export.d.ts +1 -1
  97. package/dist/host-export.js +1 -1
  98. package/dist/http.d.ts +1 -1
  99. package/dist/index.d.ts +95 -3
  100. package/dist/index.js +137 -43
  101. package/dist/index.js.map +1 -1
  102. package/dist/observability-metrics.d.ts +118 -8
  103. package/dist/retained-process-diagnostics.d.ts +4 -0
  104. package/dist/retained-process-retry.d.ts +7 -0
  105. package/dist/sandbox-resume.d.ts +55 -13
  106. package/dist/sandbox-routing.d.ts +5 -3
  107. package/dist/sandbox-snapshot-diagnostics.d.ts +20 -0
  108. package/dist/session-event-fanout.d.ts +6 -0
  109. package/dist/warm-capture-holder.d.ts +3 -0
  110. package/dist/worker-service-lifecycle.d.ts +12 -0
  111. package/dist/workflow-bundle.js +273 -41
  112. package/dist/workflows/activities.d.ts +1 -1
  113. package/dist/workflows/browser-deadline-checkpoint.d.ts +9 -0
  114. package/dist/workflows/scheduled-tasks.d.ts +4 -0
  115. package/dist/workflows/session.d.ts +36 -0
  116. package/dist/workflows.d.ts +1 -0
  117. package/package.json +21 -19
  118. package/src/activities/agent-run-admission.ts +17 -1
  119. package/src/activities/agent-turn/admission.ts +66 -3
  120. package/src/activities/agent-turn/agent-build.ts +84 -10
  121. package/src/activities/agent-turn/claim.ts +186 -43
  122. package/src/activities/agent-turn/claude-usage-observer.ts +221 -0
  123. package/src/activities/agent-turn/code-search.ts +275 -0
  124. package/src/activities/agent-turn/codex-capacity.ts +172 -8
  125. package/src/activities/agent-turn/codex-plan-entitlement.ts +254 -0
  126. package/src/activities/agent-turn/codex.ts +13 -5
  127. package/src/activities/agent-turn/compaction-prep.ts +170 -102
  128. package/src/activities/agent-turn/credential-leases.ts +122 -216
  129. package/src/activities/agent-turn/credential-restriction.ts +14 -0
  130. package/src/activities/agent-turn/errors.ts +647 -48
  131. package/src/activities/agent-turn/failure-settlement.ts +682 -130
  132. package/src/activities/agent-turn/final-reply.ts +46 -0
  133. package/src/activities/agent-turn/finalization-monitor.ts +120 -0
  134. package/src/activities/agent-turn/finalization.ts +146 -43
  135. package/src/activities/agent-turn/github-repository-bindings.ts +3 -1
  136. package/src/activities/agent-turn/governance-model.ts +69 -28
  137. package/src/activities/agent-turn/history-sink.ts +16 -2
  138. package/src/activities/agent-turn/history.ts +38 -11
  139. package/src/activities/agent-turn/input-wait-reply.ts +103 -0
  140. package/src/activities/agent-turn/knowledge-source-tools.ts +2 -0
  141. package/src/activities/agent-turn/media-artifacts.ts +2 -1
  142. package/src/activities/agent-turn/model-call-admission.ts +94 -0
  143. package/src/activities/agent-turn/model-usage.ts +64 -3
  144. package/src/activities/agent-turn/model-wire-dispatch.ts +27 -0
  145. package/src/activities/agent-turn/optional-repositories.ts +224 -0
  146. package/src/activities/agent-turn/provider-dispatch-barrier.ts +22 -0
  147. package/src/activities/agent-turn/quiescence.ts +10 -7
  148. package/src/activities/agent-turn/recovery-warning.ts +16 -0
  149. package/src/activities/agent-turn/refresh-credentials.ts +44 -0
  150. package/src/activities/agent-turn/run-credentials.ts +116 -55
  151. package/src/activities/agent-turn/run.ts +320 -93
  152. package/src/activities/agent-turn/sandbox-establish.ts +84 -8
  153. package/src/activities/agent-turn/sandbox-provision.ts +3 -1
  154. package/src/activities/agent-turn/sandbox-route.ts +4 -21
  155. package/src/activities/agent-turn/sandbox-runtime.ts +343 -18
  156. package/src/activities/agent-turn/session-title.ts +81 -5
  157. package/src/activities/agent-turn/skill-checkout.ts +111 -24
  158. package/src/activities/agent-turn/skill-install.ts +1 -1
  159. package/src/activities/agent-turn/skill-read.ts +374 -28
  160. package/src/activities/agent-turn/skill-remove.ts +46 -0
  161. package/src/activities/agent-turn/skill-save.ts +1 -1
  162. package/src/activities/agent-turn/skill-search.ts +10 -1
  163. package/src/activities/agent-turn/skill-selection.ts +4 -0
  164. package/src/activities/agent-turn/skill-tools.ts +112 -18
  165. package/src/activities/agent-turn/skill-transfer.ts +68 -23
  166. package/src/activities/agent-turn/stream-attempt.ts +313 -77
  167. package/src/activities/agent-turn/subscription-lease.ts +115 -0
  168. package/src/activities/agent-turn/tool-environment.ts +316 -167
  169. package/src/activities/agent-turn/tool-policy.ts +80 -1
  170. package/src/activities/agent-turn/turn-context.ts +35 -0
  171. package/src/activities/agent-turn/turn-sandbox-access.ts +7 -1
  172. package/src/activities/agent-turn/xai-capacity.ts +94 -51
  173. package/src/activities/automations.ts +76 -4
  174. package/src/activities/browser-deadline-checkpoint.ts +279 -0
  175. package/src/activities/capabilities.ts +108 -11
  176. package/src/activities/codemode-dispatcher.ts +36 -8
  177. package/src/activities/codex-capacity.ts +60 -25
  178. package/src/activities/codex-rotation.ts +39 -17
  179. package/src/activities/context-compaction.ts +75 -18
  180. package/src/activities/gmail-files.ts +88 -0
  181. package/src/activities/goals.ts +116 -121
  182. package/src/activities/google-drive-publication.ts +1 -3
  183. package/src/activities/image-generation-operation.ts +1 -1
  184. package/src/activities/knowledge-indexing.ts +269 -15
  185. package/src/activities/knowledge-source-sync.ts +10 -0
  186. package/src/activities/mcp-account-routes.ts +17 -0
  187. package/src/activities/mcp-credentials.ts +83 -194
  188. package/src/activities/mcp-operation-authority.ts +16 -41
  189. package/src/activities/model-call-fact-reconciler.ts +112 -0
  190. package/src/activities/open-suffix-resume.ts +15 -2
  191. package/src/activities/parent-wake.ts +46 -21
  192. package/src/activities/retained-screenshots.ts +82 -11
  193. package/src/activities/rig-verification.ts +38 -37
  194. package/src/activities/run-credential-renewal.ts +13 -1
  195. package/src/activities/run-credentials.ts +59 -17
  196. package/src/activities/run-input.ts +25 -16
  197. package/src/activities/sandbox-images.ts +0 -1
  198. package/src/activities/sandbox-lease.ts +798 -82
  199. package/src/activities/scheduled-tasks.ts +550 -178
  200. package/src/activities/session-state.ts +245 -6
  201. package/src/activities/site-auth-maintenance.ts +3 -0
  202. package/src/activities/types.ts +72 -5
  203. package/src/activities/workflow-wake.ts +2 -17
  204. package/src/activities/workspace-capture.ts +13 -2
  205. package/src/activities/workspace-credential-provider.ts +283 -0
  206. package/src/activities-control.ts +4 -0
  207. package/src/activities-turn.ts +5 -0
  208. package/src/activities.ts +13 -0
  209. package/src/activity-services.ts +16 -4
  210. package/src/browser-deadline-checkpoint-contract.ts +5 -0
  211. package/src/editable-artifact-hint-broker.ts +3 -0
  212. package/src/editable-artifact-materializer-service.ts +7 -3
  213. package/src/editable-artifact-outbox-service.ts +4 -4
  214. package/src/github-rest-mcp.ts +1 -2
  215. package/src/host-export-pump.ts +26 -4
  216. package/src/host-export.ts +3 -0
  217. package/src/http.ts +4 -2
  218. package/src/index.ts +99 -37
  219. package/src/observability-metrics.ts +510 -5
  220. package/src/personal-github-git-credentials.ts +2 -1
  221. package/src/pr-review-credentials.ts +1 -3
  222. package/src/retained-process-diagnostics.ts +50 -0
  223. package/src/retained-process-retry.ts +36 -0
  224. package/src/sandbox-resume.ts +388 -101
  225. package/src/sandbox-routing.ts +150 -25
  226. package/src/sandbox-snapshot-diagnostics.ts +112 -0
  227. package/src/session-event-fanout.ts +23 -0
  228. package/src/warm-capture-holder.ts +15 -0
  229. package/src/worker-service-lifecycle.ts +53 -0
  230. package/src/workflows/activities.ts +16 -1
  231. package/src/workflows/browser-deadline-checkpoint.ts +50 -0
  232. package/src/workflows/file-upload-reaper.ts +2 -0
  233. package/src/workflows/scheduled-tasks.ts +26 -13
  234. package/src/workflows/session.ts +242 -22
  235. package/src/workflows.ts +4 -0
  236. package/dist/activities/agent-turn/knowledge-sources.d.ts +0 -20
  237. package/dist/activities/packs.d.ts +0 -55
  238. package/dist/activities-control-HMFGSKC5.js.map +0 -1
  239. package/dist/activities-turn-AP67JADS.js.map +0 -1
  240. package/dist/chunk-A4XQWVZK.js.map +0 -1
  241. package/dist/chunk-CYITU5UH.js.map +0 -1
  242. package/dist/chunk-RAMNYSAW.js.map +0 -1
  243. package/src/activities/agent-turn/knowledge-sources.ts +0 -86
  244. package/src/activities/packs.ts +0 -166
@@ -0,0 +1,283 @@
1
+ import { environmentsEncryptionKeyBytes, type Settings } from "@opengeni/config";
2
+ import {
3
+ CredentialProviderResponse,
4
+ OPENGENI_SIGNATURE_HEADER,
5
+ signOpenGeniPayload,
6
+ type CredentialProviderRequest,
7
+ type RunCredentialsRequest,
8
+ type RunCredentialsResolution,
9
+ } from "@opengeni/contracts";
10
+ import {
11
+ decryptEnvironmentValue,
12
+ resolveWorkspaceCredentialProvider,
13
+ resolveInitiatingHuman,
14
+ type Database,
15
+ } from "@opengeni/db";
16
+ import { pinnedFetch, type OutboundNetworkSettings } from "@opengeni/network";
17
+
18
+ const MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
19
+ export const GIT_CREDENTIALS_FILE = "git/credentials";
20
+ export const GIT_CREDENTIALS_FILE_ENV = "OPENGENI_GIT_CREDENTIALS_FILE";
21
+
22
+ export class CredentialProviderError extends Error {
23
+ constructor(message: string) {
24
+ super(message);
25
+ this.name = "CredentialProviderError";
26
+ }
27
+ }
28
+
29
+ export type WorkspaceRunCredentialResolver = (
30
+ input: RunCredentialsRequest,
31
+ ) => Promise<RunCredentialsResolution>;
32
+
33
+ type ProviderOk = Extract<CredentialProviderResponse, { status: "ok" }>;
34
+
35
+ function gitCredentialLine(entry: NonNullable<ProviderOk["git"]>[number]): string {
36
+ const username = encodeURIComponent(entry.username ?? "x-access-token");
37
+ return `https://${username}:${encodeURIComponent(entry.password)}@${entry.host.toLowerCase()}`;
38
+ }
39
+
40
+ /**
41
+ * Turn the provider's `git` sugar into ordinary run-credential material: one
42
+ * renewable credential-store file plus a read-only helper configured through
43
+ * GIT_CONFIG_* environment entries appended after any the host already set.
44
+ */
45
+ export function withGitCredentialHelper(response: ProviderOk): ProviderOk {
46
+ if (!response.git?.length) return response;
47
+ const environment = { ...response.environment };
48
+ const existingCount = Number.parseInt(environment.GIT_CONFIG_COUNT ?? "0", 10);
49
+ const index = Number.isFinite(existingCount) && existingCount > 0 ? existingCount : 0;
50
+ environment[`GIT_CONFIG_KEY_${index}`] = "credential.helper";
51
+ environment[`GIT_CONFIG_VALUE_${index}`] =
52
+ `!f() { test "$1" = get && sed '/^path=/d' | git credential-store --file="$${GIT_CREDENTIALS_FILE_ENV}" get; }; f`;
53
+ environment.GIT_CONFIG_COUNT = String(index + 1);
54
+ return {
55
+ ...response,
56
+ environment,
57
+ files: [
58
+ ...(response.files ?? []),
59
+ {
60
+ path: GIT_CREDENTIALS_FILE,
61
+ content: `${response.git.map(gitCredentialLine).join("\n")}\n`,
62
+ mode: "0400",
63
+ },
64
+ ],
65
+ fileEnvironment: {
66
+ ...response.fileEnvironment,
67
+ [GIT_CREDENTIALS_FILE_ENV]: GIT_CREDENTIALS_FILE,
68
+ },
69
+ };
70
+ }
71
+
72
+ export function credentialProviderRequestBody(
73
+ input: RunCredentialsRequest,
74
+ initiatingHumanSubjectId: string | null,
75
+ initiatingHuman: CredentialProviderRequest["initiatingHuman"] = null,
76
+ selection: Pick<CredentialProviderRequest, "lane" | "mcpServers"> = {
77
+ lane: "workspace",
78
+ mcpServers: [],
79
+ },
80
+ ): CredentialProviderRequest {
81
+ const via = input.initiatorContext.via;
82
+ const latestHop = Array.isArray(via) ? via.at(-1) : null;
83
+ return {
84
+ type: "credentials.request",
85
+ lane: selection.lane,
86
+ mcpServers: selection.mcpServers.map(({ id, url }) => ({ id, url })),
87
+ purpose: input.purpose,
88
+ forceRefresh: input.forceRefresh,
89
+ accountId: input.accountId,
90
+ workspaceId: input.workspaceId,
91
+ sessionId: input.sessionId,
92
+ rootSessionId: input.rootSessionId,
93
+ parentSessionId: input.parentSessionId,
94
+ turnId: input.turnId,
95
+ attemptId: input.attemptId,
96
+ initiator: { kind: input.initiator.kind, subjectId: input.initiator.subjectId },
97
+ initiatorContext: {
98
+ kind:
99
+ latestHop?.kind === "agent"
100
+ ? "agent"
101
+ : input.initiator.kind === "subject"
102
+ ? "human"
103
+ : "service",
104
+ initiator: input.initiator,
105
+ context: input.initiatorContext,
106
+ },
107
+ initiatingHumanSubjectId,
108
+ initiatingHuman,
109
+ sandboxBackend: input.effectiveSandboxBackend,
110
+ sandboxOs: input.sandboxOs,
111
+ };
112
+ }
113
+
114
+ /** Drop undefined-valued keys; the runtime normalizer re-validates everything. */
115
+ function wire<T>(value: unknown): T {
116
+ return JSON.parse(JSON.stringify(value)) as T;
117
+ }
118
+
119
+ export function toRunCredentialsResolution(
120
+ response: CredentialProviderResponse,
121
+ scope: { accountId: string; workspaceId: string; sessionId: string },
122
+ ): RunCredentialsResolution {
123
+ if (response.status === "not_applicable") return { status: "not_applicable", ...scope };
124
+ if (response.status === "auth_needed") {
125
+ return wire({ status: "auth_needed", ...scope, authNeeded: response.authNeeded });
126
+ }
127
+ const { git: _git, ...material } = withGitCredentialHelper(response);
128
+ return wire({ ...material, status: "ok", ...scope, environment: material.environment ?? {} });
129
+ }
130
+
131
+ async function readBoundedText(response: Response): Promise<string> {
132
+ const declared = Number(response.headers.get("content-length"));
133
+ if (Number.isFinite(declared) && declared > MAX_RESPONSE_BYTES) {
134
+ throw new CredentialProviderError("credential provider response is too large");
135
+ }
136
+ const reader = response.body?.getReader();
137
+ if (!reader) return "";
138
+ const chunks: Uint8Array[] = [];
139
+ let total = 0;
140
+ for (;;) {
141
+ const { done, value } = await reader.read();
142
+ if (done) break;
143
+ total += value.byteLength;
144
+ if (total > MAX_RESPONSE_BYTES) {
145
+ await reader.cancel().catch(() => undefined);
146
+ throw new CredentialProviderError("credential provider response is too large");
147
+ }
148
+ chunks.push(value);
149
+ }
150
+ return new TextDecoder().decode(Buffer.concat(chunks));
151
+ }
152
+
153
+ export type WorkspaceCredentialProviderDeps = {
154
+ fetch?: typeof pinnedFetch;
155
+ resolveProvider?: typeof resolveWorkspaceCredentialProvider;
156
+ resolveHuman?: typeof resolveInitiatingHuman;
157
+ /** Trusted selected session-attached remote targets, frozen by the worker. */
158
+ mcpServers?: readonly { id: string; url: string }[];
159
+ };
160
+
161
+ /**
162
+ * Resolve the workspace's configured HTTP credential provider, if any. The
163
+ * returned resolver has the same contract as a host `runCredentials` port, so
164
+ * provisioning, file materialization, renewal, and reconnect notices reuse the
165
+ * existing run-credential lifecycle unchanged.
166
+ */
167
+ export async function workspaceCredentialProviderResolver(
168
+ db: Database,
169
+ settings: Settings,
170
+ scope: { accountId: string; workspaceId: string },
171
+ initiatingHumanSubjectId: string | null,
172
+ deps: WorkspaceCredentialProviderDeps = {},
173
+ ): Promise<WorkspaceRunCredentialResolver | null> {
174
+ const row = await (deps.resolveProvider ?? resolveWorkspaceCredentialProvider)(db, scope);
175
+ if (!row) return null;
176
+ if (!row.enabled) {
177
+ // A disabled workspace override is an explicit pause, not an absent
178
+ // provider. Return a resolver so the deployment port cannot be borrowed.
179
+ return async (input) => ({
180
+ status: "not_applicable",
181
+ accountId: input.accountId,
182
+ workspaceId: input.workspaceId,
183
+ sessionId: input.sessionId,
184
+ });
185
+ }
186
+ const key = environmentsEncryptionKeyBytes(settings);
187
+ if (!key) {
188
+ throw new CredentialProviderError(
189
+ "OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is required to use a workspace credential provider",
190
+ );
191
+ }
192
+ const fetchImpl = deps.fetch ?? pinnedFetch;
193
+ const network: OutboundNetworkSettings = settings;
194
+ const selection = {
195
+ lane:
196
+ "workspaceId" in row && row.workspaceId !== null
197
+ ? ("workspace" as const)
198
+ : ("organization" as const),
199
+ mcpServers: (deps.mcpServers ?? []).map(({ id, url }) => ({ id, url })),
200
+ };
201
+ return async (input) => {
202
+ const echo = {
203
+ accountId: input.accountId,
204
+ workspaceId: input.workspaceId,
205
+ sessionId: input.sessionId,
206
+ };
207
+ // Rotation and disabling take effect on the next outbound request. Never
208
+ // switch registration mid-attempt if deletion exposes an inherited row.
209
+ const current = await (deps.resolveProvider ?? resolveWorkspaceCredentialProvider)(db, scope);
210
+ if (!current?.enabled || current.id !== row.id) {
211
+ return { status: "not_applicable", ...echo };
212
+ }
213
+ const secret = decryptEnvironmentValue(key, current.secretEncrypted);
214
+ const initiatingHuman = await (deps.resolveHuman ?? resolveInitiatingHuman)(
215
+ db,
216
+ scope,
217
+ initiatingHumanSubjectId,
218
+ input.turnId,
219
+ );
220
+ const body = JSON.stringify(
221
+ credentialProviderRequestBody(input, initiatingHumanSubjectId, initiatingHuman, selection),
222
+ );
223
+ let status: number;
224
+ let text: string;
225
+ try {
226
+ const response = await fetchImpl(
227
+ current.url,
228
+ {
229
+ method: "POST",
230
+ headers: {
231
+ "content-type": "application/json",
232
+ "user-agent": "OpenGeni-CredentialProvider/1",
233
+ [OPENGENI_SIGNATURE_HEADER]: await signOpenGeniPayload(secret, body),
234
+ },
235
+ body,
236
+ signal: AbortSignal.timeout(current.timeoutMs),
237
+ },
238
+ network,
239
+ { label: "Workspace credential provider", requireHttpsOutsideLocalTest: true },
240
+ );
241
+ status = response.status;
242
+ text = await readBoundedText(response);
243
+ } catch (error) {
244
+ return failure(input, echo, `request failed: ${errorName(error)}`);
245
+ }
246
+ if (status < 200 || status >= 300) {
247
+ return failure(input, echo, `returned HTTP ${status}`);
248
+ }
249
+ let parsed: CredentialProviderResponse;
250
+ try {
251
+ parsed = CredentialProviderResponse.parse(JSON.parse(text));
252
+ } catch {
253
+ return failure(input, echo, "returned an invalid response body");
254
+ }
255
+ return toRunCredentialsResolution(parsed, echo);
256
+ };
257
+ }
258
+
259
+ function errorName(error: unknown): string {
260
+ if (error instanceof Error) {
261
+ return error.name === "TimeoutError" || error.name === "AbortError" ? "timeout" : error.name;
262
+ }
263
+ return "unknown";
264
+ }
265
+
266
+ /**
267
+ * A failed renewal throws so the renewal loop keeps the last good material and
268
+ * retries with backoff. A failed first provision degrades to a visible
269
+ * reconnect notice instead of blocking the turn.
270
+ */
271
+ function failure(
272
+ input: RunCredentialsRequest,
273
+ echo: { accountId: string; workspaceId: string; sessionId: string },
274
+ reason: string,
275
+ ): RunCredentialsResolution {
276
+ const message = `Workspace credential provider ${reason}`;
277
+ if (input.purpose === "renewal") throw new CredentialProviderError(message);
278
+ return {
279
+ status: "auth_needed",
280
+ ...echo,
281
+ authNeeded: [{ reason: "refresh_failed", message }],
282
+ };
283
+ }
@@ -2,10 +2,12 @@ import { createKnowledgeIndexingActivities } from "./activities/knowledge-indexi
2
2
  import { createSharedActivityServices } from "./activity-services";
3
3
  import { createCodexCapacityActivities } from "./activities/codex-capacity";
4
4
  import { createBrowserStateArtifactMaintenanceActivities } from "./activities/browser-state-artifact-reaper";
5
+ import { createBrowserDeadlineCheckpointActivities } from "./activities/browser-deadline-checkpoint";
5
6
  import { createAutomationActivities } from "./activities/automations";
6
7
  import { createDocumentActivities } from "./activities/documents";
7
8
  import { createFileUploadReaperActivities } from "./activities/file-upload-reaper";
8
9
  import { createGoalActivities } from "./activities/goals";
10
+ import { createModelCallFactReconcilerActivities } from "./activities/model-call-fact-reconciler";
9
11
  import { createKnowledgeSourceSyncActivities } from "./activities/knowledge-source-sync";
10
12
  import { createRetainedScreenshotMaintenanceActivities } from "./activities/retained-screenshot-reaper";
11
13
  import { createRigVerificationActivities } from "./activities/rig-verification";
@@ -39,7 +41,9 @@ export function createControlActivitiesFromServices(
39
41
  ...createCodexCapacityActivities(services),
40
42
  ...createRigVerificationActivities(services),
41
43
  ...createBrowserStateArtifactMaintenanceActivities(services),
44
+ ...createBrowserDeadlineCheckpointActivities(services),
42
45
  ...createFileUploadReaperActivities(services),
46
+ ...createModelCallFactReconcilerActivities(services),
43
47
  ...createRetainedScreenshotMaintenanceActivities(services),
44
48
  ...createWorkflowWakeActivities(services),
45
49
  ...createSandboxLeaseActivities(services),
@@ -12,6 +12,9 @@ function createTurnActivityServices(
12
12
  let servicesPromise: Promise<TurnActivityServices> | null = null;
13
13
  return async () => {
14
14
  servicesPromise ??= (async () => {
15
+ if (!dependencies.requestWorkerDrain) {
16
+ throw new Error("Turn activities require a host requestWorkerDrain lifecycle edge");
17
+ }
15
18
  const services = await shared();
16
19
  return {
17
20
  ...services,
@@ -20,6 +23,8 @@ function createTurnActivityServices(
20
23
  createProductionAgentRuntime({
21
24
  metrics: runtimeMetricsHooksForObservability(services.observability),
22
25
  }),
26
+ requestWorkerDrain: dependencies.requestWorkerDrain,
27
+ turnFinalizationTimeoutMs: dependencies.turnFinalizationTimeoutMs,
23
28
  summarizeContextForCompaction:
24
29
  dependencies.summarizeContextForCompaction ?? summarizeForCompaction,
25
30
  };
package/src/activities.ts CHANGED
@@ -69,6 +69,12 @@ export function createActivityTestHarness(dependencies: ActivityDependencies = {
69
69
  createProductionAgentRuntime({
70
70
  metrics: runtimeMetricsHooksForObservability(common.observability),
71
71
  }),
72
+ requestWorkerDrain:
73
+ dependencies.requestWorkerDrain ??
74
+ (() => {
75
+ throw new Error("Test host must provide requestWorkerDrain for stalled cleanup");
76
+ }),
77
+ turnFinalizationTimeoutMs: dependencies.turnFinalizationTimeoutMs,
72
78
  summarizeContextForCompaction:
73
79
  dependencies.summarizeContextForCompaction ?? summarizeForCompaction,
74
80
  documentServices: dependencies.documentServices ?? createDocumentServices(common.settings),
@@ -99,6 +105,8 @@ export const persistSessionAttemptQuiescence =
99
105
  defaultControlActivities.persistSessionAttemptQuiescence;
100
106
  export const reconcileSessionAttemptQuiescence =
101
107
  defaultControlActivities.reconcileSessionAttemptQuiescence;
108
+ export const reconcileSettledSessionAttempt =
109
+ defaultControlActivities.reconcileSettledSessionAttempt;
102
110
  export const recoverDispatch = defaultControlActivities.recoverDispatch;
103
111
  export const recoverEscapedMcpTimeout = defaultControlActivities.recoverEscapedMcpTimeout;
104
112
  export const peekSessionWork = defaultControlActivities.peekSessionWork;
@@ -106,6 +114,7 @@ export const settleSessionInputWait = defaultControlActivities.settleSessionInpu
106
114
  export const expireSessionHumanInput = defaultControlActivities.expireSessionHumanInput;
107
115
  export const expireSessionInteractionIntervention =
108
116
  defaultControlActivities.expireSessionInteractionIntervention;
117
+ export const expireScheduledRunHumanWait = defaultControlActivities.expireScheduledRunHumanWait;
109
118
  export const markSessionIdle = defaultControlActivities.markSessionIdle;
110
119
  export const dispatchScheduledTaskRun = defaultControlActivities.dispatchScheduledTaskRun;
111
120
  export const dispatchAutomationRun = defaultControlActivities.dispatchAutomationRun;
@@ -116,6 +125,9 @@ export const maybeContinueGoal = defaultControlActivities.maybeContinueGoal;
116
125
  export const getCodexCapacityWait = defaultControlActivities.getCodexCapacityWait;
117
126
  export const reconcileCodexCapacityWait = defaultControlActivities.reconcileCodexCapacityWait;
118
127
  export const prepareSandboxLeaseSweep = defaultControlActivities.prepareSandboxLeaseSweep;
128
+ export const listDueBrowserCheckpoints = defaultControlActivities.listDueBrowserCheckpoints;
129
+ export const checkpointBrowserBeforeDeadline =
130
+ defaultControlActivities.checkpointBrowserBeforeDeadline;
119
131
  export const drainSandboxLease = defaultControlActivities.drainSandboxLease;
120
132
  export const maintainSandboxLeaseSweep = defaultControlActivities.maintainSandboxLeaseSweep;
121
133
  export const reapSandboxLeases = defaultControlActivities.reapSandboxLeases;
@@ -124,6 +136,7 @@ export const recoverVideoGenerationWorkflows =
124
136
  defaultControlActivities.recoverVideoGenerationWorkflows;
125
137
  export const maintainRetainedScreenshots = defaultControlActivities.maintainRetainedScreenshots;
126
138
  export const maintainBrowserStateArtifacts = defaultControlActivities.maintainBrowserStateArtifacts;
139
+ export const reconcileRecentModelCallFacts = defaultControlActivities.reconcileRecentModelCallFacts;
127
140
  export const maintainSiteAuthConnections = defaultControlActivities.maintainSiteAuthConnections;
128
141
  export const dispatchSessionWorkflowWakes = defaultControlActivities.dispatchSessionWorkflowWakes;
129
142
  export const verifyRigChange = defaultControlActivities.verifyRigChange;
@@ -1,11 +1,20 @@
1
- import { dbSearchPath, getSettings, resolveNatsControlPlaneAuth } from "@opengeni/config";
2
- import { configureChildLifecycleNotices, createDb } from "@opengeni/db";
1
+ import {
2
+ codeSearchDeploymentPolicy,
3
+ dbSearchPath,
4
+ getSettings,
5
+ resolveNatsControlPlaneAuth,
6
+ } from "@opengeni/config";
7
+ import {
8
+ configureChildLifecycleNotices,
9
+ configureCodeSearchDeploymentPolicy,
10
+ createDb,
11
+ } from "@opengeni/db";
3
12
  import { createNatsEventBus } from "@opengeni/events";
4
13
  import { createObservability } from "@opengeni/observability";
5
14
  import { resolveCatalogSettings } from "@opengeni/core";
6
15
  import { createObjectStorage } from "@opengeni/storage";
7
16
  import type { ActivityDependencies, SharedActivityServices } from "./activities/types";
8
- import { observabilityEventLogger } from "./observability-metrics";
17
+ import { observabilityEventBusOptions } from "./observability-metrics";
9
18
  import { buildPersonalGitHubGitCredentials } from "./personal-github-git-credentials";
10
19
  import { createStandaloneConnectionCredentialsPort } from "./pr-review-credentials";
11
20
 
@@ -40,6 +49,9 @@ export function createSharedActivityServices(
40
49
  // Child lifecycle notice producers are process-global in @opengeni/db;
41
50
  // install the boot-validated rollout flag once for this worker.
42
51
  configureChildLifecycleNotices({ enabled: settings.childLifecycleNoticesEnabled });
52
+ // Child, scheduled and automation sessions created here freeze their
53
+ // code_search decision from the same deployment policy as the API.
54
+ configureCodeSearchDeploymentPolicy(codeSearchDeploymentPolicy(settings));
43
55
  return {
44
56
  settings: resolvedSettings,
45
57
  catalogSourceSettings: settings,
@@ -51,7 +63,7 @@ export function createSharedActivityServices(
51
63
  controlPlaneAuth
52
64
  ? { user: controlPlaneAuth.user, pass: controlPlaneAuth.password }
53
65
  : undefined,
54
- { logger: observabilityEventLogger(observability) },
66
+ observabilityEventBusOptions(observability),
55
67
  )),
56
68
  objectStorage: dependencies.objectStorage ?? createObjectStorage(settings),
57
69
  observability,
@@ -0,0 +1,5 @@
1
+ export const BROWSER_DEADLINE_CHECKPOINT_SWEEP_ID = "opengeni-browser-deadline-checkpoints-v1";
2
+ const QUEUE_SUFFIX = "-browser-checkpoint-v1";
3
+ export function browserDeadlineCheckpointTaskQueue(base: string): string {
4
+ return base.endsWith(QUEUE_SUFFIX) ? base : `${base}${QUEUE_SUFFIX}`;
5
+ }
@@ -161,6 +161,9 @@ export async function connectEditableArtifactHintBroker(
161
161
  name,
162
162
  reconnect: true,
163
163
  maxReconnectAttempts: -1,
164
+ // Same policy as the event bus: a broker or auth-callout restart must not
165
+ // permanently close this connection after two identical auth errors.
166
+ ignoreAuthErrorAbort: true,
164
167
  reconnectTimeWait: 2_000,
165
168
  reconnectJitter: 1_000,
166
169
  reconnectJitterTLS: 1_000,
@@ -1,4 +1,8 @@
1
- import { dbSearchPath, getSettings, type Settings } from "@opengeni/config";
1
+ import {
2
+ dbSearchPath,
3
+ getArtifactMaterializerSettings,
4
+ type ArtifactMaterializerSettings,
5
+ } from "@opengeni/config";
2
6
  import {
3
7
  createDb,
4
8
  dbSql,
@@ -77,7 +81,7 @@ export type CreateEditableArtifactMaterializerServiceInput = Readonly<{
77
81
  }>;
78
82
 
79
83
  export type ProductionEditableArtifactMaterializerServiceOptions = Readonly<{
80
- settings: Settings;
84
+ settings: ArtifactMaterializerSettings;
81
85
  /** Dedicated DSN; never defaults to settings.databaseUrl. */
82
86
  databaseUrl: string;
83
87
  declaredDatabaseRole: string;
@@ -671,7 +675,7 @@ export async function createMaterializerSidecarFromEnvironment(
671
675
  }> | null> {
672
676
  const sidecar = readEditableArtifactMaterializerSidecarEnvironment(environment);
673
677
  if (!sidecar) return null;
674
- const settings = getSettings();
678
+ const settings = getArtifactMaterializerSettings(environment);
675
679
  const service = await createProductionEditableArtifactMaterializerService({
676
680
  settings,
677
681
  databaseUrl: sidecar.databaseUrl,
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  dbSearchPath,
3
- getSettings,
3
+ getArtifactOutboxSettings,
4
4
  resolveNatsControlPlaneAuth,
5
- type Settings,
5
+ type ArtifactOutboxSettings,
6
6
  } from "@opengeni/config";
7
7
  import type { EditableArtifactOutboxDispatchSummary } from "@opengeni/core";
8
8
  import { createObservability, type Observability } from "@opengeni/observability";
@@ -51,7 +51,7 @@ export type CreateEditableArtifactOutboxServiceInput = Readonly<{
51
51
  }>;
52
52
 
53
53
  export type ProductionEditableArtifactOutboxServiceOptions = Readonly<{
54
- settings: Settings;
54
+ settings: ArtifactOutboxSettings;
55
55
  databaseUrl: string;
56
56
  declaredDatabaseRole: string;
57
57
  natsUrl: string;
@@ -360,7 +360,7 @@ export async function createOutboxSidecarFromEnvironment(
360
360
  ): Promise<Readonly<{ service: EditableArtifactOutboxService; httpPort: number }> | null> {
361
361
  const sidecar = readEditableArtifactOutboxSidecarEnvironment(environment);
362
362
  if (!sidecar) return null;
363
- const settings = getSettings();
363
+ const settings = getArtifactOutboxSettings(environment);
364
364
  const controlPlaneAuth = resolveNatsControlPlaneAuth(settings);
365
365
  const natsAuth: EditableArtifactHintNatsAuth = controlPlaneAuth
366
366
  ? {
@@ -113,7 +113,7 @@ export async function buildGitHubRestMcpForTurn(input: {
113
113
  resolveAuthority: appResolver,
114
114
  }),
115
115
  });
116
- configs.push(internalMcpConfig(GITHUB_REST_MCP_APP_SERVER_ID, "GitHub — OpenGeni bot"));
116
+ configs.push(internalMcpConfig(GITHUB_REST_MCP_APP_SERVER_ID, "GitHub — Opengeni bot"));
117
117
  toolRefs.push({ kind: "mcp", id: GITHUB_REST_MCP_APP_SERVER_ID });
118
118
  connectorBindings.push(
119
119
  ...githubConnectorBindings(GITHUB_REST_MCP_APP_SERVER_ID, appRepositories),
@@ -519,7 +519,6 @@ function exactPersonalDelegation(
519
519
  delegation.providerDomain !== "github.com" ||
520
520
  delegation.kind !== "oauth2" ||
521
521
  !delegation.originWorkspaceId ||
522
- !delegation.userDelegation ||
523
522
  !delegation.personalGitHubRepositorySelection
524
523
  ) {
525
524
  throw new GitHubRestAuthorityError("Personal GitHub delegation is unavailable");
@@ -1,4 +1,4 @@
1
- import type { HostEventSink, HostUsageSink } from "@opengeni/contracts";
1
+ import type { HostEventSink, HostLifecycleFactSink, HostUsageSink } from "@opengeni/contracts";
2
2
  import {
3
3
  acknowledgeHostExportBatch,
4
4
  claimHostExportBatch,
@@ -35,6 +35,8 @@ export type HostExportPumpOptions = {
35
35
  db: Database;
36
36
  eventSink?: HostEventSink;
37
37
  usageSink?: HostUsageSink;
38
+ /** Content-free per-person product lifecycle facts (`lifecycle_fact`). */
39
+ lifecycleSink?: HostLifecycleFactSink;
38
40
  observability?: Observability;
39
41
  instanceId?: string;
40
42
  pollIntervalMs?: number;
@@ -77,7 +79,7 @@ export type HostExportPump = {
77
79
  * keys. Sink implementations must therefore deduplicate those keys.
78
80
  */
79
81
  export function createHostExportPump(options: HostExportPumpOptions): HostExportPump {
80
- if (!options.eventSink && !options.usageSink) {
82
+ if (!options.eventSink && !options.usageSink && !options.lifecycleSink) {
81
83
  throw new Error("createHostExportPump requires at least one host sink");
82
84
  }
83
85
  const instanceId = validateInstanceId(
@@ -102,7 +104,9 @@ export function createHostExportPump(options: HostExportPumpOptions): HostExport
102
104
  const configuredConsumer = (kind: HostExportKind): string | null =>
103
105
  kind === "session_event"
104
106
  ? (options.eventSink?.consumerId ?? null)
105
- : (options.usageSink?.consumerId ?? null);
107
+ : kind === "usage_event"
108
+ ? (options.usageSink?.consumerId ?? null)
109
+ : (options.lifecycleSink?.consumerId ?? null);
106
110
 
107
111
  const recordResult = (result: HostExportDrainResult, durationSeconds: number) => {
108
112
  const labels = { kind: result.kind, outcome: result.outcome };
@@ -242,7 +246,7 @@ export function createHostExportPump(options: HostExportPumpOptions): HostExport
242
246
  ...common,
243
247
  });
244
248
  if (batch) return await deliverClaim(batch.events.length, () => sink.deliverEvents(batch));
245
- } else {
249
+ } else if (kind === "usage_event") {
246
250
  const sink = options.usageSink;
247
251
  if (!sink) return { kind, outcome: "not_configured" };
248
252
  const batch = await claimHostExportBatch(options.db, {
@@ -250,6 +254,16 @@ export function createHostExportPump(options: HostExportPumpOptions): HostExport
250
254
  ...common,
251
255
  });
252
256
  if (batch) return await deliverClaim(batch.events.length, () => sink.deliverUsage(batch));
257
+ } else {
258
+ const sink = options.lifecycleSink;
259
+ if (!sink) return { kind, outcome: "not_configured" };
260
+ const batch = await claimHostExportBatch(options.db, {
261
+ kind,
262
+ ...common,
263
+ });
264
+ if (batch) {
265
+ return await deliverClaim(batch.events.length, () => sink.deliverLifecycleFacts(batch));
266
+ }
253
267
  }
254
268
  } catch (error) {
255
269
  return await settleFailure(error);
@@ -276,6 +290,7 @@ export function createHostExportPump(options: HostExportPumpOptions): HostExport
276
290
  const results: HostExportDrainResult[] = [];
277
291
  if (options.eventSink) results.push(await drainKind("session_event"));
278
292
  if (options.usageSink) results.push(await drainKind("usage_event"));
293
+ if (options.lifecycleSink) results.push(await drainKind("lifecycle_fact"));
279
294
  return results;
280
295
  };
281
296
 
@@ -326,9 +341,16 @@ export function createHostExportPump(options: HostExportPumpOptions): HostExport
326
341
  consumerId: options.usageSink.consumerId,
327
342
  });
328
343
  }
344
+ if (options.lifecycleSink) {
345
+ await registerHostExportConsumer(scoped, {
346
+ kind: "lifecycle_fact",
347
+ consumerId: options.lifecycleSink.consumerId,
348
+ });
349
+ }
329
350
  });
330
351
  if (options.eventSink) retiringKinds.delete("session_event");
331
352
  if (options.usageSink) retiringKinds.delete("usage_event");
353
+ if (options.lifecycleSink) retiringKinds.delete("lifecycle_fact");
332
354
  running = true;
333
355
  loopPromise = (async () => {
334
356
  while (true) {
@@ -16,6 +16,9 @@ export type {
16
16
  HostEventExport,
17
17
  HostEventExportBatch,
18
18
  HostEventSink,
19
+ HostLifecycleFactExport,
20
+ HostLifecycleFactExportBatch,
21
+ HostLifecycleFactSink,
19
22
  HostUsageExport,
20
23
  HostUsageExportBatch,
21
24
  HostUsageSink,
package/src/http.ts CHANGED
@@ -120,6 +120,7 @@ export async function runReadinessChecks(
120
120
  export function dbReadyCheck(
121
121
  db: Database,
122
122
  posture?: RuntimeDatabasePostureOptions,
123
+ afterDatabaseReady?: () => Promise<void>,
123
124
  ): () => Promise<void> {
124
125
  let inFlight: Promise<void> | undefined;
125
126
  return () => {
@@ -130,9 +131,10 @@ export function dbReadyCheck(
130
131
  inFlight = (async () => {
131
132
  if (posture) {
132
133
  await assertRuntimeDatabasePosture(db, posture);
133
- return;
134
+ } else {
135
+ await db.execute(dbSql`select 1`);
134
136
  }
135
- await db.execute(dbSql`select 1`);
137
+ await afterDatabaseReady?.();
136
138
  })().finally(() => {
137
139
  inFlight = undefined;
138
140
  });