@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,47 +1,46 @@
1
1
  import type { Settings } from "@opengeni/config";
2
- import type { ConnectionCredentialsPort } from "@opengeni/contracts";
3
- import type { HostMcpAcceptedAuthority } from "@opengeni/contracts/host-mcp-bindings";
2
+ import { stableJson } from "@opengeni/contracts";
4
3
  import {
5
4
  buildConnectionTokenResolver,
6
- buildHostConnectionTokenResolver,
7
- getSessionTurnForAttempt,
8
- authorizeDirectHostMcpUse,
9
- resolveAcceptedHostMcpBinding,
10
5
  resolveAcceptedConnectionUse,
11
- sessionTenancyProductActivated,
12
6
  type Database,
13
7
  type ResolveConnectionCredentialInput,
14
8
  type ResolveConnectionCredentialResult,
15
9
  type SessionTurnForExecution,
16
10
  } from "@opengeni/db";
17
- import { recordTenancyCompatibilityLaneUse, type Observability } from "@opengeni/observability";
18
11
  import { mcpOperationAuthorityDigest } from "./mcp-operation-authority";
19
12
 
20
- const OPENGENI_CONNECTION_ID_PATTERN =
21
- /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
22
-
23
- export function connectionTokenResolverForTurn(input: {
13
+ type TurnConnectionInput = {
24
14
  db: Database;
25
15
  settings: Settings;
26
- connectionCredentials?: ConnectionCredentialsPort | null;
27
16
  accountId: string;
28
17
  workspaceId: string;
29
18
  sessionId: string;
30
- rootSessionId: string;
31
19
  attemptId: string;
32
20
  turn: SessionTurnForExecution;
21
+ /** Policy identities before account expansion; never used to select credentials. */
22
+ canonicalMcpServerIds?: readonly string[];
33
23
  authorizeAcceptedUse?: typeof resolveAcceptedConnectionUse;
34
- /** Test seam; production always reads the canonical active-attempt projection. */
35
- getHostTurnForAttempt?: typeof getSessionTurnForAttempt;
36
- /** Test seam for the activation fence on pre-snapshot workspace refs. */
37
- isSessionTenancyProductActivated?: typeof sessionTenancyProductActivated;
38
- /** Optional; used only for content-free compatibility-lane counters. */
39
- observability?: Observability | null | undefined;
40
- }): (request: ResolveConnectionCredentialInput) => Promise<ResolveConnectionCredentialResult> {
41
- const hostResolver = input.connectionCredentials?.mcpCredentials;
42
- const readHostTurn = input.getHostTurnForAttempt ?? getSessionTurnForAttempt;
43
- const hostDb = input.db;
44
- const nativeResolver = buildConnectionTokenResolver(input.db, input.settings);
24
+ };
25
+
26
+ type CredentialResolver = (
27
+ request: ResolveConnectionCredentialInput,
28
+ ) => Promise<ResolveConnectionCredentialResult>;
29
+
30
+ export function connectionTokenResolverForTurn(input: TurnConnectionInput): CredentialResolver {
31
+ return bindNativeConnectionCredentialsToTurn(
32
+ input,
33
+ buildConnectionTokenResolver(input.db, input.settings),
34
+ );
35
+ }
36
+
37
+ /** Bind the ordinary credential resolver to one immutable accepted turn.
38
+ * Credential acquisition and refresh stay inside the native connection engine;
39
+ * this layer adds the exact execution context and physical-request checks. */
40
+ export function bindNativeConnectionCredentialsToTurn(
41
+ input: TurnConnectionInput,
42
+ nativeResolver: CredentialResolver,
43
+ ): CredentialResolver {
45
44
  const recoveryEnabled = (serverId: string) =>
46
45
  input.settings.mcpServers.some(
47
46
  (server) =>
@@ -50,84 +49,14 @@ export function connectionTokenResolverForTurn(input: {
50
49
  server.operationRecovery !== undefined &&
51
50
  Object.keys(server.operationRecovery ?? {}).length > 0,
52
51
  );
53
- const configuredResolver = hostResolver
54
- ? async (
55
- credentialRequest: ResolveConnectionCredentialInput,
56
- ): Promise<ResolveConnectionCredentialResult> => {
57
- let authorizedSnapshot: HostMcpAcceptedAuthority | undefined;
58
- const wantsDigest = recoveryEnabled(credentialRequest.serverId);
59
- const resolver = buildHostConnectionTokenResolver(hostResolver, {
60
- accountId: input.accountId,
61
- workspaceId: input.workspaceId,
62
- sessionId: input.sessionId,
63
- rootSessionId: input.rootSessionId,
64
- turnId: input.turn.id,
65
- attemptId: input.attemptId,
66
- executionGeneration: input.turn.executionGeneration,
67
- initiator: input.turn.initiator,
68
- initiatorContext: input.turn.initiatorContext,
69
- surface: "model",
70
- resolveAcceptedBinding: (request) => resolveAcceptedHostMcpBinding(hostDb, request),
71
- // Only immutable captured direct-turn authority is currently supported.
72
- // Direct, scheduled and exact causal/child snapshots are validated;
73
- // no authority follows merely from a binding or creator identity.
74
- authorizeDurableBinding: (request) =>
75
- authorizeDirectHostMcpUse(
76
- hostDb,
77
- request,
78
- wantsDigest
79
- ? (snapshot) => {
80
- authorizedSnapshot = snapshot;
81
- }
82
- : undefined,
83
- ),
84
- authorizeExecution: async (request) => {
85
- // Native UUID references already pass the accepted-use boundary below.
86
- // Legacy opaque host IDs retain their compatibility path, but not a
87
- // right to execute after their accepted attempt has stopped.
88
- if (
89
- request.connectionRef.authoritySource !== "host" &&
90
- (!request.connectionRef.connectionId ||
91
- OPENGENI_CONNECTION_ID_PATTERN.test(request.connectionRef.connectionId))
92
- )
93
- return true;
94
- if (!request.attemptId) return false;
95
- const current = await readHostTurn(
96
- hostDb,
97
- request.workspaceId,
98
- request.sessionId,
99
- request.attemptId,
100
- );
101
- return (
102
- current !== null &&
103
- current.id === request.turnId &&
104
- current.executionGeneration === request.executionGeneration
105
- );
106
- },
107
- });
108
- const result = await resolver(credentialRequest);
109
- return result.status === "ok" && authorizedSnapshot
110
- ? {
111
- ...result,
112
- operationAuthorityDigest: mcpOperationAuthorityDigest(credentialRequest, {
113
- host: authorizedSnapshot,
114
- }),
115
- }
116
- : result;
117
- }
118
- : nativeResolver;
119
- const baseResolver = (request: ResolveConnectionCredentialInput) =>
120
- input.connectionCredentials?.mcpAuthoritySource === "host" &&
121
- request.connectionRef.authoritySource !== "host"
122
- ? nativeResolver(request)
123
- : configuredResolver(request);
52
+ const canonicalMcpServerIds = new Set(
53
+ input.canonicalMcpServerIds ??
54
+ input.settings.mcpServers.filter((server) => server.connectionRef).map((server) => server.id),
55
+ );
124
56
  return async (request) => {
125
- // Host-owned refs carry explicit provenance because their opaque ids may
126
- // themselves be valid UUIDs. They never enter OpenGeni's native connection
127
- // authority or PostgreSQL UUID lookup; the bound host authorizes the exact
128
- // immutable turn context on every credential request.
57
+ // Superseded host references must never fall through to native lookup,
58
+ // even when their opaque identifier happens to be a valid native UUID.
129
59
  if (request.connectionRef.authoritySource === "host") {
130
- if (hostResolver) return await baseResolver(request);
131
60
  return {
132
61
  status: "auth_needed",
133
62
  reason: "unsupported_auth",
@@ -144,34 +73,31 @@ export function connectionTokenResolverForTurn(input: {
144
73
  : {}),
145
74
  };
146
75
  }
147
- // Before explicit provenance existed, the public embedding contract let a
148
- // bound host use any non-UUID opaque id. Preserve those frozen/stored refs
149
- // during upgrades while keeping every UUID-shaped omission on OpenGeni's
150
- // native accepted-use authority. New host refs must carry authoritySource
151
- // so UUID-shaped host ids are unambiguous.
76
+ const accountBindings = input.turn.mcpAccountBindings ?? [];
77
+ const accountBinding = accountBindings.find((binding) => binding.serverId === request.serverId);
78
+ // Canonical policy identity cannot be invoked as a credential fallback once
79
+ // this turn selected explicit account routes for that connector.
152
80
  if (
153
- hostResolver &&
154
- input.connectionCredentials?.mcpAuthoritySource !== "host" &&
155
- request.connectionRef.connectionId &&
156
- !OPENGENI_CONNECTION_ID_PATTERN.test(request.connectionRef.connectionId)
81
+ (input.turn.mcpAccountBindings != null &&
82
+ !accountBinding &&
83
+ canonicalMcpServerIds.has(request.serverId)) ||
84
+ (!accountBinding &&
85
+ accountBindings.some((binding) => binding.canonicalServerId === request.serverId)) ||
86
+ (accountBinding &&
87
+ (request.connectionRef.connectionId !== accountBinding.connectionId ||
88
+ request.connectionRef.providerDomain !== accountBinding.providerDomain ||
89
+ request.connectionRef.kind !== accountBinding.kind ||
90
+ request.connectionRef.subjectScope !== accountBinding.subjectScope ||
91
+ stableJson(request.connectionRef) !== stableJson(accountBinding.connectionRef) ||
92
+ (accountBinding.subjectScope === "subject" &&
93
+ request.subjectId !== accountBinding.ownerSubjectId)))
157
94
  ) {
158
- try {
159
- return await baseResolver(request);
160
- } catch {
161
- return {
162
- status: "auth_needed",
163
- reason: "refresh_failed",
164
- providerDomain: request.connectionRef.providerDomain,
165
- authoritySource: "host",
166
- ...(request.connectionRef.provider ? { provider: request.connectionRef.provider } : {}),
167
- connectionId: request.connectionRef.connectionId,
168
- ...(request.connectionRef.scopes ? { scopes: request.connectionRef.scopes } : {}),
169
- ...(request.connectionRef.resource ? { resource: request.connectionRef.resource } : {}),
170
- ...(request.connectionRef.selectedResources
171
- ? { selectedResources: request.connectionRef.selectedResources }
172
- : {}),
173
- };
174
- }
95
+ return {
96
+ status: "auth_needed",
97
+ reason: "personal_authority_unavailable",
98
+ providerDomain: request.connectionRef.providerDomain,
99
+ ...(request.connectionRef.provider ? { provider: request.connectionRef.provider } : {}),
100
+ };
175
101
  }
176
102
  const acceptedDelegation = input.turn.personalConnectionDelegations.find(
177
103
  (delegation) =>
@@ -191,9 +117,7 @@ export function connectionTokenResolverForTurn(input: {
191
117
  const subjectScope: "subject" | "workspace" =
192
118
  request.connectionRef.subjectScope === "subject" ? "subject" : "workspace";
193
119
  // Every subject-scoped request must match an exact connection frozen on the
194
- // accepted turn. This also hard-fences pre-cutover common-user turns that
195
- // lack a userDelegation: the DB resolver denies those rows because only a
196
- // true legacy_user connection is eligible for bounded compatibility.
120
+ // accepted turn. The database then checks its immutable sender snapshot.
197
121
  if (
198
122
  subjectScope === "subject" &&
199
123
  (!acceptedDelegation || !request.connectionRef.connectionId)
@@ -205,31 +129,15 @@ export function connectionTokenResolverForTurn(input: {
205
129
  ...(request.connectionRef.provider ? { provider: request.connectionRef.provider } : {}),
206
130
  };
207
131
  }
208
- // Workspace-scope requests now run through the same accepted-use authority
209
- // (migration 0279): the exact workspace-owned connection is revalidated
210
- // inside the canonical lifecycle fences and every use leaves an idempotent
211
- // audit fact. A ref with no connection id is the bounded pre-snapshot
212
- // legacy path - it cannot be authorized by exact identity, so it keeps the
213
- // unprivileged resolution the old short-circuit used.
132
+ // Workspace connections also need an exact identity for accepted-use
133
+ // validation and attribution. Never rediscover a credential by domain here.
214
134
  if (subjectScope === "workspace" && !request.connectionRef.connectionId) {
215
- if (
216
- await (input.isSessionTenancyProductActivated ?? sessionTenancyProductActivated)(
217
- input.db,
218
- input.workspaceId,
219
- )
220
- ) {
221
- return {
222
- status: "auth_needed",
223
- reason: "missing_connection",
224
- providerDomain: request.connectionRef.providerDomain,
225
- ...(request.connectionRef.provider ? { provider: request.connectionRef.provider } : {}),
226
- };
227
- }
228
- // This lane writes no `connection_use_audit_facts` row, so this counter is
229
- // the only evidence it was taken. Lane name only - never the server,
230
- // provider domain, connection, or subject.
231
- recordTenancyCompatibilityLaneUse(input.observability, "connection_pre_snapshot_ref");
232
- return await baseResolver(request);
135
+ return {
136
+ status: "auth_needed",
137
+ reason: "missing_connection",
138
+ providerDomain: request.connectionRef.providerDomain,
139
+ ...(request.connectionRef.provider ? { provider: request.connectionRef.provider } : {}),
140
+ };
233
141
  }
234
142
  const credentialUseContext = {
235
143
  accountId: input.accountId,
@@ -299,50 +207,31 @@ export function connectionTokenResolverForTurn(input: {
299
207
  },
300
208
  };
301
209
  };
302
- // One place records the `legacy_user` lane for both resolution paths: the
303
- // scope comes back on the resolver result when the DB resolver authorized
304
- // internally, and on the resolution itself when the host resolver did.
305
- const recordAuthorizedScope = (scope: "workspace" | "user" | "legacy_user" | undefined) => {
306
- if (scope === "legacy_user") {
307
- recordTenancyCompatibilityLaneUse(input.observability, "connection_legacy_user");
308
- }
309
- };
310
- if (!hostResolver) {
311
- const result = await baseResolver({
312
- ...request,
313
- connectionUseContext: credentialUseContext,
314
- });
315
- if (result.status === "ok") recordAuthorizedScope(result.connectionUseAttribution?.scope);
316
- return withProviderRequestAuthorization(result);
317
- }
318
- const authorization = await authorize(credentialUseContext);
319
- if (authorization.status === "denied") {
210
+ const { subjectId: requestSubjectId, ...requestWithoutSubject } = request;
211
+ const result = await nativeResolver({
212
+ ...requestWithoutSubject,
213
+ ...(accountBinding?.connectionAuthorityGeneration !== undefined
214
+ ? { expectedAuthorityGeneration: accountBinding.connectionAuthorityGeneration }
215
+ : {}),
216
+ // Workspace authority has no personal owner, even when the turn has a
217
+ // causal human. Do not pass that human as credential ownership context.
218
+ ...(accountBinding?.subjectScope !== "workspace" && requestSubjectId !== undefined
219
+ ? { subjectId: requestSubjectId }
220
+ : {}),
221
+ connectionUseContext: credentialUseContext,
222
+ });
223
+ if (
224
+ accountBinding &&
225
+ result.status === "ok" &&
226
+ result.connectionId !== accountBinding.connectionId
227
+ ) {
320
228
  return {
321
229
  status: "auth_needed",
322
- reason:
323
- request.connectionRef.subjectScope === "subject"
324
- ? "personal_authority_unavailable"
325
- : "missing_connection",
326
- providerDomain: request.connectionRef.providerDomain,
327
- ...(request.connectionRef.provider ? { provider: request.connectionRef.provider } : {}),
230
+ reason: "personal_authority_unavailable",
231
+ providerDomain: accountBinding.providerDomain,
328
232
  };
329
233
  }
330
- recordAuthorizedScope(authorization.attribution.scope);
331
- const result = await baseResolver({
332
- ...request,
333
- connectionUseContext: credentialUseContext,
334
- connectionUseAuthority: authorization.attribution,
335
- connectionRef: {
336
- ...request.connectionRef,
337
- connectionId: authorization.attribution.connectionId,
338
- kind: authorization.connectionKind,
339
- subjectScope: authorization.attribution.scope === "workspace" ? "workspace" : "subject",
340
- },
341
- ...(authorization.attribution.ownerSubjectId
342
- ? { subjectId: authorization.attribution.ownerSubjectId }
343
- : {}),
344
- });
345
- return withProviderRequestAuthorization(result, authorization.attribution);
234
+ return withProviderRequestAuthorization(result);
346
235
  };
347
236
  }
348
237
 
@@ -1,23 +1,18 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { stableJson } from "@opengeni/contracts";
3
3
  import type { ConnectionUseAttribution } from "@opengeni/contracts/connection-authority";
4
- import type { HostMcpAcceptedAuthority } from "@opengeni/contracts/host-mcp-bindings";
5
4
  import type { ResolveConnectionCredentialInput } from "@opengeni/db";
6
5
 
7
6
  /** Metadata only. Callers must obtain authority from a current live fence.
8
7
  * This equality key is never authorization and never replaces physical-use checks. */
9
8
  export function mcpOperationAuthorityDigest(
10
9
  request: ResolveConnectionCredentialInput,
11
- authority:
12
- | {
13
- native: ConnectionUseAttribution;
14
- host?: never;
15
- principal?: { kind: string; subjectId?: string };
16
- }
17
- | { host: HostMcpAcceptedAuthority; native?: never },
10
+ authority: {
11
+ native: ConnectionUseAttribution;
12
+ principal?: { kind: string; subjectId?: string };
13
+ },
18
14
  ): string {
19
15
  const n = authority.native;
20
- const h = authority.host;
21
16
  const ref = request.connectionRef;
22
17
  const metadata = {
23
18
  version: 1,
@@ -31,7 +26,6 @@ export function mcpOperationAuthorityDigest(
31
26
  providerDomain: ref.providerDomain,
32
27
  kind: ref.kind,
33
28
  subjectScope: ref.subjectScope,
34
- hostBinding: ref.hostBinding,
35
29
  scopes: ref.scopes ? [...ref.scopes].sort() : undefined,
36
30
  resource: ref.resource,
37
31
  selectedResources: ref.selectedResources
@@ -40,37 +34,18 @@ export function mcpOperationAuthorityDigest(
40
34
  )
41
35
  : undefined,
42
36
  },
43
- native: n
44
- ? {
45
- organizationId: n.organizationId,
46
- workspaceId: n.workspaceId,
47
- sessionId: n.sessionId,
48
- connectionId: n.connectionId,
49
- connectionGeneration: n.connectionGeneration,
50
- scope: n.scope,
51
- ownerSubjectId: n.ownerSubjectId,
52
- authorityId: n.authorityId,
53
- grantId: n.grantId,
54
- principal: "principal" in authority ? authority.principal : undefined,
55
- }
56
- : undefined,
57
- host: h
58
- ? {
59
- accountId: h.accountId,
60
- workspaceId: h.workspaceId,
61
- targetSessionId: h.targetSessionId,
62
- targetSessionVisibility: h.targetSessionVisibility,
63
- targetSessionAuthorityEpoch: h.targetSessionAuthorityEpoch,
64
- ownerSubjectId: h.ownerSubjectId,
65
- ownerOrganizationMembershipId: h.ownerOrganizationMembershipId,
66
- ownerMembershipAuthorizationRevision: h.ownerMembershipAuthorizationRevision,
67
- bindingId: h.bindingId,
68
- bindingGeneration: h.bindingGeneration,
69
- definition: h.definition,
70
- delegationId: h.delegationId,
71
- delegationGeneration: h.delegationGeneration,
72
- }
73
- : undefined,
37
+ native: {
38
+ organizationId: n.organizationId,
39
+ workspaceId: n.workspaceId,
40
+ sessionId: n.sessionId,
41
+ connectionId: n.connectionId,
42
+ connectionGeneration: n.connectionGeneration,
43
+ scope: n.scope,
44
+ ownerSubjectId: n.ownerSubjectId,
45
+ authorityId: n.authorityId,
46
+ grantId: n.grantId,
47
+ principal: authority.principal,
48
+ },
74
49
  };
75
50
  return createHash("sha256").update(stableJson(metadata)).digest("hex");
76
51
  }
@@ -0,0 +1,112 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { listWorkspaceIdsAfter, reconcileModelCallFacts } from "@opengeni/db";
3
+ import type { ControlActivityServices } from "./types";
4
+
5
+ /** Recent charged calls are rechecked on every run; older gaps use the backfill script. */
6
+ export const MODEL_CALL_FACT_RECONCILE_LOOKBACK_MS = 2 * 60 * 60 * 1_000;
7
+ /** Skip the newest calls so a live fact write is not raced by its own repair. */
8
+ export const MODEL_CALL_FACT_RECONCILE_SETTLE_MS = 10 * 60 * 1_000;
9
+ export const MODEL_CALL_FACT_RECONCILE_WORKSPACE_LIMIT = 500;
10
+ export const MODEL_CALL_FACT_RECONCILE_RUN_BUDGET_MS = 3 * 60 * 1_000;
11
+ const WORKSPACE_PAGE_SIZE = 200;
12
+
13
+ export type ReconcileRecentModelCallFactsResult = {
14
+ workspaces: number;
15
+ missing: number;
16
+ repaired: number;
17
+ unrepaired: number;
18
+ /** Workspaces with more missing facts than one bounded pass examines. */
19
+ truncatedWorkspaces: number;
20
+ failedWorkspaces: number;
21
+ budgetExhausted: boolean;
22
+ };
23
+
24
+ export type ModelCallFactReconcilerOptions = {
25
+ now?: () => number;
26
+ runBudgetMs?: number;
27
+ startAfterWorkspaceId?: () => string;
28
+ reconcile?: typeof reconcileModelCallFacts;
29
+ listWorkspaces?: typeof listWorkspaceIdsAfter;
30
+ };
31
+
32
+ /**
33
+ * Rebuild missing Insights call facts for recently charged model calls. Each
34
+ * workspace pass is bounded and idempotent, one failure never stops the rest,
35
+ * and each run starts at a random workspace so a run that exhausts its budget
36
+ * does not starve the same tail of workspaces every time.
37
+ */
38
+ export function createModelCallFactReconcilerActivities(
39
+ services: () => Promise<ControlActivityServices>,
40
+ options: ModelCallFactReconcilerOptions = {},
41
+ ) {
42
+ const now = options.now ?? Date.now;
43
+ const runBudgetMs = options.runBudgetMs ?? MODEL_CALL_FACT_RECONCILE_RUN_BUDGET_MS;
44
+ const startAfterWorkspaceId = options.startAfterWorkspaceId ?? randomUUID;
45
+ const reconcile = options.reconcile ?? reconcileModelCallFacts;
46
+ const listWorkspaces = options.listWorkspaces ?? listWorkspaceIdsAfter;
47
+
48
+ async function reconcileRecentModelCallFacts(): Promise<ReconcileRecentModelCallFactsResult> {
49
+ const { db, observability } = await services();
50
+ const startedAt = now();
51
+ const until = new Date(startedAt - MODEL_CALL_FACT_RECONCILE_SETTLE_MS);
52
+ const since = new Date(until.getTime() - MODEL_CALL_FACT_RECONCILE_LOOKBACK_MS);
53
+ const result: ReconcileRecentModelCallFactsResult = {
54
+ workspaces: 0,
55
+ missing: 0,
56
+ repaired: 0,
57
+ unrepaired: 0,
58
+ truncatedWorkspaces: 0,
59
+ failedWorkspaces: 0,
60
+ budgetExhausted: false,
61
+ };
62
+ const start = startAfterWorkspaceId();
63
+ let cursor: string | null = start;
64
+ let wrapped = false;
65
+ while (true) {
66
+ const page = await listWorkspaces(db, { afterId: cursor, limit: WORKSPACE_PAGE_SIZE });
67
+ const ids = wrapped ? page.filter((id) => id <= start) : page;
68
+ for (const workspaceId of ids) {
69
+ if (now() - startedAt >= runBudgetMs) {
70
+ result.budgetExhausted = true;
71
+ break;
72
+ }
73
+ result.workspaces += 1;
74
+ try {
75
+ const outcome = await reconcile(db, {
76
+ workspaceId,
77
+ since,
78
+ until,
79
+ limit: MODEL_CALL_FACT_RECONCILE_WORKSPACE_LIMIT,
80
+ });
81
+ result.missing += outcome.missing;
82
+ result.repaired += outcome.repaired;
83
+ result.unrepaired += outcome.unrepaired;
84
+ if (outcome.truncated) result.truncatedWorkspaces += 1;
85
+ } catch (error) {
86
+ result.failedWorkspaces += 1;
87
+ observability.warn("model call fact reconciliation failed for a workspace", {
88
+ workspaceId,
89
+ errorName: error instanceof Error ? error.name : "unknown",
90
+ });
91
+ }
92
+ }
93
+ if (result.budgetExhausted) break;
94
+ if (wrapped && ids.length < page.length) break;
95
+ if (page.length < WORKSPACE_PAGE_SIZE) {
96
+ if (wrapped) break;
97
+ wrapped = true;
98
+ cursor = null;
99
+ continue;
100
+ }
101
+ cursor = page[page.length - 1]!;
102
+ }
103
+ if (result.failedWorkspaces > 0 || result.truncatedWorkspaces > 0 || result.budgetExhausted) {
104
+ observability.warn("model call fact reconciliation incomplete", { ...result });
105
+ } else if (result.missing > 0) {
106
+ observability.info("model call fact reconciliation", { ...result });
107
+ }
108
+ return result;
109
+ }
110
+
111
+ return { reconcileRecentModelCallFacts };
112
+ }
@@ -2,6 +2,7 @@ import type { Agent } from "@openai/agents";
2
2
  import {
3
3
  OPEN_SUFFIX_RUN_STATE_BLOB,
4
4
  approvalIdentifier,
5
+ withPublicApprovalFields,
5
6
  type HumanInputResponse,
6
7
  type SessionEvent,
7
8
  } from "@opengeni/contracts";
@@ -164,7 +165,7 @@ function memberResult(input: {
164
165
  };
165
166
  }
166
167
 
167
- async function resultItemForOpenSuffixMember(input: {
168
+ export async function resultItemForOpenSuffixMember(input: {
168
169
  agent: Agent<any, any>;
169
170
  row: OpenSuffixPendingToolCall;
170
171
  trigger: Pick<SessionEvent, "type" | "payload">;
@@ -177,6 +178,18 @@ async function resultItemForOpenSuffixMember(input: {
177
178
  const name = toolNameFromCallItem(input.row.callItem);
178
179
  const rejected = (): { resultItem: Record<string, unknown>; eventOutput: unknown } => {
179
180
  const payload = input.trigger.payload as { message?: unknown; decision?: unknown };
181
+ if (
182
+ input.row.interruptionKind === "approval" &&
183
+ payload.decision === "reject" &&
184
+ !(typeof payload.message === "string" && payload.message.trim().length > 0)
185
+ ) {
186
+ // This approval was rejected before invocation, not interrupted during execution.
187
+ return memberResult({
188
+ callId: input.row.callId,
189
+ name,
190
+ output: "Tool approval was rejected. This proposed tool call was not executed.",
191
+ });
192
+ }
180
193
  const message =
181
194
  typeof payload.message === "string" && payload.message.trim().length > 0
182
195
  ? payload.message
@@ -380,7 +393,7 @@ export async function settleOpenSuffixResumeIfNeeded(input: {
380
393
  ? [
381
394
  {
382
395
  type: "session.requiresAction" as const,
383
- payload: { approvals: requiresActionApprovals },
396
+ payload: { approvals: requiresActionApprovals.map(withPublicApprovalFields) },
384
397
  },
385
398
  ]
386
399
  : []),