@opengeni/worker-bundle 1.0.0-canary.1 → 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,16 +1,20 @@
1
1
  import {
2
2
  CODEX_CREDENTIAL_LEASE_TTL_MS,
3
3
  XAI_CREDENTIAL_LEASE_TTL_MS,
4
+ CLAUDE_CREDENTIAL_LEASE_TTL_MS,
5
+ heartbeatClaudeCredentialLeaseUntil,
4
6
  heartbeatCodexCredentialLeaseUntil,
5
7
  heartbeatXaiCredentialLeaseUntil,
6
8
  } from "@opengeni/db";
7
9
  import type { SharedActivityServices } from "../types";
8
- import { codexCredentialLeaseDeadlineExpired } from "./codex";
10
+ import {
11
+ SubscriptionTurnLease,
12
+ type LeaseRenewReason,
13
+ type LeaseLossReason,
14
+ } from "./subscription-lease";
15
+ export type { LeaseRenewReason, LeaseLossReason } from "./subscription-lease";
9
16
  import { safeErrorDiagnostic } from "./errors";
10
17
 
11
- export type LeaseRenewReason = "timer" | "runtime_event" | "model_usage";
12
- export type LeaseLossReason = "deadline" | "not_found";
13
-
14
18
  export class CodexCredentialLeaseLostError extends Error {
15
19
  readonly code = "codex_credential_lease_lost";
16
20
 
@@ -34,229 +38,128 @@ export type TurnCredentialLeaseDeps = {
34
38
  * cross-replica fairness primitive; the heartbeat here only extends its short
35
39
  * TTL. A killed worker stops heartbeating and the holder self-expires.
36
40
  */
37
- export class CodexTurnLease {
38
- held = false;
39
- lost = false;
40
- lossReason: LeaseLossReason | null = null;
41
- holderId: string | null = null;
42
- generation: number | null = null;
43
- // Monotonic worker deadline, not a comparison between the Postgres and
44
- // worker wall clocks. It is advanced only after a database renewal confirms,
45
- // from the request START + TTL; slow queries therefore shorten (never extend)
46
- // the conservative ownership window.
47
- confirmedUntilMs: number | null = null;
48
- private heartbeatInFlight = false;
49
- private heartbeatTimer: ReturnType<typeof setInterval> | undefined;
50
-
51
- constructor(private readonly deps: TurnCredentialLeaseDeps) {}
52
-
53
- markLost = (reason: LeaseLossReason): void => {
54
- if (this.lost) return;
55
- this.lost = true;
56
- this.lossReason = reason;
57
- this.deps.observability.incrementCounter({
58
- name: "opengeni_codex_lease_renewals_total",
59
- help: "Codex lease renewal checkpoints by outcome and reason.",
60
- labels: { workspace_key: this.deps.codexWorkspaceKey, outcome: "lost", reason },
61
- });
62
- this.deps.observability.warn("Codex credential lease was lost during an active turn", {
63
- workspaceId: this.deps.workspaceId,
64
- turnId: this.deps.getTurnId(),
65
- reason,
66
- });
67
- };
68
-
69
- /** Fail closed at the last worker-confirmed lease deadline before dispatch. */
70
- assertUsable = (): void => {
71
- if (this.lost) {
72
- throw new CodexCredentialLeaseLostError(this.lossReason ?? "not_found");
73
- }
74
- if (
75
- !this.held ||
76
- this.holderId === null ||
77
- this.generation === null ||
78
- codexCredentialLeaseDeadlineExpired(this.confirmedUntilMs)
79
- ) {
80
- this.markLost("deadline");
81
- throw new CodexCredentialLeaseLostError("deadline");
82
- }
83
- };
84
-
85
- renew = async (reason: LeaseRenewReason): Promise<void> => {
86
- const turnId = this.deps.getTurnId();
87
- if (!turnId || !this.held || !this.holderId || this.generation === null || this.lost) {
88
- return;
89
- }
90
- if (codexCredentialLeaseDeadlineExpired(this.confirmedUntilMs)) {
91
- this.markLost("deadline");
92
- return;
93
- }
94
- if (this.heartbeatInFlight) return;
95
- this.heartbeatInFlight = true;
96
- const priorConfirmedUntilMs = this.confirmedUntilMs;
97
- const renewalStartedAtMs = performance.now();
98
- try {
99
- const renewedUntil = await heartbeatCodexCredentialLeaseUntil(
100
- this.deps.db,
101
- this.deps.accountId,
102
- this.deps.workspaceId,
103
- turnId,
104
- this.holderId,
105
- this.generation,
106
- CODEX_CREDENTIAL_LEASE_TTL_MS,
107
- );
108
- if (!renewedUntil) {
109
- this.markLost("not_found");
110
- } else if (
111
- codexCredentialLeaseDeadlineExpired(priorConfirmedUntilMs) ||
112
- codexCredentialLeaseDeadlineExpired(renewalStartedAtMs + CODEX_CREDENTIAL_LEASE_TTL_MS)
113
- ) {
114
- // The database call may have waited on a lock until the prior lease
115
- // expired. A late successful UPDATE is not proof that this worker
116
- // owned the credential continuously; do not accept or extend it.
117
- this.markLost("deadline");
118
- } else {
119
- this.confirmedUntilMs = renewalStartedAtMs + CODEX_CREDENTIAL_LEASE_TTL_MS;
120
- this.deps.observability.incrementCounter({
41
+ export class CodexTurnLease extends SubscriptionTurnLease {
42
+ constructor(deps: TurnCredentialLeaseDeps) {
43
+ super({
44
+ ttlMs: CODEX_CREDENTIAL_LEASE_TTL_MS,
45
+ getTurnId: deps.getTurnId,
46
+ heartbeat: ({ turnId, holderId, generation }) =>
47
+ heartbeatCodexCredentialLeaseUntil(
48
+ deps.db,
49
+ deps.accountId,
50
+ deps.workspaceId,
51
+ turnId,
52
+ holderId,
53
+ generation,
54
+ CODEX_CREDENTIAL_LEASE_TTL_MS,
55
+ ),
56
+ lostError: (reason) => new CodexCredentialLeaseLostError(reason),
57
+ onLost: (reason) => {
58
+ deps.observability.incrementCounter({
121
59
  name: "opengeni_codex_lease_renewals_total",
122
60
  help: "Codex lease renewal checkpoints by outcome and reason.",
123
- labels: {
124
- workspace_key: this.deps.codexWorkspaceKey,
125
- outcome: "completed",
126
- reason,
127
- },
61
+ labels: { workspace_key: deps.codexWorkspaceKey, outcome: "lost", reason },
128
62
  });
129
- }
130
- } catch (error) {
131
- if (codexCredentialLeaseDeadlineExpired(this.confirmedUntilMs)) {
132
- this.markLost("deadline");
133
- return;
134
- }
135
- this.deps.observability.warn("Codex credential lease heartbeat failed", {
136
- workspaceId: this.deps.workspaceId,
137
- turnId,
138
- reason,
139
- ...safeErrorDiagnostic(error),
140
- });
141
- this.deps.observability.incrementCounter({
142
- name: "opengeni_codex_lease_renewals_total",
143
- help: "Codex lease renewal checkpoints by outcome and reason.",
144
- labels: {
145
- workspace_key: this.deps.codexWorkspaceKey,
146
- outcome: "error",
63
+ deps.observability.warn("Codex credential lease was lost during an active turn", {
64
+ workspaceId: deps.workspaceId,
65
+ turnId: deps.getTurnId(),
147
66
  reason,
148
- },
149
- });
150
- } finally {
151
- this.heartbeatInFlight = false;
152
- }
153
- };
154
-
155
- startHeartbeat = (): void => {
156
- if (!this.deps.getTurnId() || this.heartbeatTimer) return;
157
- this.heartbeatTimer = setInterval(() => {
158
- void this.renew("timer");
159
- }, 60_000);
160
- this.heartbeatTimer.unref?.();
161
- };
162
-
163
- stopHeartbeat = (): void => {
164
- if (!this.heartbeatTimer) return;
165
- clearInterval(this.heartbeatTimer);
166
- this.heartbeatTimer = undefined;
167
- };
67
+ });
68
+ },
69
+ onRenewed: (reason) =>
70
+ deps.observability.incrementCounter({
71
+ name: "opengeni_codex_lease_renewals_total",
72
+ help: "Codex lease renewal checkpoints by outcome and reason.",
73
+ labels: { workspace_key: deps.codexWorkspaceKey, outcome: "completed", reason },
74
+ }),
75
+ onError: (error, reason) => {
76
+ deps.observability.warn("Codex credential lease heartbeat failed", {
77
+ workspaceId: deps.workspaceId,
78
+ turnId: deps.getTurnId(),
79
+ reason,
80
+ ...safeErrorDiagnostic(error),
81
+ });
82
+ deps.observability.incrementCounter({
83
+ name: "opengeni_codex_lease_renewals_total",
84
+ help: "Codex lease renewal checkpoints by outcome and reason.",
85
+ labels: { workspace_key: deps.codexWorkspaceKey, outcome: "error", reason },
86
+ });
87
+ },
88
+ });
89
+ }
168
90
  }
169
91
 
170
- /** The xAI/SuperGrok credential holder for one running turn; same shape. */
171
- export class XaiTurnLease {
172
- held = false;
173
- lost = false;
92
+ /** Scoped subscriptions share the existing holder and monotonic deadline implementation. */
93
+ class ScopedSubscriptionTurnLease extends SubscriptionTurnLease {
174
94
  subjectId: string | null = null;
175
- holderId: string | null = null;
176
- generation: number | null = null;
177
- confirmedUntilMs: number | null = null;
178
- private heartbeatInFlight = false;
179
- private heartbeatTimer: ReturnType<typeof setInterval> | undefined;
180
-
181
- constructor(private readonly deps: TurnCredentialLeaseDeps) {}
182
-
183
- markLost = (reason: LeaseLossReason): void => {
184
- if (this.lost) return;
185
- this.lost = true;
186
- this.deps.observability.warn("xAI credential lease was lost during an active turn", {
187
- workspaceId: this.deps.workspaceId,
188
- turnId: this.deps.getTurnId(),
189
- reason,
95
+ constructor(
96
+ deps: TurnCredentialLeaseDeps,
97
+ provider: {
98
+ name: string;
99
+ code: string;
100
+ ttlMs: number;
101
+ heartbeat: typeof heartbeatXaiCredentialLeaseUntil;
102
+ },
103
+ ) {
104
+ super({
105
+ ttlMs: provider.ttlMs,
106
+ getTurnId: deps.getTurnId,
107
+ heartbeat: ({ turnId, holderId, generation }) =>
108
+ this.subjectId
109
+ ? provider.heartbeat(deps.db, {
110
+ workspaceId: deps.workspaceId,
111
+ subjectId: this.subjectId,
112
+ turnId,
113
+ holderId,
114
+ generation,
115
+ leaseTtlMs: provider.ttlMs,
116
+ })
117
+ : Promise.resolve(null),
118
+ lostError: (reason) =>
119
+ Object.assign(
120
+ new Error(provider.name + " credential lease is not usable for provider dispatch"),
121
+ { code: provider.code, reason },
122
+ ),
123
+ onLost: (reason) =>
124
+ deps.observability.warn(
125
+ provider.name + " credential lease was lost during an active turn",
126
+ { workspaceId: deps.workspaceId, turnId: deps.getTurnId(), reason },
127
+ ),
128
+ onError: (error) =>
129
+ deps.observability.warn(provider.name + " credential lease heartbeat failed", {
130
+ workspaceId: deps.workspaceId,
131
+ turnId: deps.getTurnId(),
132
+ ...safeErrorDiagnostic(error),
133
+ }),
190
134
  });
191
- };
192
-
193
- renew = async (): Promise<void> => {
194
- const turnId = this.deps.getTurnId();
195
- if (
196
- !turnId ||
197
- !this.held ||
198
- !this.subjectId ||
199
- !this.holderId ||
200
- this.generation === null ||
201
- this.lost
202
- ) {
203
- return;
204
- }
205
- if (codexCredentialLeaseDeadlineExpired(this.confirmedUntilMs)) {
206
- this.markLost("deadline");
207
- return;
208
- }
209
- if (this.heartbeatInFlight) return;
210
- this.heartbeatInFlight = true;
211
- const renewalStartedAtMs = performance.now();
212
- try {
213
- const renewedUntil = await heartbeatXaiCredentialLeaseUntil(this.deps.db, {
214
- workspaceId: this.deps.workspaceId,
215
- subjectId: this.subjectId,
216
- turnId,
217
- holderId: this.holderId,
218
- generation: this.generation,
219
- leaseTtlMs: XAI_CREDENTIAL_LEASE_TTL_MS,
220
- });
221
- if (!renewedUntil) {
222
- this.markLost("not_found");
223
- } else {
224
- this.confirmedUntilMs = renewalStartedAtMs + XAI_CREDENTIAL_LEASE_TTL_MS;
225
- }
226
- } catch (error) {
227
- if (codexCredentialLeaseDeadlineExpired(this.confirmedUntilMs)) {
228
- this.markLost("deadline");
229
- return;
230
- }
231
- this.deps.observability.warn("xAI credential lease heartbeat failed", {
232
- workspaceId: this.deps.workspaceId,
233
- turnId,
234
- ...safeErrorDiagnostic(error),
235
- });
236
- } finally {
237
- this.heartbeatInFlight = false;
238
- }
239
- };
240
-
241
- startHeartbeat = (): void => {
242
- if (!this.deps.getTurnId() || this.heartbeatTimer) return;
243
- this.heartbeatTimer = setInterval(() => {
244
- void this.renew();
245
- }, 60_000);
246
- this.heartbeatTimer.unref?.();
247
- };
248
-
249
- stopHeartbeat = (): void => {
250
- if (!this.heartbeatTimer) return;
251
- clearInterval(this.heartbeatTimer);
252
- this.heartbeatTimer = undefined;
253
- };
135
+ }
136
+ }
137
+ export class XaiTurnLease extends ScopedSubscriptionTurnLease {
138
+ constructor(deps: TurnCredentialLeaseDeps) {
139
+ super(deps, {
140
+ name: "SuperGrok",
141
+ code: "xai_credential_lease_lost",
142
+ ttlMs: XAI_CREDENTIAL_LEASE_TTL_MS,
143
+ heartbeat: heartbeatXaiCredentialLeaseUntil,
144
+ });
145
+ }
146
+ }
147
+ export class ClaudeTurnLease extends ScopedSubscriptionTurnLease {
148
+ constructor(deps: TurnCredentialLeaseDeps) {
149
+ super(deps, {
150
+ name: "Claude",
151
+ code: "claude_credential_lease_lost",
152
+ ttlMs: CLAUDE_CREDENTIAL_LEASE_TTL_MS,
153
+ heartbeat: heartbeatClaudeCredentialLeaseUntil,
154
+ });
155
+ }
254
156
  }
255
157
 
256
- /** Both serving-credential leases for one turn attempt plus their composite views. */
158
+ /** Serving-credential leases for one turn attempt plus their composite views. */
257
159
  export type TurnCredentialLeases = {
258
160
  codex: CodexTurnLease;
259
161
  xai: XaiTurnLease;
162
+ claude: ClaudeTurnLease;
260
163
  renewServing: (reason: LeaseRenewReason) => Promise<void>;
261
164
  servingLost: () => boolean;
262
165
  };
@@ -264,13 +167,16 @@ export type TurnCredentialLeases = {
264
167
  export function createTurnCredentialLeases(deps: TurnCredentialLeaseDeps): TurnCredentialLeases {
265
168
  const codex = new CodexTurnLease(deps);
266
169
  const xai = new XaiTurnLease(deps);
170
+ const claude = new ClaudeTurnLease(deps);
267
171
  return {
268
172
  codex,
269
173
  xai,
174
+ claude,
270
175
  renewServing: async (reason) => {
271
176
  await codex.renew(reason);
272
- await xai.renew();
177
+ await xai.renew(reason);
178
+ await claude.renew(reason);
273
179
  },
274
- servingLost: () => codex.lost || xai.lost,
180
+ servingLost: () => codex.lost || xai.lost || claude.lost,
275
181
  };
276
182
  }
@@ -0,0 +1,14 @@
1
+ import { readTurnExecutionPolicyV1, type TurnExecutionPolicyV1 } from "@opengeni/contracts";
2
+
3
+ /** Only accepted policies establish the ceiling; ordinary metadata is not authority. */
4
+ export function turnCredentialRestriction(
5
+ acceptedPolicy: TurnExecutionPolicyV1,
6
+ initialSessionMetadata: unknown,
7
+ ): "developer_setup" | undefined {
8
+ const initialPolicy = readTurnExecutionPolicyV1(initialSessionMetadata);
9
+ return acceptedPolicy.credentialRestriction === "developer_setup" ||
10
+ (initialPolicy.kind === "valid" &&
11
+ initialPolicy.policy.credentialRestriction === "developer_setup")
12
+ ? "developer_setup"
13
+ : undefined;
14
+ }