@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,254 @@
1
+ // ChatGPT plan entitlement for Codex-billed turns.
2
+ //
3
+ // A connected ChatGPT account can move to a plan that no longer includes the
4
+ // requested model (production: Pro to Free, then every request answered with
5
+ // an empty HTTP 400). This module turns that evidence into a typed decision:
6
+ // re-check the account's current plan, and either exclude that credential for
7
+ // this model and let the normal failover protocol move the SAME turn, or fail
8
+ // with a typed, plain-language payload. It never retries the request itself.
9
+
10
+ import { productLabelForModelId } from "@opengeni/config";
11
+ import {
12
+ codexPlanDisplayName,
13
+ codexPlanEntitlementLost,
14
+ codexPlanIsUpgrade,
15
+ codexPlanKey,
16
+ type CodexEntitlementRejection,
17
+ } from "@opengeni/codex";
18
+ import {
19
+ codexPlanExcludesModel,
20
+ codexPlanPreviouslyExcludedModel,
21
+ connectionModelAllowed,
22
+ type CodexCredentialPlanRecheck,
23
+ type CodexPlanEntitlementExclusion,
24
+ } from "@opengeni/db";
25
+
26
+ export const CODEX_PLAN_ENTITLEMENT_CODE = "codex_plan_entitlement";
27
+ export const CODEX_REQUEST_REJECTED_CODE = "codex_request_rejected";
28
+
29
+ export type CodexPlanEntitlementFailurePayload = {
30
+ error: string;
31
+ code: typeof CODEX_PLAN_ENTITLEMENT_CODE;
32
+ retryable: false;
33
+ planType: string | null;
34
+ model: string | null;
35
+ detail?: string;
36
+ };
37
+
38
+ export type CodexRequestRejectedFailurePayload = {
39
+ error: string;
40
+ code: typeof CODEX_REQUEST_REJECTED_CODE;
41
+ retryable: false;
42
+ planType: string | null;
43
+ detail?: string;
44
+ };
45
+
46
+ /**
47
+ * The user-set label of one connected ChatGPT account. The account email is
48
+ * deliberately never used: this text lands in the durable `turn.failed` event
49
+ * that every session reader (including embedding-host end users) can see, and
50
+ * an inherited organization credential's email may be a member's personal
51
+ * address. Without a label the copy says "The connected ChatGPT account".
52
+ */
53
+ export function codexAccountDisplayLabel(
54
+ account: { label: string | null } | null | undefined,
55
+ ): string | null {
56
+ const label = account?.label?.trim() || null;
57
+ return label ? label.slice(0, 120) : null;
58
+ }
59
+
60
+ function accountPhrase(accountLabel: string | null): string {
61
+ return accountLabel ? `The ChatGPT account "${accountLabel}"` : "The connected ChatGPT account";
62
+ }
63
+
64
+ function rejectionDetail(rejection: CodexEntitlementRejection | null): string | undefined {
65
+ if (!rejection) return undefined;
66
+ return rejection.evidence === "empty_body"
67
+ ? `The Codex backend answered HTTP ${rejection.status} with no error body.`
68
+ : `The Codex backend refused the model for this plan (HTTP ${rejection.status}).`;
69
+ }
70
+
71
+ /** Terminal copy when the serving account's plan does not include the model. */
72
+ export function codexPlanEntitlementFailurePayload(input: {
73
+ accountLabel: string | null;
74
+ planType: string | null;
75
+ planChanged: boolean;
76
+ modelId: string | null;
77
+ rejection?: CodexEntitlementRejection | null;
78
+ /** The same turn waits for another connected account instead of failing. */
79
+ waiting?: boolean;
80
+ }): CodexPlanEntitlementFailurePayload {
81
+ const plan = codexPlanDisplayName(input.planType);
82
+ const model = input.modelId ? productLabelForModelId(input.modelId) : "this model";
83
+ const lead = accountPhrase(input.accountLabel);
84
+ const next = input.waiting
85
+ ? "Opengeni is waiting for another connected account to become available."
86
+ : "Upgrade it, use another connected account, or choose another model.";
87
+ const error = plan
88
+ ? `${lead} ${input.planChanged ? "is now" : "is"} on the ${plan} plan, which doesn't include ${model}. ${next}`
89
+ : `${lead} no longer has access to ${model} on its current plan. ${next}`;
90
+ const detail = rejectionDetail(input.rejection ?? null);
91
+ return {
92
+ error,
93
+ code: CODEX_PLAN_ENTITLEMENT_CODE,
94
+ retryable: false,
95
+ planType: plan ? codexPlanKey(input.planType) : null,
96
+ model: input.modelId,
97
+ ...(detail ? { detail } : {}),
98
+ };
99
+ }
100
+
101
+ /**
102
+ * Terminal copy for an empty Codex rejection. With `planChecked`, the account's
103
+ * plan was re-read and did not explain it, so OpenGeni kept the account.
104
+ */
105
+ export function codexRequestRejectedFailurePayload(input: {
106
+ accountLabel: string | null;
107
+ planType: string | null;
108
+ rejection: CodexEntitlementRejection;
109
+ planChecked?: boolean;
110
+ }): CodexRequestRejectedFailurePayload {
111
+ const plan = codexPlanDisplayName(input.planType);
112
+ const lead = accountPhrase(input.accountLabel);
113
+ const planClause =
114
+ input.planChecked === false
115
+ ? ""
116
+ : plan
117
+ ? `${lead} still reports the ${plan} plan, so Opengeni did not switch accounts. `
118
+ : `Opengeni could not confirm the current plan of ${lead.charAt(0).toLowerCase()}${lead.slice(1)}, so it did not switch accounts. `;
119
+ return {
120
+ error:
121
+ `The Codex backend rejected this request (HTTP ${input.rejection.status}) without an error message. ` +
122
+ `${planClause}Try again, or choose another model if it keeps failing.`,
123
+ code: CODEX_REQUEST_REJECTED_CODE,
124
+ retryable: false,
125
+ planType: plan ? codexPlanKey(input.planType) : null,
126
+ ...(rejectionDetail(input.rejection) ? { detail: rejectionDetail(input.rejection)! } : {}),
127
+ };
128
+ }
129
+
130
+ /**
131
+ * Admission-time refusal: the only accounts that could serve this turn were
132
+ * already proven not to include its model on their current plan.
133
+ */
134
+ export class CodexPlanEntitlementError extends Error {
135
+ readonly code = CODEX_PLAN_ENTITLEMENT_CODE;
136
+ constructor(readonly payload: CodexPlanEntitlementFailurePayload) {
137
+ super(payload.error);
138
+ this.name = "CodexPlanEntitlementError";
139
+ }
140
+ }
141
+
142
+ export type CodexPlanEntitlementAssessment =
143
+ | {
144
+ kind: "entitlement_lost";
145
+ /**
146
+ * Plan the exclusion binds to: the freshly observed plan, or the
147
+ * recorded plan when the provider reported none.
148
+ */
149
+ planType: string | null;
150
+ /** The provider reported the current plan during this re-check. */
151
+ planObserved: boolean;
152
+ /** The observed plan was reached by a recorded (non-upgrade) plan change. */
153
+ planChanged: boolean;
154
+ credentialVersion: number | null;
155
+ }
156
+ | { kind: "unexplained"; planType: string | null };
157
+
158
+ /** Pure decision over one plan re-check (see `codexPlanEntitlementLost`). */
159
+ export function assessCodexPlanEntitlement(
160
+ rejection: CodexEntitlementRejection,
161
+ recheck: CodexCredentialPlanRecheck,
162
+ modelId: string | null,
163
+ ): CodexPlanEntitlementAssessment {
164
+ const planObserved = recheck.planType !== null;
165
+ // The persisted change record, or (if that write did not land) the plan
166
+ // recorded just before this re-check when the fresh observation differs.
167
+ const planChangedFrom =
168
+ planObserved &&
169
+ recheck.previousPlanType !== null &&
170
+ codexPlanKey(recheck.previousPlanType) !== codexPlanKey(recheck.planType)
171
+ ? recheck.previousPlanType
172
+ : recheck.planChangedFrom;
173
+ const planChanged =
174
+ planObserved &&
175
+ planChangedFrom !== null &&
176
+ codexPlanKey(planChangedFrom) !== codexPlanKey(recheck.planType) &&
177
+ !codexPlanIsUpgrade(planChangedFrom, recheck.planType);
178
+ if (
179
+ codexPlanEntitlementLost({
180
+ evidence: rejection.evidence,
181
+ currentPlanType: recheck.planType,
182
+ planChangedFrom,
183
+ previouslyExcluded:
184
+ planObserved &&
185
+ codexPlanPreviouslyExcludedModel(
186
+ { planType: recheck.planType, planEntitlementExclusion: recheck.exclusion },
187
+ modelId,
188
+ ),
189
+ })
190
+ ) {
191
+ return {
192
+ kind: "entitlement_lost",
193
+ planType: planObserved ? recheck.planType : recheck.previousPlanType,
194
+ planObserved,
195
+ planChanged,
196
+ credentialVersion: recheck.credentialVersion,
197
+ };
198
+ }
199
+ return { kind: "unexplained", planType: recheck.planType };
200
+ }
201
+
202
+ type AdmissionAccount = {
203
+ id: string;
204
+ label: string | null;
205
+ planType: string | null;
206
+ allocatorEnabled: boolean;
207
+ allowedModelIds?: string[] | null;
208
+ planEntitlementExclusion?: CodexPlanEntitlementExclusion | null;
209
+ };
210
+
211
+ /**
212
+ * Pure admission check after credential selection found no account. Returns
213
+ * the plan-excluded accounts that alone explain it, or null when capacity,
214
+ * health, or policy (not plan entitlement) is the reason:
215
+ *
216
+ * - a manual pin (user intent) whose account's plan excludes the model;
217
+ * - with rotation off, the active account's plan excludes the model;
218
+ * - with rotation on, every allocatable account permitted for the model is
219
+ * plan-excluded.
220
+ */
221
+ export function codexPlanEntitlementAdmissionBlock<T extends AdmissionAccount>(input: {
222
+ accounts: readonly T[];
223
+ modelId: string;
224
+ credentialId: string | null;
225
+ rotationEnabled: boolean;
226
+ activeCredentialId: string | null;
227
+ pinnedCredentialId: string | null;
228
+ pinSource: "manual" | "policy" | null;
229
+ now: Date;
230
+ }): T[] | null {
231
+ if (input.credentialId !== null) return null;
232
+ const permitted = input.accounts.filter((account) =>
233
+ connectionModelAllowed(account.allowedModelIds, input.modelId),
234
+ );
235
+ const excluded = permitted.filter((account) =>
236
+ codexPlanExcludesModel(account, input.modelId, input.now),
237
+ );
238
+ if (excluded.length === 0) return null;
239
+ if (input.pinnedCredentialId && input.pinSource !== "policy") {
240
+ const pinned = excluded.find((account) => account.id === input.pinnedCredentialId);
241
+ return pinned ? [pinned] : null;
242
+ }
243
+ if (!input.rotationEnabled) {
244
+ const active = excluded.find((account) => account.id === input.activeCredentialId);
245
+ return active ? [active] : null;
246
+ }
247
+ const allocatable = permitted.filter((account) => account.allocatorEnabled);
248
+ const allocatableExcluded = allocatable.filter((account) =>
249
+ codexPlanExcludesModel(account, input.modelId, input.now),
250
+ );
251
+ return allocatable.length > 0 && allocatableExcluded.length === allocatable.length
252
+ ? allocatableExcluded
253
+ : null;
254
+ }
@@ -3,7 +3,7 @@ import {
3
3
  fetchCodexUsageForAccount,
4
4
  type CodexAccountStatus,
5
5
  } from "@opengeni/db";
6
- import { type ResolvedModelProvider, type Settings } from "@opengeni/config";
6
+ import { type ModelProviderApi, type ResolvedModelProvider, type Settings } from "@opengeni/config";
7
7
  import { codexAccountNeedsLiveCapacityRefresh } from "../codex-rotation";
8
8
  import {
9
9
  refreshCodexUsageAndRepairCapacityWaiters,
@@ -19,14 +19,18 @@ export function codexWorkspaceMetricKey(workspaceId: string): string {
19
19
  /** Stable public request identity across partial resumes and activity retries. */
20
20
  export function acceptsPromptCacheKeyForTurn(
21
21
  resolvedModel: {
22
- provider: { kind: ResolvedModelProvider["kind"]; builtin?: boolean };
22
+ provider: { kind: ResolvedModelProvider["kind"]; builtin?: boolean; api?: ModelProviderApi };
23
23
  } | null,
24
24
  ): boolean {
25
25
  if (!resolvedModel) {
26
26
  return true;
27
27
  }
28
28
  return (
29
- resolvedModel.provider.builtin === true || resolvedModel.provider.kind === "codex-subscription"
29
+ resolvedModel.provider.builtin === true ||
30
+ resolvedModel.provider.kind === "codex-subscription" ||
31
+ // Native Claude consumes this internal key as session identity; it never
32
+ // sends OpenAI's prompt_cache_key field to the Messages endpoint.
33
+ resolvedModel.provider.api === "anthropic-messages"
30
34
  );
31
35
  }
32
36
 
@@ -61,6 +65,7 @@ export async function refreshCappedCodexUsageRows(
61
65
  signalCodexCapacityWorkflow?: ActivityServices["signalCodexCapacityWorkflow"] | undefined;
62
66
  wakeSessionWorkflow: ActivityServices["wakeSessionWorkflow"];
63
67
  },
68
+ acceptedTurnId?: string,
64
69
  ): Promise<
65
70
  Array<
66
71
  Pick<
@@ -82,10 +87,13 @@ export async function refreshCappedCodexUsageRows(
82
87
  return accounts;
83
88
  }
84
89
  await refreshCodexUsageAndRepairCapacityWaiters(
85
- stale.map((account) => () => fetchCodexUsageForAccount(db, settings, workspaceId, account.id)),
90
+ stale.map(
91
+ (account) => () =>
92
+ fetchCodexUsageForAccount(db, settings, workspaceId, account.id, undefined, acceptedTurnId),
93
+ ),
86
94
  () => signalPendingCodexCapacityWakeTargets({ db, ...capacitySignals }, workspaceId),
87
95
  );
88
- return listCodexAccountStatuses(db, workspaceId).catch(() => accounts);
96
+ return listCodexAccountStatuses(db, workspaceId, acceptedTurnId).catch(() => accounts);
89
97
  }
90
98
 
91
99
  /**