@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
@@ -1,3 +1,6 @@
1
+ import { withDirectModelProviders } from "@opengeni/config";
2
+ import { loadDirectModelProviderConnection } from "@opengeni/db";
3
+ import { FILESYSTEM_DISCONTINUITY_PROTOCOL } from "./recovery-warning";
1
4
  import {
2
5
  applySessionTurnSettlement,
3
6
  claimSessionWorkForAttempt,
@@ -14,9 +17,11 @@ import {
14
17
  type SessionTurnForExecution,
15
18
  } from "@opengeni/db";
16
19
  import { appendAndPublishTurnEventsFenced, publishDurableSessionEvents } from "@opengeni/events";
20
+ import { linkCurrentSpanToAdmission, turnExecutionTelemetryKey } from "@opengeni/observability";
17
21
  import { deliverChildRequiresActionToParent } from "../parent-wake";
18
22
  import {
19
23
  assertTurnExecutionPolicyMatchesConfigV1,
24
+ settingsForAcceptedSubscriptionTurn,
20
25
  resolveTurnExecutionPolicyV1,
21
26
  type Settings,
22
27
  } from "@opengeni/config";
@@ -47,18 +52,22 @@ import {
47
52
  recordSessionEventAppendPhase,
48
53
  recordSessionEventPublishLatency,
49
54
  recordTurnStartupPhase,
55
+ measureTurnStartupPhase,
50
56
  recordTurnStartupMilestone,
51
57
  turnLifecycleMetricsFor,
52
58
  } from "../../observability-metrics";
53
59
  import { createTurnCredentialLeases } from "./credential-leases";
54
60
  import { createTurnMediaArtifacts } from "./media-artifacts";
55
61
  import { readTurnExecutionPolicyV1 } from "@opengeni/contracts";
62
+ import { turnCredentialRestriction } from "./credential-restriction";
56
63
 
57
64
  import {
58
65
  credentialSubjectIdForTurnInitiator,
59
66
  turnExecutionPolicyBillingIdentity,
60
67
  legacyTurnExecutionPolicyInput,
61
68
  ensureRunAllowed,
69
+ AllowanceExhaustedError,
70
+ type AllowanceRefusal,
62
71
  } from "./admission";
63
72
  import { providerRecoveryCountFromMetadata, isWorkerShutdownCancellation } from "./errors";
64
73
  import { throwIfTurnOperationCancelled, waitForTurnOperation } from "./sandbox-provision";
@@ -178,8 +187,17 @@ export async function claimTurnAttempt(deps: ClaimTurnDeps): Promise<ClaimTurnOu
178
187
  acknowledgeLostAttemptOwnership,
179
188
  } = deps;
180
189
 
181
- const deploymentCatalogSettings = (await resolveCatalogSettings(db, catalogSourceSettings))
182
- .settings;
190
+ const deploymentCatalogSettings = (
191
+ await measureTurnStartupPhase(
192
+ observability,
193
+ {
194
+ phase: "claim_catalog_read",
195
+ provider: "unresolved",
196
+ backend: "unresolved",
197
+ },
198
+ () => resolveCatalogSettings(db, catalogSourceSettings),
199
+ )
200
+ ).settings;
183
201
 
184
202
  const validatePendingSystemUpdateAuthority: NonNullable<
185
203
  ClaimSessionWorkForAttemptInput["validatePendingSystemUpdateAuthority"]
@@ -191,15 +209,25 @@ export async function claimTurnAttempt(deps: ClaimTurnDeps): Promise<ClaimTurnOu
191
209
  sessionId: input.sessionId,
192
210
  update,
193
211
  });
194
- const claim = await claimSessionWorkForAttempt(db, input.workspaceId, {
195
- sessionId: input.sessionId,
196
- workflowId: input.workflowId,
197
- workflowRunId: input.workflowRunId,
198
- attemptId: input.attemptId,
199
- dispatchId,
200
- trigger: input.trigger,
201
- validatePendingSystemUpdateAuthority,
202
- });
212
+ const claim = await measureTurnStartupPhase(
213
+ observability,
214
+ {
215
+ phase: "claim_atomic",
216
+ provider: "unresolved",
217
+ backend: "unresolved",
218
+ },
219
+ () =>
220
+ claimSessionWorkForAttempt(db, input.workspaceId, {
221
+ filesystemDiscontinuityProtocol: FILESYSTEM_DISCONTINUITY_PROTOCOL,
222
+ sessionId: input.sessionId,
223
+ workflowId: input.workflowId,
224
+ workflowRunId: input.workflowRunId,
225
+ attemptId: input.attemptId,
226
+ dispatchId,
227
+ trigger: input.trigger,
228
+ validatePendingSystemUpdateAuthority,
229
+ }),
230
+ );
203
231
  if (claim.action === "unclaimed") {
204
232
  control.activityStatus = "unclaimed";
205
233
  return { exit: { status: "unclaimed", reason: claim.reason } };
@@ -209,6 +237,21 @@ export async function claimTurnAttempt(deps: ClaimTurnDeps): Promise<ClaimTurnOu
209
237
  attempt.dispatchId = dispatchId;
210
238
  attempt.executionGeneration = turn.executionGeneration;
211
239
  attempt.providerRecoveryCount = providerRecoveryCountFromMetadata(turn.metadata);
240
+ const authRecovery = turn.metadata?.claudeAuthRecovery;
241
+ attempt.claudeAuthRecovery =
242
+ authRecovery &&
243
+ typeof authRecovery === "object" &&
244
+ "credentialId" in authRecovery &&
245
+ typeof authRecovery.credentialId === "string" &&
246
+ "credentialVersion" in authRecovery &&
247
+ typeof authRecovery.credentialVersion === "number" &&
248
+ Number.isSafeInteger(authRecovery.credentialVersion) &&
249
+ authRecovery.credentialVersion > 0
250
+ ? {
251
+ credentialId: authRecovery.credentialId,
252
+ credentialVersion: authRecovery.credentialVersion,
253
+ }
254
+ : undefined;
212
255
  attempt.triggerEventId = turn.triggerEventId;
213
256
  // The durable attempt UUID is stable for a Temporal retry of this activity
214
257
  // input and freshly generated for worker-death redispatch/continue-as-new.
@@ -223,25 +266,42 @@ export async function claimTurnAttempt(deps: ClaimTurnDeps): Promise<ClaimTurnOu
223
266
  // Therefore every failure with no turnId came from the one atomic claim
224
267
  // transaction and can be classified without conflating ordinary runtime
225
268
  // or transport failures with admission failures.
226
- const session = await requireSession(db, input.workspaceId, input.sessionId);
227
269
  let installedApiIntegrations: readonly ApiIntegrationRuntime[] = [];
228
270
  const credentialSubjectId = credentialSubjectIdForTurnInitiator(turn);
229
271
  const fileAuthoritySubjectId = turn.initiatingHumanSubjectId ?? null;
230
- const mcpSettings = await settingsWithEnabledCapabilityMcpServers(
231
- db,
232
- input.workspaceId,
233
- deploymentCatalogSettings,
234
- {
235
- ...(credentialSubjectId
236
- ? { subjectId: credentialSubjectId }
237
- : {
238
- personalConnectionDelegations: turn.personalConnectionDelegations,
239
- }),
240
- onResolvedApiIntegrations: (integrations) => {
241
- installedApiIntegrations = integrations;
272
+ // Both are fresh scoped reads on the root pool after exact claim ownership.
273
+ // Neither consumes the other's result; retain the capability helper's own
274
+ // subject/delegation authority and await both before credential/policy gates.
275
+ const [session, mcpSettings] = await Promise.all([
276
+ measureTurnStartupPhase(
277
+ observability,
278
+ {
279
+ phase: "claim_session_read",
280
+ provider: "unresolved",
281
+ backend: turn.sandboxBackend,
242
282
  },
243
- },
244
- );
283
+ () => requireSession(db, input.workspaceId, input.sessionId),
284
+ ),
285
+ measureTurnStartupPhase(
286
+ observability,
287
+ {
288
+ phase: "claim_capability_settings",
289
+ provider: "unresolved",
290
+ backend: turn.sandboxBackend,
291
+ },
292
+ () =>
293
+ settingsWithEnabledCapabilityMcpServers(db, input.workspaceId, deploymentCatalogSettings, {
294
+ ...(credentialSubjectId
295
+ ? { subjectId: credentialSubjectId }
296
+ : {
297
+ personalConnectionDelegations: turn.personalConnectionDelegations,
298
+ }),
299
+ onResolvedApiIntegrations: (integrations) => {
300
+ installedApiIntegrations = integrations;
301
+ },
302
+ }),
303
+ ),
304
+ ]);
245
305
  // Read the active-credential flag once for the runtime capability overlay.
246
306
  // Accepted billing/provider identity comes from the turn policy below,
247
307
  // never from this mutable health snapshot.
@@ -249,6 +309,7 @@ export async function claimTurnAttempt(deps: ClaimTurnDeps): Promise<ClaimTurnOu
249
309
  db,
250
310
  mcpSettings,
251
311
  input.workspaceId,
312
+ turn.id,
252
313
  );
253
314
  const codexSettings = await settingsWithCodexCredential(
254
315
  db,
@@ -273,21 +334,42 @@ export async function claimTurnAttempt(deps: ClaimTurnDeps): Promise<ClaimTurnOu
273
334
  gatewaySettings,
274
335
  claimedPolicy.kind === "valid" ? claimedPolicy.policy.productModelId : turn.model,
275
336
  );
276
- const capabilitySettings = await settingsWithOrganizationProviderCredentials(
337
+ let capabilitySettings = await settingsWithOrganizationProviderCredentials(
277
338
  db,
278
339
  input.accountId,
279
340
  input.workspaceId,
280
341
  workspaceProviderSettings,
281
342
  claimedPolicy.kind === "valid" ? claimedPolicy.policy.productModelId : turn.model,
282
343
  );
344
+ const selectedDirectConnection = await loadDirectModelProviderConnection(
345
+ db,
346
+ capabilitySettings,
347
+ input.workspaceId,
348
+ claimedPolicy.kind === "valid" ? claimedPolicy.policy.productModelId : (turn.model ?? ""),
349
+ );
350
+ // Execution only needs the selected customer connection. Ordinary turns
351
+ // must not load or install unrelated workspace provider configurations.
352
+ if (selectedDirectConnection) {
353
+ capabilitySettings = withDirectModelProviders(capabilitySettings, [selectedDirectConnection]);
354
+ }
283
355
  const codexAppsCredentialId = capabilitySettings.codexConnectedAppsEnabled
284
356
  ? await resolveCodexAppsCredentialIdForRun(db, input.workspaceId)
285
357
  : null;
286
- runtime.configure(capabilitySettings);
287
- const policyForAbsent =
358
+ const candidatePolicy =
288
359
  claimedPolicy.kind === "valid"
289
360
  ? claimedPolicy.policy
290
361
  : resolveTurnExecutionPolicyV1(capabilitySettings, legacyTurnExecutionPolicyInput(turn));
362
+ // This context is frozen by the accepted-turn writer from the exact source
363
+ // turn. Its reserved restriction field cannot come from public service JSON.
364
+ const credentialRestriction =
365
+ claimedPolicy.kind === "absent"
366
+ ? turn.initiatorContext?.credentialRestriction === "developer_setup"
367
+ ? "developer_setup"
368
+ : turnCredentialRestriction(candidatePolicy, session.metadata)
369
+ : undefined;
370
+ const policyForAbsent = credentialRestriction
371
+ ? { ...candidatePolicy, credentialRestriction }
372
+ : candidatePolicy;
291
373
  const installedPolicy = await installOrReadTurnExecutionPolicyForAttempt(db, {
292
374
  accountId: input.accountId,
293
375
  workspaceId: input.workspaceId,
@@ -300,6 +382,16 @@ export async function claimTurnAttempt(deps: ClaimTurnDeps): Promise<ClaimTurnOu
300
382
  if (!installedPolicy.accepted) {
301
383
  throw new TurnAttemptFencedError(`turn execution policy was fenced: ${installedPolicy.reason}`);
302
384
  }
385
+ capabilitySettings = settingsForAcceptedSubscriptionTurn(
386
+ capabilitySettings,
387
+ installedPolicy.policy,
388
+ {
389
+ modelId: turn.model,
390
+ reasoningEffort: turn.reasoningEffort,
391
+ latencyMode: turn.latencyMode,
392
+ },
393
+ );
394
+ runtime.configure(capabilitySettings);
303
395
  const verifiedExecutionPolicy = assertTurnExecutionPolicyMatchesConfigV1(
304
396
  capabilitySettings,
305
397
  installedPolicy.policy,
@@ -317,10 +409,14 @@ export async function claimTurnAttempt(deps: ClaimTurnDeps): Promise<ClaimTurnOu
317
409
  billingState.countsTowardTokenCap = billingIdentity.countsTowardTokenCap;
318
410
  billingState.isCodexTurn = billingIdentity.codexSubscription;
319
411
  billingState.isXaiTurn = billingIdentity.xaiSubscription;
412
+ billingState.isClaudeTurn =
413
+ verifiedExecutionPolicy.provider.kind === "claude-subscription-workspace" ||
414
+ verifiedExecutionPolicy.provider.kind === "claude-subscription-organization";
320
415
  const trigger = await getSessionEvent(db, input.workspaceId, attempt.triggerEventId);
321
416
  if (!trigger) {
322
417
  throw new Error(`Trigger event not found: ${attempt.triggerEventId}`);
323
418
  }
419
+ if (trigger.type === "user.message") linkCurrentSpanToAdmission(trigger.id);
324
420
  const humanInputResume = await getHumanInputResumeForEvent(
325
421
  db,
326
422
  input.workspaceId,
@@ -336,6 +432,7 @@ export async function claimTurnAttempt(deps: ClaimTurnDeps): Promise<ClaimTurnOu
336
432
  attempt.triggerType = trigger.type;
337
433
  const attachPendingUpdatesAfterOpenSuffix = async (): Promise<boolean> => {
338
434
  const attached = await claimSessionWorkForAttempt(db, input.workspaceId, {
435
+ filesystemDiscontinuityProtocol: FILESYSTEM_DISCONTINUITY_PROTOCOL,
339
436
  sessionId: input.sessionId,
340
437
  workflowId: input.workflowId,
341
438
  workflowRunId: input.workflowRunId,
@@ -352,20 +449,27 @@ export async function claimTurnAttempt(deps: ClaimTurnDeps): Promise<ClaimTurnOu
352
449
  // bypass OpenGeni credit/token gates)
353
450
  // AND the optional host `entitlements` port (when bound, its admitRun replaces
354
451
  // the local credit read). Unset port → today's local-ledger path.
355
- await waitForTurnOperation(
356
- ensureRunAllowed(
357
- capabilitySettings,
358
- db,
359
- input.accountId,
360
- input.workspaceId,
361
- billingState.isExternallyBilledTurn,
362
- entitlements,
363
- billingState.chargesOpenGeniCredits,
364
- billingState.countsTowardTokenCap,
365
- ),
366
- cancellationSignal,
367
- undefined,
368
- );
452
+ let allowanceRefusal: AllowanceRefusal | null = null;
453
+ try {
454
+ await waitForTurnOperation(
455
+ ensureRunAllowed(
456
+ capabilitySettings,
457
+ db,
458
+ input.accountId,
459
+ input.workspaceId,
460
+ billingState.isExternallyBilledTurn,
461
+ entitlements,
462
+ billingState.chargesOpenGeniCredits,
463
+ billingState.countsTowardTokenCap,
464
+ turn.initiatingHumanSubjectId,
465
+ ),
466
+ cancellationSignal,
467
+ undefined,
468
+ );
469
+ } catch (error) {
470
+ if (!(error instanceof AllowanceExhaustedError)) throw error;
471
+ allowanceRefusal = error.refusal;
472
+ }
369
473
  // Setup (variableSet load, MCP connects, sandbox restore) does not
370
474
  // stream and so never observes cancellation on its own; these explicit
371
475
  // checks let a graceful shutdown checkpoint the turn before the worker is
@@ -387,6 +491,7 @@ export async function claimTurnAttempt(deps: ClaimTurnDeps): Promise<ClaimTurnOu
387
491
  turnId: attempt.turnId,
388
492
  opAcks: {},
389
493
  };
494
+ eventing.heartbeatDetails = heartbeatDetails;
390
495
  const opJournal = makeTurnOpJournal(activityContext, heartbeatDetails);
391
496
  eventing.heartbeatTimer = startActivityHeartbeat(activityContext, heartbeatDetails);
392
497
  let producerSeq = 0;
@@ -497,6 +602,9 @@ export async function claimTurnAttempt(deps: ClaimTurnDeps): Promise<ClaimTurnOu
497
602
  ...(inputSettlement.suppressGoalContinuation !== undefined
498
603
  ? { suppressGoalContinuation: inputSettlement.suppressGoalContinuation }
499
604
  : {}),
605
+ ...(inputSettlement.allowanceGoalPause
606
+ ? { allowanceGoalPause: inputSettlement.allowanceGoalPause }
607
+ : {}),
500
608
  events: inputs,
501
609
  ...(runState ? { runState } : {}),
502
610
  ...(compactionRequestFailure ? { compactionRequestFailure } : {}),
@@ -547,12 +655,47 @@ export async function claimTurnAttempt(deps: ClaimTurnDeps): Promise<ClaimTurnOu
547
655
  throwIfTurnCancelled();
548
656
  recordTurnStartupPhase(observability, {
549
657
  phase: "claim_and_policy",
658
+ executionCorrelationId: turnExecutionTelemetryKey(
659
+ input.workspaceId,
660
+ input.sessionId,
661
+ input.attemptId,
662
+ ),
550
663
  provider: turnExecutionPolicy.providerId,
551
664
  backend: turn.sandboxBackend,
552
665
  outcome: "completed",
553
666
  durationSeconds: (performance.now() - activityStarted) / 1_000,
554
667
  });
555
668
  const turnStartSettlementStartedAt = performance.now();
669
+ if (allowanceRefusal) {
670
+ // Claim has frozen the exact human, but no provider or sandbox has started.
671
+ // Use the same terminal valve as a post-response stop, retaining a usable
672
+ // session and a visible typed refusal instead of manufacturing a failure.
673
+ if (
674
+ !(await eventing.settle({
675
+ events: [
676
+ { type: "usage.exhausted", payload: allowanceRefusal },
677
+ {
678
+ type: "turn.completed",
679
+ payload: {
680
+ output: "",
681
+ segmentLimit: "budget_exhausted",
682
+ ...allowanceRefusal,
683
+ },
684
+ },
685
+ { type: "session.status.changed", payload: { status: "idle" } },
686
+ ],
687
+ turnStatus: "completed",
688
+ sessionStatus: "idle",
689
+ activeTurnId: null,
690
+ allowanceGoalPause: { rationale: allowanceRefusal.message },
691
+ }))
692
+ ) {
693
+ return { exit: claimedResult({ status: "cancelled" }) };
694
+ }
695
+ control.turnMetricOutcome = "completed";
696
+ control.activityStatus = "idle";
697
+ return { exit: claimedResult({ status: "idle" }) };
698
+ }
556
699
  if (
557
700
  !(await eventing.settle({
558
701
  events: [
@@ -0,0 +1,221 @@
1
+ import { createHash } from "node:crypto";
2
+ import {
3
+ emptyClaudeUsage,
4
+ mergeClaudeUsage,
5
+ parseClaudeUsageHeaders,
6
+ type parseModelProvidersJson,
7
+ type ClaudeUsageObservation,
8
+ } from "@opengeni/config";
9
+
10
+ type Scope = "workspace" | "organization";
11
+ type ClaudeRequestCredential = {
12
+ token: string;
13
+ connectionId: string;
14
+ credentialVersion: number;
15
+ };
16
+ export class ClaudeSubscriptionConnectionUnavailable extends Error {
17
+ readonly status = 409;
18
+ readonly code = "claude_subscription_connection_changed";
19
+ constructor() {
20
+ super(
21
+ "Claude connection changed or was disconnected. Start a new turn with the current connection.",
22
+ );
23
+ }
24
+ }
25
+ export type CapturedClaudeUsage = {
26
+ scope: Scope;
27
+ token: string;
28
+ expectedConnectionId: string;
29
+ expectedCredentialVersion: number;
30
+ observation?: ClaudeUsageObservation;
31
+ responseStatus?: number;
32
+ upstreamModelId?: string;
33
+ requestId?: string;
34
+ refresh?: { status: "reconnect"; checkedAt: string };
35
+ };
36
+
37
+ /** Capture the exact credential before requests; replacement fences late responses. */
38
+ export async function createClaudeUsageObserver(
39
+ providers: ReturnType<typeof parseModelProvidersJson>,
40
+ latest: Map<string, CapturedClaudeUsage>,
41
+ readCredential: (scope: Scope) => Promise<{
42
+ token: string;
43
+ connectionId: string;
44
+ credentialVersion: number;
45
+ } | null>,
46
+ ) {
47
+ const managedProviderIds = new Set(
48
+ providers
49
+ .filter(
50
+ (provider) =>
51
+ provider.kind === "claude-subscription-workspace" ||
52
+ provider.kind === "claude-subscription-organization",
53
+ )
54
+ .map((provider) => provider.id),
55
+ );
56
+ const bindings = await Promise.all(
57
+ providers
58
+ .filter(
59
+ (provider) =>
60
+ provider.kind === "claude-subscription-workspace" ||
61
+ provider.kind === "claude-subscription-organization",
62
+ )
63
+ .map(async (provider) => {
64
+ const scope: Scope =
65
+ provider.kind === "claude-subscription-workspace" ? "workspace" : "organization";
66
+ const binding = provider.anthropic?.credentialBinding;
67
+ if (binding && provider.apiKey)
68
+ return [
69
+ provider.id,
70
+ {
71
+ scope,
72
+ token: provider.apiKey,
73
+ expectedConnectionId: binding.connectionId,
74
+ expectedCredentialVersion: binding.credentialVersion,
75
+ },
76
+ ] as const;
77
+ const credential = await readCredential(scope).catch(() => null);
78
+ if (!credential || credential.token !== provider.apiKey) return null;
79
+ return [
80
+ provider.id,
81
+ {
82
+ scope,
83
+ token: credential.token,
84
+ expectedConnectionId: credential.connectionId,
85
+ expectedCredentialVersion: credential.credentialVersion,
86
+ },
87
+ ] as const;
88
+ }),
89
+ );
90
+ const captured = new Map(bindings.filter((binding) => binding !== null));
91
+ let anonymousReceipt = 0;
92
+ const observeBinding = (
93
+ binding:
94
+ | Pick<
95
+ CapturedClaudeUsage,
96
+ "scope" | "token" | "expectedConnectionId" | "expectedCredentialVersion"
97
+ >
98
+ | undefined,
99
+ response: Response,
100
+ upstreamModelId?: string,
101
+ requestToken?: string | null,
102
+ ) => {
103
+ if (requestToken === null) return;
104
+ if (!binding) return;
105
+ const { scope, ...capturedIdentity } = binding;
106
+ const identity = { ...capturedIdentity, token: requestToken ?? capturedIdentity.token };
107
+ // Same-generation OAuth renewal can overlap an older request. Never attach
108
+ // its authentication failure to the newly renewed token or merge the two.
109
+ const tokenKey = createHash("sha256").update(identity.token).digest("hex");
110
+ const rawRequestId = response.headers.get("request-id");
111
+ const requestId = rawRequestId && rawRequestId.length <= 256 ? rawRequestId : undefined;
112
+ // Retain exact physical responses, including HTTP 200 streams which can
113
+ // terminate with typed authentication/rate-limit errors. Parallel titles
114
+ // must never overwrite the serving request receipt.
115
+ const receiptKey =
116
+ requestId ??
117
+ (response.status === 401 || response.status === 429
118
+ ? `anonymous-${++anonymousReceipt}`
119
+ : "aggregate");
120
+ const captureKey = `${receiptKey}:${scope}:${identity.expectedConnectionId}:${identity.expectedCredentialVersion}:${tokenKey}:${upstreamModelId ?? "unknown"}`;
121
+ const previous = latest.get(captureKey);
122
+ let observation = parseClaudeUsageHeaders(response.headers, new Date(), upstreamModelId);
123
+ if (observation && previous?.observation) {
124
+ const merged = mergeClaudeUsage(
125
+ mergeClaudeUsage(emptyClaudeUsage(binding.expectedCredentialVersion), previous.observation),
126
+ observation,
127
+ );
128
+ observation = {
129
+ windows: merged.windows,
130
+ observedAt: merged.observedAt!,
131
+ source: merged.source!,
132
+ requestStatus: merged.requestStatus ?? null,
133
+ requestRestrictions: merged.requestRestrictions ?? [],
134
+ };
135
+ }
136
+ if (requestId || observation || response.status === 401 || response.status === 429)
137
+ latest.set(captureKey, {
138
+ scope,
139
+ responseStatus: response.status,
140
+ ...(response.headers.get("request-id") && response.headers.get("request-id")!.length <= 256
141
+ ? { requestId: response.headers.get("request-id")! }
142
+ : {}),
143
+ ...(upstreamModelId ? { upstreamModelId } : {}),
144
+ ...identity,
145
+ ...(previous?.observation && !observation ? { observation: previous.observation } : {}),
146
+ ...(observation ? { observation } : {}),
147
+ ...(response.status === 401
148
+ ? { refresh: { status: "reconnect", checkedAt: new Date().toISOString() } }
149
+ : {}),
150
+ });
151
+ };
152
+ const observe = (
153
+ providerId: string,
154
+ response: Response,
155
+ upstreamModelId?: string,
156
+ requestToken?: string | null,
157
+ ) => observeBinding(captured.get(providerId), response, upstreamModelId, requestToken);
158
+ const prepareRequestWithObserver = async (
159
+ providerId: string,
160
+ headers: Headers,
161
+ resolve: (binding: {
162
+ scope: Scope;
163
+ expectedConnectionId: string;
164
+ expectedCredentialVersion: number;
165
+ }) => Promise<ClaudeRequestCredential | null>,
166
+ ) => {
167
+ if (!managedProviderIds.has(providerId)) return { headers, observe };
168
+ const binding = captured.get(providerId);
169
+ if (!binding) throw new ClaudeSubscriptionConnectionUnavailable();
170
+ const credential = await resolve({
171
+ scope: binding.scope,
172
+ expectedConnectionId: binding.expectedConnectionId,
173
+ expectedCredentialVersion: binding.expectedCredentialVersion,
174
+ });
175
+ if (
176
+ !credential ||
177
+ credential.connectionId !== binding.expectedConnectionId ||
178
+ credential.credentialVersion !== binding.expectedCredentialVersion
179
+ )
180
+ throw new ClaudeSubscriptionConnectionUnavailable();
181
+ const dispatched = Object.freeze({ ...binding, token: credential.token });
182
+ captured.set(providerId, dispatched);
183
+ headers.set("authorization", `Bearer ${credential.token}`);
184
+ headers.delete("x-api-key");
185
+ return {
186
+ headers,
187
+ observe: (
188
+ _providerId: string,
189
+ response: Response,
190
+ upstreamModelId?: string,
191
+ requestToken?: string | null,
192
+ ) => observeBinding(dispatched, response, upstreamModelId, requestToken),
193
+ };
194
+ };
195
+ return Object.assign(observe, {
196
+ prepareRequestWithObserver,
197
+ async prepareRequest(...args: Parameters<typeof prepareRequestWithObserver>) {
198
+ return (await prepareRequestWithObserver(...args)).headers;
199
+ },
200
+ binding(providerId: string) {
201
+ const binding = captured.get(providerId);
202
+ return binding ? { ...binding } : undefined;
203
+ },
204
+ renew(
205
+ providerId: string,
206
+ credential: {
207
+ token: string;
208
+ connectionId: string;
209
+ credentialVersion: number;
210
+ },
211
+ ) {
212
+ const binding = captured.get(providerId);
213
+ if (
214
+ binding &&
215
+ binding.expectedConnectionId === credential.connectionId &&
216
+ binding.expectedCredentialVersion === credential.credentialVersion
217
+ )
218
+ captured.set(providerId, { ...binding, token: credential.token });
219
+ },
220
+ });
221
+ }