@opengeni/core 3.0.1 → 3.1.2

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 (257) hide show
  1. package/README.md +4 -4
  2. package/dist/access/index.d.ts +131 -7
  3. package/dist/access/service-initiator.d.ts +3 -0
  4. package/dist/application/archived-session-imports.d.ts +6 -0
  5. package/dist/application/composer-submit.d.ts +1 -1
  6. package/dist/application/connect-operation.d.ts +6 -0
  7. package/dist/application/external-workspace-members.d.ts +13 -1
  8. package/dist/application/modal-native-live-declaration-join.d.ts +37 -0
  9. package/dist/application/modal-native-original-configuration.d.ts +79 -0
  10. package/dist/application/modal-native-worker-host-transport.d.ts +297 -0
  11. package/dist/application/new-session-drafts.d.ts +29 -2
  12. package/dist/application/organization-privacy.d.ts +57 -0
  13. package/dist/application/prepared-mcp-connection.d.ts +31 -0
  14. package/dist/application/sandbox-recovery.d.ts +58 -0
  15. package/dist/application/session-commands.d.ts +3 -0
  16. package/dist/application/session-tenancy.d.ts +13 -4
  17. package/dist/billing/agent-run-admission.d.ts +21 -0
  18. package/dist/billing/limits.d.ts +27 -1
  19. package/dist/billing/model-call-admission.d.ts +34 -0
  20. package/dist/billing/model-usage-settlement.d.ts +112 -0
  21. package/dist/canonical-human-identities.js +1 -1
  22. package/dist/{chunk-VCZFFEPB.js → chunk-7T2SZ7BI.js} +16 -2
  23. package/dist/chunk-7T2SZ7BI.js.map +1 -0
  24. package/dist/{chunk-ZYCNLYGH.js → chunk-OMJFIIWK.js} +178 -12
  25. package/dist/chunk-OMJFIIWK.js.map +1 -0
  26. package/dist/{chunk-H7X52RI2.js → chunk-QI2M2B2K.js} +83 -8
  27. package/dist/chunk-QI2M2B2K.js.map +1 -0
  28. package/dist/{chunk-OF65T3PM.js → chunk-TTWTXQB2.js} +94 -6
  29. package/dist/chunk-TTWTXQB2.js.map +1 -0
  30. package/dist/codex-model-availability.d.ts +55 -0
  31. package/dist/default-session-model.d.ts +192 -0
  32. package/dist/dependencies.d.ts +41 -7
  33. package/dist/developer-setup-credential-ceiling.d.ts +10 -0
  34. package/dist/domain/agent-config-resolution.d.ts +49 -0
  35. package/dist/domain/automations.d.ts +8 -1
  36. package/dist/domain/capabilities.d.ts +112 -8
  37. package/dist/domain/connection-account-selection-error.d.ts +8 -0
  38. package/dist/domain/connector-tool-permissions.d.ts +62 -0
  39. package/dist/domain/direct-model-provider.d.ts +3 -0
  40. package/dist/domain/editable-artifacts/agent-application.d.ts +10 -1
  41. package/dist/domain/editable-artifacts/durable-export.d.ts +22 -1
  42. package/dist/domain/editable-artifacts/ports.d.ts +10 -0
  43. package/dist/domain/editable-artifacts/service.d.ts +3 -1
  44. package/dist/domain/editable-artifacts/types.d.ts +1 -1
  45. package/dist/domain/encryption-key.d.ts +12 -0
  46. package/dist/domain/insights-timing.d.ts +16 -0
  47. package/dist/domain/insights-usage.d.ts +27 -0
  48. package/dist/domain/insights.d.ts +21 -3
  49. package/dist/domain/knowledge-files.d.ts +1 -1
  50. package/dist/domain/knowledge-preparation.d.ts +10 -0
  51. package/dist/domain/knowledge-preparation.test.d.ts +1 -0
  52. package/dist/domain/knowledge-search.d.ts +15 -76
  53. package/dist/domain/mcp-account-bindings.d.ts +17 -0
  54. package/dist/domain/mcp-account-routes.d.ts +21 -0
  55. package/dist/domain/native-mcp-connection-admission.d.ts +3 -0
  56. package/dist/domain/organization-integration-catalog.d.ts +15 -0
  57. package/dist/domain/personal-connection-delegations.d.ts +73 -12
  58. package/dist/domain/pr-review.d.ts +121 -6
  59. package/dist/domain/realtime-voice-billing.d.ts +103 -0
  60. package/dist/domain/resources.d.ts +17 -1
  61. package/dist/domain/scheduled-task-access.d.ts +265 -0
  62. package/dist/domain/scheduled-task-account-audit.d.ts +27 -0
  63. package/dist/domain/scheduled-task-audience.d.ts +70 -0
  64. package/dist/domain/scheduled-tasks.d.ts +61 -18
  65. package/dist/domain/session-connection-accounts.d.ts +53 -0
  66. package/dist/domain/session-file-access.d.ts +4 -0
  67. package/dist/domain/session-native-connection-replacement.d.ts +14 -0
  68. package/dist/domain/session-start-timing.d.ts +7 -0
  69. package/dist/domain/session-tool-policy.d.ts +102 -4
  70. package/dist/domain/sessions.d.ts +179 -15
  71. package/dist/domain/site-audience.d.ts +52 -0
  72. package/dist/domain/skills.d.ts +29 -1
  73. package/dist/domain/slack-bot.d.ts +46 -1
  74. package/dist/domain/slack-publication-secret-safety.d.ts +1 -1
  75. package/dist/domain/vaults/index.d.ts +7 -0
  76. package/dist/domain/vaults/mapping.d.ts +25 -0
  77. package/dist/domain/vaults/provider-registry.d.ts +72 -0
  78. package/dist/domain/vaults/provider-sync.d.ts +176 -0
  79. package/dist/domain/vaults/provider-writes.d.ts +57 -0
  80. package/dist/domain/vaults/secret-resolver.d.ts +34 -0
  81. package/dist/domain/vaults/types.d.ts +24 -0
  82. package/dist/domain/vaults/vault-service.d.ts +35 -0
  83. package/dist/domain/voice-input-billing.d.ts +77 -0
  84. package/dist/domain/web-search-billing.d.ts +51 -0
  85. package/dist/domain/workspace-credits.d.ts +7 -0
  86. package/dist/editable-artifact-live/application.d.ts +2 -1
  87. package/dist/editable-artifact-live/server.d.ts +5 -1
  88. package/dist/editable-artifact-live/ticket.d.ts +2 -1
  89. package/dist/editable-artifact-live/types.d.ts +9 -0
  90. package/dist/editable-artifact-live.js +2 -2
  91. package/dist/editable-artifacts.js +10 -2
  92. package/dist/goal-admission.d.ts +627 -0
  93. package/dist/host-session-usage-limit.d.ts +15 -0
  94. package/dist/index.d.ts +43 -8
  95. package/dist/index.js +22682 -11743
  96. package/dist/index.js.map +1 -1
  97. package/dist/managed-session.d.ts +110 -7
  98. package/dist/model-call.d.ts +58 -0
  99. package/dist/model-catalog.d.ts +29 -3
  100. package/dist/model-provider-credentials.d.ts +43 -0
  101. package/dist/product-usage-metrics.d.ts +30 -0
  102. package/dist/rigs/index.d.ts +1 -13
  103. package/dist/sandbox/fleet.d.ts +58 -4
  104. package/dist/sandbox/routing.d.ts +42 -2
  105. package/dist/sandbox/runtime-settings.d.ts +27 -17
  106. package/dist/session-authorization.d.ts +10 -0
  107. package/dist/session-usage-limit-metrics.d.ts +60 -0
  108. package/dist/session-usage-limits.d.ts +89 -0
  109. package/dist/site-agent-clamp.d.ts +71 -0
  110. package/dist/site-session-origin.d.ts +39 -2
  111. package/dist/transcription.d.ts +120 -3
  112. package/dist/turn-surface.d.ts +20 -0
  113. package/dist/user-presence.d.ts +32 -0
  114. package/dist/workflow-wake-contract.d.ts +13 -1
  115. package/package.json +15 -22
  116. package/src/access/index.ts +961 -38
  117. package/src/access/service-initiator.ts +50 -0
  118. package/src/application/api-integration-servers.ts +9 -0
  119. package/src/application/archived-session-imports.ts +161 -0
  120. package/src/application/composer-submit.ts +3 -2
  121. package/src/application/connect-authority.ts +3 -3
  122. package/src/application/connect-operation.ts +37 -3
  123. package/src/application/external-continuation.ts +20 -3
  124. package/src/application/external-link-work-admission.ts +3 -0
  125. package/src/application/external-workspace-members.ts +51 -3
  126. package/src/application/modal-native-live-declaration-join.ts +196 -0
  127. package/src/application/modal-native-original-configuration.ts +261 -0
  128. package/src/application/modal-native-worker-host-transport.ts +263 -0
  129. package/src/application/new-session-drafts.ts +172 -55
  130. package/src/application/organization-privacy.ts +161 -0
  131. package/src/application/prepared-mcp-connection.ts +133 -0
  132. package/src/application/sandbox-recovery.ts +78 -0
  133. package/src/application/session-commands.ts +73 -1
  134. package/src/application/session-mcp-credential-rotation.ts +61 -18
  135. package/src/application/session-tenancy.ts +262 -44
  136. package/src/application/user-resource-grants.ts +1 -29
  137. package/src/billing/agent-run-admission.ts +117 -0
  138. package/src/billing/limits.ts +138 -14
  139. package/src/billing/model-call-admission.ts +118 -0
  140. package/src/billing/model-usage-settlement.ts +472 -0
  141. package/src/codex-model-availability.ts +264 -0
  142. package/src/default-session-model.ts +692 -0
  143. package/src/dependencies.ts +48 -6
  144. package/src/developer-setup-credential-ceiling.ts +60 -0
  145. package/src/domain/agent-config-resolution.ts +148 -0
  146. package/src/domain/automations.ts +76 -2
  147. package/src/domain/capabilities.ts +830 -181
  148. package/src/domain/connection-account-selection-error.ts +19 -0
  149. package/src/domain/connector-tool-permissions.ts +572 -0
  150. package/src/domain/direct-model-provider.ts +62 -0
  151. package/src/domain/editable-artifacts/agent-application.ts +44 -6
  152. package/src/domain/editable-artifacts/durable-export.ts +100 -5
  153. package/src/domain/editable-artifacts/in-memory.ts +6 -0
  154. package/src/domain/editable-artifacts/ports.ts +11 -0
  155. package/src/domain/editable-artifacts/service.ts +20 -0
  156. package/src/domain/editable-artifacts/types.ts +1 -1
  157. package/src/domain/encryption-key.ts +26 -0
  158. package/src/domain/file-owner.ts +16 -1
  159. package/src/domain/github-action-policies.ts +1 -1
  160. package/src/domain/github-skill-source.ts +3 -1
  161. package/src/domain/insights-timing.ts +95 -0
  162. package/src/domain/insights-usage.ts +67 -0
  163. package/src/domain/insights.ts +167 -43
  164. package/src/domain/knowledge-files.ts +10 -3
  165. package/src/domain/knowledge-messages.ts +1 -0
  166. package/src/domain/knowledge-preparation.test.ts +276 -0
  167. package/src/domain/knowledge-preparation.ts +147 -0
  168. package/src/domain/knowledge-search.test.ts +617 -5
  169. package/src/domain/knowledge-search.ts +240 -23
  170. package/src/domain/mcp-account-bindings.ts +169 -0
  171. package/src/domain/mcp-account-routes.ts +152 -0
  172. package/src/domain/native-mcp-connection-admission.ts +14 -0
  173. package/src/domain/organization-integration-catalog.ts +88 -0
  174. package/src/domain/personal-connection-delegations.ts +399 -257
  175. package/src/domain/pr-review.ts +24 -8
  176. package/src/domain/realtime-voice-billing.ts +425 -0
  177. package/src/domain/resources.ts +50 -4
  178. package/src/domain/scheduled-task-access.ts +1089 -0
  179. package/src/domain/scheduled-task-account-audit.ts +90 -0
  180. package/src/domain/scheduled-task-audience.ts +249 -0
  181. package/src/domain/scheduled-tasks.ts +1313 -374
  182. package/src/domain/session-connection-accounts.ts +239 -0
  183. package/src/domain/session-file-access.ts +47 -0
  184. package/src/domain/session-native-connection-replacement.ts +64 -0
  185. package/src/domain/session-start-timing.ts +60 -0
  186. package/src/domain/session-tool-policy.ts +489 -14
  187. package/src/domain/sessions.ts +2254 -717
  188. package/src/domain/site-audience.ts +130 -0
  189. package/src/domain/skills.ts +9 -0
  190. package/src/domain/slack-bot.ts +141 -4
  191. package/src/domain/slack-publication-secret-safety.ts +1 -1
  192. package/src/domain/vaults/index.ts +19 -0
  193. package/src/domain/vaults/mapping.ts +190 -0
  194. package/src/domain/vaults/provider-registry.ts +201 -0
  195. package/src/domain/vaults/provider-sync.ts +531 -0
  196. package/src/domain/vaults/provider-writes.ts +288 -0
  197. package/src/domain/vaults/secret-resolver.ts +464 -0
  198. package/src/domain/vaults/types.ts +26 -0
  199. package/src/domain/vaults/vault-service.ts +1316 -0
  200. package/src/domain/voice-input-billing.ts +330 -0
  201. package/src/domain/web-search-billing.ts +169 -0
  202. package/src/domain/workspace-credits.ts +13 -0
  203. package/src/editable-artifact-live/application.ts +8 -1
  204. package/src/editable-artifact-live/server.ts +154 -6
  205. package/src/editable-artifact-live/ticket.ts +86 -1
  206. package/src/editable-artifact-live/types.ts +10 -0
  207. package/src/goal-admission.ts +220 -0
  208. package/src/host-session-usage-limit.ts +25 -0
  209. package/src/index.ts +75 -11
  210. package/src/managed-session.ts +137 -6
  211. package/src/model-call.ts +79 -0
  212. package/src/model-catalog.ts +345 -91
  213. package/src/model-provider-credentials.ts +394 -0
  214. package/src/product-usage-metrics.ts +101 -0
  215. package/src/rigs/index.ts +23 -111
  216. package/src/sandbox/fleet.ts +253 -28
  217. package/src/sandbox/routing.ts +204 -43
  218. package/src/sandbox/runtime-settings.ts +159 -85
  219. package/src/session-authorization.ts +172 -23
  220. package/src/session-usage-limit-metrics.ts +95 -0
  221. package/src/session-usage-limits.ts +415 -0
  222. package/src/site-agent-clamp.ts +226 -0
  223. package/src/site-session-origin.ts +84 -5
  224. package/src/transcription.ts +123 -4
  225. package/src/turn-surface.ts +47 -0
  226. package/src/user-presence.ts +134 -0
  227. package/src/workflow-wake-contract.ts +14 -1
  228. package/dist/application/host-mcp-owner.d.ts +0 -6
  229. package/dist/application/host-mcp-resolvers.d.ts +0 -13
  230. package/dist/chunk-5WZYFL45.js +0 -266
  231. package/dist/chunk-5WZYFL45.js.map +0 -1
  232. package/dist/chunk-H7X52RI2.js.map +0 -1
  233. package/dist/chunk-OF65T3PM.js.map +0 -1
  234. package/dist/chunk-VCZFFEPB.js.map +0 -1
  235. package/dist/chunk-ZYCNLYGH.js.map +0 -1
  236. package/dist/domain/connection-authority.d.ts +0 -97
  237. package/dist/domain/connection-authority.js +0 -169
  238. package/dist/domain/connection-authority.js.map +0 -1
  239. package/dist/domain/environments.d.ts +0 -33
  240. package/dist/domain/host-mcp-authority-source-admission.d.ts +0 -10
  241. package/dist/domain/host-mcp-task-admission.d.ts +0 -18
  242. package/dist/domain/packs.d.ts +0 -59
  243. package/dist/domain/product-integration-pack.d.ts +0 -44
  244. package/dist/domain/product-integration-skill.gen.d.ts +0 -5
  245. package/dist/remote-mcp-credentials.d.ts +0 -23
  246. package/dist/remote-mcp-credentials.js +0 -11
  247. package/dist/remote-mcp-credentials.js.map +0 -1
  248. package/src/application/host-mcp-owner.ts +0 -41
  249. package/src/application/host-mcp-resolvers.ts +0 -84
  250. package/src/domain/connection-authority.ts +0 -329
  251. package/src/domain/environments.ts +0 -120
  252. package/src/domain/host-mcp-authority-source-admission.ts +0 -25
  253. package/src/domain/host-mcp-task-admission.ts +0 -121
  254. package/src/domain/packs.ts +0 -699
  255. package/src/domain/product-integration-pack.ts +0 -42
  256. package/src/domain/product-integration-skill.gen.ts +0 -52
  257. package/src/remote-mcp-credentials.ts +0 -361
@@ -0,0 +1,263 @@
1
+ import { createHash, createHmac, timingSafeEqual } from "node:crypto";
2
+ import { types } from "node:util";
3
+ import type { Settings } from "@opengeni/config";
4
+ import {
5
+ ModalNativeConfigurationCaptureRefV2,
6
+ ModalNativeOriginalScopeV2,
7
+ ModalNativeProofGrantRefV2,
8
+ } from "@opengeni/contracts/modal-native-proof-v2";
9
+ import { z } from "zod";
10
+
11
+ // Private transport only. This module is not an ordinary access-token entry,
12
+ // native acquisition issuer, custody authorizer, public registrar or effect CAS.
13
+ // A successful MAC authenticates this host transport, NOT the named human,
14
+ // live origin, grant, configuration, provider response or physical settlement.
15
+ const PREFIX = "ogmnp2_";
16
+ const SUBKEY_DOMAIN = "opengeni:modal-native-worker-host:transport:subkey:v2";
17
+ const MESSAGE_DOMAIN = "opengeni:modal-native-worker-host:transport:message:v2\0";
18
+ const TRANSPORT_TTL_SECONDS = 60;
19
+ const MAX_BODY_BYTES = 4 * 1024 * 1024;
20
+ const MAX_ENVELOPE_BYTES = 16_384;
21
+ const typedArrayPrototype = Object.getPrototypeOf(Uint8Array.prototype);
22
+ const nativeByteLength = Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteLength")!.get!;
23
+ const nativeByteOffset = Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteOffset")!.get!;
24
+ const nativeBuffer = Object.getOwnPropertyDescriptor(typedArrayPrototype, "buffer")!.get!;
25
+ const uuid = z.string().uuid();
26
+ const revision = z.number().int().positive().safe();
27
+ const base = {
28
+ protocol: z.literal("opengeni-modal-native-worker-host-proof"),
29
+ version: z.literal(2),
30
+ principalKind: z.literal("worker_host"),
31
+ purpose: z.literal("original-native-custody-proof"),
32
+ scope: ModalNativeOriginalScopeV2,
33
+ configurationCaptureRef: ModalNativeConfigurationCaptureRefV2,
34
+ proofGrantRef: ModalNativeProofGrantRefV2,
35
+ requestId: uuid,
36
+ };
37
+ const acquisition = z
38
+ .object({
39
+ claimId: uuid,
40
+ nonce: uuid,
41
+ claimRevision: revision,
42
+ recordRevision: revision,
43
+ captureId: uuid,
44
+ acquisitionId: uuid,
45
+ })
46
+ .strict();
47
+ const intent = z.discriminatedUnion("action", [
48
+ z.object({ ...base, action: z.literal("declare_preparation") }).strict(),
49
+ z
50
+ .object({ ...base, action: z.literal("claim_namespace_read"), recordRevision: revision })
51
+ .strict(),
52
+ z
53
+ .object({ ...base, action: z.literal("claim_original_command_read"), recordRevision: revision })
54
+ .strict(),
55
+ z.object({ ...base, action: z.literal("capture_namespace_binding"), acquisition }).strict(),
56
+ z.object({ ...base, action: z.literal("capture_original_page"), acquisition }).strict(),
57
+ z.object({ ...base, action: z.literal("reconcile_same_capture"), acquisition }).strict(),
58
+ z.object({ ...base, action: z.literal("settle_original_acquisition"), acquisition }).strict(),
59
+ ]);
60
+ const timing = z
61
+ .object({
62
+ bodySha256: z.string().regex(/^[a-f0-9]{64}$/u),
63
+ issuedAt: z.number().int().nonnegative().safe(),
64
+ expiresAt: z.number().int().positive().safe(),
65
+ })
66
+ .strict();
67
+ type Intent = z.infer<typeof intent>;
68
+ export type NativeWorkerHostTransportDescriptionV2 = Intent & z.infer<typeof timing>;
69
+
70
+ declare const transportBrand: unique symbol;
71
+ export type NativeWorkerHostTransport = Readonly<{ [transportBrand]: true }>;
72
+ declare const verifiedBrand: unique symbol;
73
+ export type VerifiedNativeWorkerHostTransport = Readonly<{ [verifiedBrand]: true }>;
74
+ const transports = new WeakMap<NativeWorkerHostTransport, Buffer>();
75
+ const verified = new WeakMap<
76
+ VerifiedNativeWorkerHostTransport,
77
+ {
78
+ transport: NativeWorkerHostTransport;
79
+ description: NativeWorkerHostTransportDescriptionV2;
80
+ }
81
+ >();
82
+
83
+ /** Inspect before schema parsing; getters/Proxies must not execute inside the
84
+ * signing boundary. This is JSON correlation, never an authority constructor. */
85
+ function ownJson(value: unknown, depth = 0): void {
86
+ if (depth > 12) throw new Error("Unsupported native host transport input");
87
+ if (value === null || typeof value === "string" || typeof value === "boolean") return;
88
+ if (typeof value === "number" && Number.isFinite(value) && !Object.is(value, -0)) return;
89
+ if (typeof value !== "object" || types.isProxy(value))
90
+ throw new Error("Unsupported native host transport input");
91
+ const array = Array.isArray(value);
92
+ const prototype = Object.getPrototypeOf(value);
93
+ if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null)
94
+ throw new Error("Unsupported native host transport input");
95
+ const descriptors = Object.getOwnPropertyDescriptors(value);
96
+ const keys = Reflect.ownKeys(descriptors).filter((key) => !(array && key === "length"));
97
+ if (array && keys.length !== (value as unknown[]).length)
98
+ throw new Error("Unsupported native host transport input");
99
+ for (const key of keys) {
100
+ if (typeof key !== "string" || (array && !/^(0|[1-9][0-9]*)$/u.test(key)))
101
+ throw new Error("Unsupported native host transport input");
102
+ const descriptor = descriptors[key]!;
103
+ if (!descriptor.enumerable || !("value" in descriptor))
104
+ throw new Error("Unsupported native host transport input");
105
+ ownJson(descriptor.value, depth + 1);
106
+ }
107
+ }
108
+
109
+ function bodyHash(body: Uint8Array): string {
110
+ if (types.isProxy(body) || !types.isUint8Array(body))
111
+ throw new Error("Unsupported native host transport body");
112
+ // Native getters ignore caller-owned byteLength/buffer/offset overrides. Reject
113
+ // shared backing storage: its bytes could change concurrently during the copy.
114
+ const length = nativeByteLength.call(body) as number;
115
+ const backing = nativeBuffer.call(body) as ArrayBuffer;
116
+ if (length > MAX_BODY_BYTES || !types.isArrayBuffer(backing))
117
+ throw new Error("Unsupported native host transport body");
118
+ const snapshot = new Uint8Array(length);
119
+ try {
120
+ snapshot.set(new Uint8Array(backing, nativeByteOffset.call(body) as number, length));
121
+ return createHash("sha256").update(snapshot).digest("hex");
122
+ } catch {
123
+ throw new Error("Unsupported native host transport body");
124
+ } finally {
125
+ snapshot.fill(0);
126
+ }
127
+ }
128
+
129
+ function keyFor(transport: NativeWorkerHostTransport): Buffer {
130
+ const key = transports.get(transport);
131
+ if (!key) throw new Error("Native host transport is unavailable");
132
+ return key;
133
+ }
134
+
135
+ function mac(key: Buffer, encoded: string): Buffer {
136
+ return createHmac("sha256", key).update(MESSAGE_DOMAIN).update(PREFIX).update(encoded).digest();
137
+ }
138
+
139
+ /** Host startup only. Deliberately DOES NOT call resolveFirstPartyDelegationSecret:
140
+ * neither configured access-key substitution nor the public local/test fallback
141
+ * can select this new purpose root. Missing explicit deployment secret is OFF.
142
+ * No variable lookup, provider request, config mutation or persistence occurs. */
143
+ export function configuredNativeWorkerHostTransport(
144
+ settings: Pick<Settings, "delegationSecret">,
145
+ ): NativeWorkerHostTransport | null {
146
+ if (!settings || typeof settings !== "object" || types.isProxy(settings)) return null;
147
+ const descriptor = Object.getOwnPropertyDescriptor(settings, "delegationSecret");
148
+ if (!descriptor || !("value" in descriptor) || typeof descriptor.value !== "string") return null;
149
+ const secret = descriptor.value.trim();
150
+ if (!secret) return null;
151
+ const transient = Buffer.from(secret, "utf8");
152
+ try {
153
+ const key = createHmac("sha256", transient).update(SUBKEY_DOMAIN).digest();
154
+ const handle = Object.freeze(Object.create(null)) as NativeWorkerHostTransport;
155
+ transports.set(handle, key);
156
+ return handle;
157
+ } finally {
158
+ transient.fill(0);
159
+ }
160
+ }
161
+
162
+ /** Release only this transport's derived-key copy, not a config/grant/custody
163
+ * lifecycle or observation slot. Outstanding native I/O needs its own join. */
164
+ export function disposeNativeWorkerHostTransport(transport: NativeWorkerHostTransport): void {
165
+ const key = transports.get(transport);
166
+ if (key) key.fill(0);
167
+ transports.delete(transport);
168
+ }
169
+
170
+ /** Private worker/core transport serialization. The caller MUST first establish
171
+ * canonical original-host admission. This helper cannot establish that admission
172
+ * and is intentionally not exported from the core package's public entrypoints. */
173
+ export function signNativeWorkerHostTransport(
174
+ transport: NativeWorkerHostTransport,
175
+ input: Intent,
176
+ body: Uint8Array,
177
+ nowSeconds = Math.floor(Date.now() / 1000),
178
+ ): string {
179
+ const key = keyFor(transport);
180
+ ownJson(input);
181
+ const parsed = intent.safeParse(input);
182
+ const clock = z.number().int().nonnegative().safe().safeParse(nowSeconds);
183
+ if (!parsed.success || !clock.success) throw new Error("Unsupported native host transport input");
184
+ const timed = timing.safeParse({
185
+ bodySha256: bodyHash(body),
186
+ issuedAt: clock.data,
187
+ expiresAt: clock.data + TRANSPORT_TTL_SECONDS,
188
+ });
189
+ if (!timed.success) throw new Error("Unsupported native host transport input");
190
+ const description: NativeWorkerHostTransportDescriptionV2 = {
191
+ ...parsed.data,
192
+ ...timed.data,
193
+ };
194
+ const encoded = Buffer.from(JSON.stringify(description), "utf8").toString("base64url");
195
+ if (PREFIX.length + encoded.length + 1 + 43 > MAX_ENVELOPE_BYTES)
196
+ throw new Error("Unsupported native host transport input");
197
+ return `${PREFIX}${encoded}.${mac(key, encoded).toString("base64url")}`;
198
+ }
199
+
200
+ /** Only host transport authenticity is verified. A later narrow authorizer must
201
+ * independently source-join original live/maintenance authority, configuration,
202
+ * grant, claim, acquisition, body fields/cursors and exact action-specific CAS. */
203
+ export function verifyNativeWorkerHostTransport(
204
+ transport: NativeWorkerHostTransport,
205
+ envelope: string,
206
+ body: Uint8Array,
207
+ nowSeconds = Math.floor(Date.now() / 1000),
208
+ ): VerifiedNativeWorkerHostTransport | null {
209
+ const key = keyFor(transport);
210
+ if (
211
+ typeof envelope !== "string" ||
212
+ envelope.length > MAX_ENVELOPE_BYTES ||
213
+ !envelope.startsWith(PREFIX)
214
+ )
215
+ return null;
216
+ const pieces = envelope.slice(PREFIX.length).split(".");
217
+ if (pieces.length !== 2) return null;
218
+ const [encoded, signature] = pieces as [string, string];
219
+ if (!/^[A-Za-z0-9_-]+$/u.test(encoded) || !/^[A-Za-z0-9_-]{43}$/u.test(signature)) return null;
220
+ const received = Buffer.from(signature, "base64url");
221
+ if (received.length !== 32 || received.toString("base64url") !== signature) return null;
222
+ if (!timingSafeEqual(received, mac(key, encoded))) return null;
223
+ try {
224
+ const decoded = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8")) as Record<
225
+ string,
226
+ unknown
227
+ >;
228
+ const { bodySha256, issuedAt, expiresAt, ...rawIntent } = decoded;
229
+ const description = {
230
+ ...intent.parse(rawIntent),
231
+ ...timing.parse({ bodySha256, issuedAt, expiresAt }),
232
+ };
233
+ if (
234
+ !Number.isSafeInteger(nowSeconds) ||
235
+ nowSeconds < 0 ||
236
+ description.issuedAt > nowSeconds ||
237
+ description.expiresAt <= nowSeconds ||
238
+ description.expiresAt - description.issuedAt !== TRANSPORT_TTL_SECONDS ||
239
+ description.bodySha256 !== bodyHash(body) ||
240
+ Buffer.from(JSON.stringify(description), "utf8").toString("base64url") !== encoded
241
+ )
242
+ return null;
243
+ const proof = Object.freeze(Object.create(null)) as VerifiedNativeWorkerHostTransport;
244
+ verified.set(proof, { transport, description });
245
+ return proof;
246
+ } catch {
247
+ return null;
248
+ }
249
+ }
250
+
251
+ /** Request-local inspection of a previously verified MAC, NOT current admission.
252
+ * The narrow host authorizer must still recheck expiry and current authority/CAS.
253
+ * Copying a description, token or ref cannot mint grant or native-I/O authority. */
254
+ export function nativeWorkerHostTransportDescription(
255
+ transport: NativeWorkerHostTransport,
256
+ proof: VerifiedNativeWorkerHostTransport,
257
+ ): NativeWorkerHostTransportDescriptionV2 {
258
+ keyFor(transport);
259
+ const record = verified.get(proof);
260
+ if (!record || record.transport !== transport)
261
+ throw new Error("Native host transport proof is unavailable");
262
+ return structuredClone(record.description);
263
+ }
@@ -11,8 +11,8 @@ import {
11
11
  getEnrollment,
12
12
  getRig,
13
13
  getSandbox,
14
- getVariableSet,
15
14
  NewSessionDraftAccessError,
15
+ newSessionDraftModelProvided,
16
16
  newSessionDraftSelectedProjectChannelId,
17
17
  newSessionDraftToolsProvided,
18
18
  newSessionSelectionHistory,
@@ -31,12 +31,14 @@ import {
31
31
  validateGitHubRepositorySelection,
32
32
  validateToolRefs,
33
33
  } from "../domain/resources";
34
- import {
35
- hasPermission,
36
- externalAttributionForAuthorization,
37
- type AccessGrantAuthorization,
38
- } from "../access";
34
+ import { hasVerifiedOwningUserAuthorization, type AccessGrantAuthorization } from "../access";
39
35
  import { assertConfiguredModel, assertWorkspaceModelPolicyAllows } from "../domain/sessions";
36
+ import {
37
+ resolveCallerWorkspaceModelSelections,
38
+ resolveDefaultSessionModel,
39
+ workspaceCatalogListsModel,
40
+ } from "../default-session-model";
41
+ import { canonicalizeConfiguredModelId, type Settings } from "@opengeni/config";
40
42
 
41
43
  type NewSessionDraftDependencies = Pick<AppDependencies, "settings" | "db" | "objectStorage">;
42
44
 
@@ -58,6 +60,9 @@ function mapNewSessionDraft(
58
60
  model: row.model,
59
61
  reasoningEffort: row.reasoningEffort,
60
62
  latencyMode: row.latencyMode,
63
+ ...(newSessionDraftModelProvided(row) !== undefined
64
+ ? { modelProvided: newSessionDraftModelProvided(row) }
65
+ : {}),
61
66
  ...(selectedProjectChannelId !== undefined ? { selectedProjectChannelId } : {}),
62
67
  options: publicNewSessionDraftOptions(row),
63
68
  selectionHistory: newSessionSelectionHistory(row),
@@ -65,15 +70,108 @@ function mapNewSessionDraft(
65
70
  });
66
71
  }
67
72
 
73
+ /**
74
+ * Whether a stored draft's model policy was the person's choice. A row written
75
+ * before the marker existed (or by an older client) counts as a choice unless
76
+ * it is exactly the deployment default policy (model, reasoning, standard
77
+ * speed), which is what an untouched composer used to save.
78
+ */
79
+ export function draftModelProvided(
80
+ settings: Settings,
81
+ draft: Pick<NewSessionDraftValue, "model" | "reasoningEffort" | "latencyMode" | "modelProvided">,
82
+ ): boolean {
83
+ if (draft.modelProvided !== undefined) return draft.modelProvided;
84
+ return !(
85
+ canonicalizeConfiguredModelId(settings, draft.model) ===
86
+ canonicalizeConfiguredModelId(settings, settings.openaiModel) &&
87
+ draft.reasoningEffort === settings.openaiReasoningEffort &&
88
+ draft.latencyMode === "standard"
89
+ );
90
+ }
91
+
92
+ /** The resolved new-chat default for this actor and workspace. */
93
+ async function actorDefaultModel(
94
+ deps: Pick<NewSessionDraftDependencies, "db" | "settings">,
95
+ grant: AccessGrant,
96
+ workspaceId: string,
97
+ ) {
98
+ return await resolveDefaultSessionModel(deps.db, deps.settings, {
99
+ accountId: grant.accountId,
100
+ workspaceId,
101
+ subjectId: grant.subjectId,
102
+ });
103
+ }
104
+
105
+ /**
106
+ * False only when this workspace's catalog demonstrably no longer lists the
107
+ * model. A catalog read failure is not proof of removal, so it keeps the
108
+ * saved choice.
109
+ */
110
+ async function workspaceCatalogMayHaveModel(
111
+ deps: Pick<NewSessionDraftDependencies, "db" | "settings">,
112
+ grant: AccessGrant,
113
+ workspaceId: string,
114
+ modelId: string,
115
+ ): Promise<boolean> {
116
+ try {
117
+ const selections = await resolveCallerWorkspaceModelSelections(deps.db, deps.settings, {
118
+ accountId: grant.accountId,
119
+ workspaceId,
120
+ subjectId: grant.subjectId,
121
+ });
122
+ return workspaceCatalogListsModel(
123
+ selections,
124
+ canonicalizeConfiguredModelId(deps.settings, modelId),
125
+ );
126
+ } catch {
127
+ return true;
128
+ }
129
+ }
130
+
131
+ type NewSessionDraftReadOptions = {
132
+ /**
133
+ * Project a draft that follows the default onto today's resolved default.
134
+ * Callers that only reuse a chosen model (Slack defaults) skip the
135
+ * resolution; session creation resolves the default itself.
136
+ */
137
+ projectDefaultModel?: boolean;
138
+ };
139
+
68
140
  async function hydrateNewSessionDraft(
69
141
  deps: Pick<NewSessionDraftDependencies, "db" | "settings">,
70
142
  grant: AccessGrant,
71
143
  workspaceId: string,
72
144
  row: Awaited<ReturnType<typeof getNewSessionDraftInTransaction>>,
145
+ readOptions: NewSessionDraftReadOptions = {},
73
146
  ): Promise<NewSessionDraftValue | null> {
74
147
  if (!row) return null;
75
- const mapped = mapNewSessionDraft(row);
76
- if (!mapped) return null;
148
+ const stored = mapNewSessionDraft(row);
149
+ if (!stored) return null;
150
+ // A draft that follows the default is projected onto today's default, so a
151
+ // later subscription connect or credit purchase replaces an untouched free
152
+ // model. The stored row is unchanged until the person saves again.
153
+ let modelProvided = draftModelProvided(deps.settings, stored);
154
+ if (
155
+ modelProvided &&
156
+ readOptions.projectDefaultModel !== false &&
157
+ !(await workspaceCatalogMayHaveModel(deps, grant, workspaceId, stored.model))
158
+ ) {
159
+ // The saved choice left this workspace's catalog (a removed provider or a
160
+ // retired model). A new chat must not start on a model that cannot run, so
161
+ // the draft follows today's default until the person picks again. A model
162
+ // that is listed but temporarily unavailable keeps the person's choice.
163
+ modelProvided = false;
164
+ }
165
+ let mapped: NewSessionDraftValue = { ...stored, modelProvided };
166
+ if (!modelProvided && readOptions.projectDefaultModel !== false) {
167
+ const resolved = await actorDefaultModel(deps, grant, workspaceId);
168
+ mapped = {
169
+ ...mapped,
170
+ model: resolved.model,
171
+ reasoningEffort: resolved.reasoningEffort,
172
+ latencyMode: resolved.model === stored.model ? stored.latencyMode : "standard",
173
+ };
174
+ }
77
175
  const runtimeSettings = await settingsWithEnabledCapabilityMcpServers(
78
176
  deps.db,
79
177
  workspaceId,
@@ -112,35 +210,11 @@ async function hydrateNewSessionDraft(
112
210
  }
113
211
 
114
212
  const options = { ...mapped.options };
115
- const variableSetIds = options.variableSetIds ?? [];
116
- if (variableSetIds.length > 0) {
117
- const selectionsAuthorized =
118
- hasPermission(grant.permissions, "variable-sets:attach") &&
119
- hasPermission(grant.permissions, "variable-sets:use") &&
120
- (
121
- await Promise.all(
122
- variableSetIds.map((variableSetId) =>
123
- getVariableSet(
124
- deps.db,
125
- { accountId: grant.accountId, workspaceId, subjectId: grant.subjectId },
126
- variableSetId,
127
- ),
128
- ),
129
- )
130
- ).every(Boolean);
131
- if (selectionsAuthorized) {
132
- options.variableSetIds = variableSetIds;
133
- options.variableSetId = variableSetIds[variableSetIds.length - 1];
134
- } else {
135
- delete options.variableSetIds;
136
- delete options.variableSetId;
137
- }
138
- } else {
139
- delete options.variableSetIds;
140
- delete options.variableSetId;
141
- }
213
+ // Variable Sets were replaced by vaults: stale draft selections are dropped.
214
+ delete options.variableSetIds;
215
+ delete options.variableSetId;
142
216
  if (options.rigId) {
143
- const rig = await getRig(deps.db, workspaceId, options.rigId);
217
+ const rig = await getRig(deps.db, { ...grant, workspaceId }, options.rigId);
144
218
  if (!rig?.activeVersion) delete options.rigId;
145
219
  }
146
220
  if (options.targetSandboxId) {
@@ -190,6 +264,7 @@ async function getActorNewSessionDraftInFileScope(
190
264
  deps: Pick<NewSessionDraftDependencies, "settings" | "db">,
191
265
  grant: AccessGrant,
192
266
  workspaceId: string,
267
+ readOptions: NewSessionDraftReadOptions = {},
193
268
  ): Promise<NewSessionDraftValue> {
194
269
  const row = await withWorkspaceSubjectRls(deps.db, workspaceId, grant.subjectId, (scoped) =>
195
270
  getNewSessionDraftInTransaction(scoped, {
@@ -197,21 +272,26 @@ async function getActorNewSessionDraftInFileScope(
197
272
  subjectId: grant.subjectId,
198
273
  }),
199
274
  );
200
- return (
201
- (await hydrateNewSessionDraft(deps, grant, workspaceId, row)) ?? {
202
- revision: 0,
203
- text: "",
204
- resources: [],
205
- tools: [],
206
- toolsProvided: false,
207
- model: deps.settings.openaiModel,
208
- reasoningEffort: deps.settings.openaiReasoningEffort,
209
- latencyMode: "standard",
210
- options: {},
211
- selectionHistory: { projects: [] },
212
- updatedAt: null,
213
- }
214
- );
275
+ const hydrated = await hydrateNewSessionDraft(deps, grant, workspaceId, row, readOptions);
276
+ if (hydrated) return hydrated;
277
+ const resolved =
278
+ readOptions.projectDefaultModel === false
279
+ ? { model: deps.settings.openaiModel, reasoningEffort: deps.settings.openaiReasoningEffort }
280
+ : await actorDefaultModel(deps, grant, workspaceId);
281
+ return {
282
+ revision: 0,
283
+ text: "",
284
+ resources: [],
285
+ tools: [],
286
+ toolsProvided: false,
287
+ model: resolved.model,
288
+ reasoningEffort: resolved.reasoningEffort,
289
+ latencyMode: "standard",
290
+ modelProvided: false,
291
+ options: {},
292
+ selectionHistory: { projects: [] },
293
+ updatedAt: null,
294
+ };
215
295
  }
216
296
 
217
297
  /**
@@ -283,6 +363,7 @@ async function saveActorNewSessionDraftInFileScope(
283
363
  model: input.model,
284
364
  reasoningEffort: input.reasoningEffort,
285
365
  latencyMode: input.latencyMode,
366
+ ...(input.modelProvided !== undefined ? { modelProvided: input.modelProvided } : {}),
286
367
  ...(input.selectedProjectChannelId !== undefined
287
368
  ? { selectedProjectChannelId: input.selectedProjectChannelId }
288
369
  : {}),
@@ -295,14 +376,18 @@ async function saveActorNewSessionDraftInFileScope(
295
376
  // A managed human's own personal workspace has no membership row at
296
377
  // all, so the human-removal fence above must fall back to the
297
378
  // organization-membership pointer for them — and only for the
298
- // canonical managed-cookie session that owns it.
379
+ // verified owning-user authorization for this exact grant.
299
380
  personalWorkspaceOwnerException:
300
381
  canonicalManagedHumanSession ||
301
- externalAttributionForAuthorization(externalAuthorization, grant) !== null,
382
+ (externalAuthorization?.grant === grant &&
383
+ hasVerifiedOwningUserAuthorization(externalAuthorization)),
302
384
  }),
303
385
  ),
304
386
  );
305
- return mapNewSessionDraft(saved)!;
387
+ // Report the same model-policy marker a read of this row reports, so the
388
+ // save response and the next GET agree for old and new clients alike.
389
+ const mapped = mapNewSessionDraft(saved)!;
390
+ return { ...mapped, modelProvided: draftModelProvided(deps.settings, mapped) };
306
391
  } catch (error) {
307
392
  if (error instanceof NewSessionDraftAccessError) {
308
393
  throw new HTTPException(403, { message: error.message });
@@ -316,12 +401,13 @@ export async function getActorNewSessionDraft(
316
401
  grant: AccessGrant,
317
402
  workspaceId: string,
318
403
  authorization?: AccessGrantAuthorization,
404
+ readOptions: NewSessionDraftReadOptions = {},
319
405
  ): Promise<NewSessionDraftValue> {
320
406
  const actor = authorization
321
407
  ? await fileOwnerContextForAccess(deps, authorization, "sessions:read")
322
408
  : { subjectId: grant.subjectId, privateFileOwnerSubjectId: null };
323
409
  return withSessionRlsActorContext(actor, () =>
324
- getActorNewSessionDraftInFileScope(deps, grant, workspaceId),
410
+ getActorNewSessionDraftInFileScope(deps, grant, workspaceId, readOptions),
325
411
  );
326
412
  }
327
413
  export async function saveActorNewSessionDraft(
@@ -333,3 +419,34 @@ export async function saveActorNewSessionDraft(
333
419
  : { subjectId: grant.subjectId, privateFileOwnerSubjectId: null };
334
420
  return withSessionRlsActorContext(actor, () => saveActorNewSessionDraftInFileScope(...args));
335
421
  }
422
+
423
+ /**
424
+ * The model policy the person explicitly chose in the website composer, or an
425
+ * empty object when their draft follows the default.
426
+ *
427
+ * Only the model policy is reused. Connectors, repositories, Variable Sets,
428
+ * Sandbox Environment and compute target in the draft are the person's last
429
+ * explicit narrowing of one website chat, so surfaces that start work without
430
+ * the composer (Slack) resolve those from workspace defaults instead of
431
+ * silently inheriting that narrowing.
432
+ */
433
+ export async function getActorNewSessionModelChoice(
434
+ deps: Parameters<typeof getActorNewSessionDraft>[0],
435
+ grant: AccessGrant,
436
+ workspaceId: string,
437
+ ): Promise<
438
+ Pick<NewSessionDraftValue, "model" | "reasoningEffort" | "latencyMode"> | Record<string, never>
439
+ > {
440
+ // A draft that follows the default is not projected here; session creation
441
+ // resolves the default itself, once.
442
+ const draft = await getActorNewSessionDraft(deps, grant, workspaceId, undefined, {
443
+ projectDefaultModel: false,
444
+ });
445
+ return draft.revision > 0 && draft.modelProvided === true
446
+ ? {
447
+ model: draft.model,
448
+ reasoningEffort: draft.reasoningEffort,
449
+ latencyMode: draft.latencyMode,
450
+ }
451
+ : {};
452
+ }