@opengeni/core 0.28.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 (224) hide show
  1. package/README.md +1 -1
  2. package/dist/access/external-actor-authority.d.ts +47 -0
  3. package/dist/access/index.d.ts +182 -3
  4. package/dist/access/service-initiator.d.ts +3 -0
  5. package/dist/application/api-integration-servers.d.ts +20 -0
  6. package/dist/application/archived-session-imports.d.ts +6 -0
  7. package/dist/application/composer-submit.d.ts +24 -0
  8. package/dist/application/connect-authority.d.ts +13 -0
  9. package/dist/application/connect-operation.d.ts +31 -0
  10. package/dist/application/external-continuation.d.ts +15 -0
  11. package/dist/application/external-identity-lifecycle.d.ts +20 -0
  12. package/dist/application/external-link-work-admission.d.ts +12 -0
  13. package/dist/application/external-workspace-members.d.ts +34 -0
  14. package/dist/application/modal-native-worker-host-transport.d.ts +297 -0
  15. package/dist/application/new-session-drafts.d.ts +39 -2
  16. package/dist/application/sandbox-recovery.d.ts +58 -0
  17. package/dist/application/session-commands.d.ts +26 -13
  18. package/dist/application/session-mcp-credential-rotation.d.ts +29 -0
  19. package/dist/application/session-tenancy.d.ts +19 -0
  20. package/dist/application/user-resource-grants.d.ts +20 -0
  21. package/dist/billing/limits.d.ts +11 -0
  22. package/dist/canonical-human-identities.d.ts +7 -2
  23. package/dist/canonical-human-identities.js +17 -4
  24. package/dist/canonical-human-identities.js.map +1 -1
  25. package/dist/{chunk-6WKPWE5S.js → chunk-2MKMRGSY.js} +108 -24
  26. package/dist/chunk-2MKMRGSY.js.map +1 -0
  27. package/dist/{chunk-NYPMQ7NO.js → chunk-GVO3N37G.js} +205 -37
  28. package/dist/chunk-GVO3N37G.js.map +1 -0
  29. package/dist/{chunk-JJU6I5XD.js → chunk-LCQXNR62.js} +36 -10
  30. package/dist/chunk-LCQXNR62.js.map +1 -0
  31. package/dist/chunk-MM6N7CTO.js +387 -0
  32. package/dist/chunk-MM6N7CTO.js.map +1 -0
  33. package/dist/chunk-QO5GVFFO.js +235 -0
  34. package/dist/chunk-QO5GVFFO.js.map +1 -0
  35. package/dist/codex-model-availability.d.ts +14 -0
  36. package/dist/default-session-model.d.ts +139 -0
  37. package/dist/dependencies.d.ts +87 -4
  38. package/dist/developer-setup-credential-ceiling.d.ts +10 -0
  39. package/dist/domain/agent-config-resolution.d.ts +49 -0
  40. package/dist/domain/automations.d.ts +58 -0
  41. package/dist/domain/capabilities.d.ts +22 -4
  42. package/dist/domain/company-profile-agent-admin.d.ts +25 -0
  43. package/dist/domain/connection-account-selection-error.d.ts +8 -0
  44. package/dist/domain/connector-tool-permissions.d.ts +36 -0
  45. package/dist/domain/direct-model-provider.d.ts +3 -0
  46. package/dist/domain/editable-artifacts/agent-application.d.ts +10 -1
  47. package/dist/domain/editable-artifacts/durable-export.d.ts +22 -1
  48. package/dist/domain/editable-artifacts/ports.d.ts +10 -0
  49. package/dist/domain/editable-artifacts/service.d.ts +4 -3
  50. package/dist/domain/environments.d.ts +5 -9
  51. package/dist/domain/external-creation-attribution.d.ts +6 -0
  52. package/dist/domain/file-owner.d.ts +8 -0
  53. package/dist/domain/github-action-policies.d.ts +36 -0
  54. package/dist/domain/github-repository-bindings.d.ts +117 -0
  55. package/dist/domain/github-skill-source.d.ts +5 -0
  56. package/dist/domain/governed-learning-slack-publication.d.ts +45 -0
  57. package/dist/domain/insights-timing.d.ts +16 -0
  58. package/dist/domain/insights-usage.d.ts +27 -0
  59. package/dist/domain/insights.d.ts +28 -1
  60. package/dist/domain/knowledge-files.d.ts +20 -0
  61. package/dist/domain/knowledge-messages.d.ts +39 -0
  62. package/dist/domain/knowledge-messages.test.d.ts +1 -0
  63. package/dist/domain/knowledge-preparation.d.ts +10 -0
  64. package/dist/domain/knowledge-preparation.test.d.ts +1 -0
  65. package/dist/domain/knowledge-search.d.ts +15 -0
  66. package/dist/domain/knowledge-search.test.d.ts +1 -0
  67. package/dist/domain/knowledge.d.ts +9 -0
  68. package/dist/domain/mcp-account-bindings.d.ts +19 -0
  69. package/dist/domain/mcp-account-routes.d.ts +21 -0
  70. package/dist/domain/native-mcp-connection-admission.d.ts +3 -0
  71. package/dist/domain/organization-integration-catalog.d.ts +15 -0
  72. package/dist/domain/organization-membership-lifecycle.d.ts +1 -0
  73. package/dist/domain/personal-connection-delegations.d.ts +87 -4
  74. package/dist/domain/pr-review.d.ts +249 -0
  75. package/dist/domain/resources.d.ts +24 -3
  76. package/dist/domain/scheduled-task-access.d.ts +247 -0
  77. package/dist/domain/scheduled-tasks.d.ts +103 -6
  78. package/dist/domain/session-connection-accounts.d.ts +16 -0
  79. package/dist/domain/session-file-access.d.ts +4 -0
  80. package/dist/domain/session-native-connection-replacement.d.ts +14 -0
  81. package/dist/domain/session-start-timing.d.ts +7 -0
  82. package/dist/domain/session-tool-policy.d.ts +93 -4
  83. package/dist/domain/sessions.d.ts +275 -28
  84. package/dist/domain/skill-imports.d.ts +1 -0
  85. package/dist/domain/skill-search.d.ts +30 -0
  86. package/dist/domain/skills.d.ts +117 -0
  87. package/dist/domain/slack-bot.d.ts +46 -1
  88. package/dist/domain/workspace-members.d.ts +8 -1
  89. package/dist/editable-artifact-live/application.d.ts +12 -3
  90. package/dist/editable-artifact-live/ports.d.ts +0 -1
  91. package/dist/editable-artifact-live/server.d.ts +5 -2
  92. package/dist/editable-artifact-live/ticket.d.ts +2 -2
  93. package/dist/editable-artifact-live/types.d.ts +14 -2
  94. package/dist/editable-artifact-live.js +2 -2
  95. package/dist/editable-artifacts.js +10 -4
  96. package/dist/index.d.ts +49 -3
  97. package/dist/index.js +18826 -6847
  98. package/dist/index.js.map +1 -1
  99. package/dist/managed-auth-session-sets.d.ts +131 -0
  100. package/dist/managed-auth-session-sets.js +51 -0
  101. package/dist/managed-auth-session-sets.js.map +1 -0
  102. package/dist/managed-session.d.ts +98 -11
  103. package/dist/model-catalog.d.ts +117 -0
  104. package/dist/product-usage-metrics.d.ts +30 -0
  105. package/dist/rigs/index.d.ts +22 -5
  106. package/dist/rigs/provider-images.d.ts +1 -1
  107. package/dist/sandbox/fleet.d.ts +59 -10
  108. package/dist/sandbox/routing.d.ts +22 -2
  109. package/dist/sandbox/runtime-settings.d.ts +56 -0
  110. package/dist/session-authorization.d.ts +38 -1
  111. package/dist/site-session-origin.d.ts +9 -0
  112. package/dist/transcription.d.ts +12 -1
  113. package/dist/turn-surface.d.ts +18 -0
  114. package/dist/user-presence.d.ts +32 -0
  115. package/package.json +18 -11
  116. package/src/access/external-actor-authority.ts +94 -0
  117. package/src/access/index.ts +1386 -44
  118. package/src/access/service-initiator.ts +50 -0
  119. package/src/application/api-integration-servers.ts +215 -0
  120. package/src/application/archived-session-imports.ts +161 -0
  121. package/src/application/composer-submit.ts +77 -0
  122. package/src/application/connect-authority.ts +77 -0
  123. package/src/application/connect-operation.ts +80 -0
  124. package/src/application/external-continuation.ts +129 -0
  125. package/src/application/external-identity-lifecycle.ts +48 -0
  126. package/src/application/external-link-work-admission.ts +90 -0
  127. package/src/application/external-workspace-members.ts +232 -0
  128. package/src/application/modal-native-worker-host-transport.ts +263 -0
  129. package/src/application/new-session-drafts.ts +222 -29
  130. package/src/application/sandbox-recovery.ts +78 -0
  131. package/src/application/session-commands.ts +430 -73
  132. package/src/application/session-mcp-credential-rotation.ts +217 -0
  133. package/src/application/session-tenancy.ts +445 -0
  134. package/src/application/user-resource-grants.ts +154 -0
  135. package/src/billing/limits.ts +103 -24
  136. package/src/canonical-human-identities.ts +22 -3
  137. package/src/codex-model-availability.ts +136 -0
  138. package/src/default-session-model.ts +656 -0
  139. package/src/dependencies.ts +97 -5
  140. package/src/developer-setup-credential-ceiling.ts +60 -0
  141. package/src/domain/agent-config-resolution.ts +148 -0
  142. package/src/domain/automations.ts +270 -0
  143. package/src/domain/capabilities.ts +244 -151
  144. package/src/domain/company-profile-agent-admin.ts +56 -0
  145. package/src/domain/connection-account-selection-error.ts +19 -0
  146. package/src/domain/connector-tool-permissions.ts +371 -0
  147. package/src/domain/direct-model-provider.ts +62 -0
  148. package/src/domain/editable-artifacts/agent-application.ts +61 -15
  149. package/src/domain/editable-artifacts/durable-export.ts +100 -5
  150. package/src/domain/editable-artifacts/in-memory.ts +6 -0
  151. package/src/domain/editable-artifacts/ports.ts +11 -0
  152. package/src/domain/editable-artifacts/service.ts +32 -1
  153. package/src/domain/editable-artifacts/snapshot-compaction.ts +9 -5
  154. package/src/domain/editable-artifacts/snapshot-verifier-genesis.ts +10 -8
  155. package/src/domain/editable-artifacts/snapshot-verifier.ts +10 -3
  156. package/src/domain/environments.ts +25 -45
  157. package/src/domain/external-creation-attribution.ts +22 -0
  158. package/src/domain/file-owner.ts +67 -0
  159. package/src/domain/github-action-policies.ts +169 -0
  160. package/src/domain/github-repository-bindings.ts +295 -0
  161. package/src/domain/github-skill-source.ts +233 -0
  162. package/src/domain/governed-learning-slack-publication.ts +157 -0
  163. package/src/domain/insights-timing.ts +95 -0
  164. package/src/domain/insights-usage.ts +67 -0
  165. package/src/domain/insights.ts +270 -169
  166. package/src/domain/knowledge-files.ts +46 -0
  167. package/src/domain/knowledge-messages.test.ts +91 -0
  168. package/src/domain/knowledge-messages.ts +106 -0
  169. package/src/domain/knowledge-preparation.test.ts +276 -0
  170. package/src/domain/knowledge-preparation.ts +147 -0
  171. package/src/domain/knowledge-search.test.ts +561 -0
  172. package/src/domain/knowledge-search.ts +256 -0
  173. package/src/domain/knowledge.ts +133 -0
  174. package/src/domain/mcp-account-bindings.ts +171 -0
  175. package/src/domain/mcp-account-routes.ts +121 -0
  176. package/src/domain/native-mcp-connection-admission.ts +14 -0
  177. package/src/domain/organization-integration-catalog.ts +88 -0
  178. package/src/domain/organization-membership-lifecycle.ts +10 -0
  179. package/src/domain/personal-connection-delegations.ts +758 -115
  180. package/src/domain/pr-review.ts +687 -0
  181. package/src/domain/resources.ts +182 -25
  182. package/src/domain/scheduled-task-access.ts +1052 -0
  183. package/src/domain/scheduled-tasks.ts +1952 -215
  184. package/src/domain/session-connection-accounts.ts +67 -0
  185. package/src/domain/session-file-access.ts +47 -0
  186. package/src/domain/session-native-connection-replacement.ts +64 -0
  187. package/src/domain/session-start-timing.ts +55 -0
  188. package/src/domain/session-tool-policy.ts +443 -19
  189. package/src/domain/sessions.ts +3417 -619
  190. package/src/domain/skill-imports.ts +137 -17
  191. package/src/domain/skill-search.ts +221 -0
  192. package/src/domain/skills.ts +104 -0
  193. package/src/domain/slack-bot.ts +128 -3
  194. package/src/domain/workspace-members.ts +34 -2
  195. package/src/editable-artifact-live/application.ts +29 -4
  196. package/src/editable-artifact-live/ports.ts +0 -1
  197. package/src/editable-artifact-live/server.ts +167 -13
  198. package/src/editable-artifact-live/ticket.ts +87 -6
  199. package/src/editable-artifact-live/types.ts +16 -2
  200. package/src/editable-artifact-live/wire.ts +0 -10
  201. package/src/index.ts +69 -4
  202. package/src/managed-auth-session-sets.ts +371 -0
  203. package/src/managed-session.ts +520 -2
  204. package/src/model-catalog.ts +873 -0
  205. package/src/product-usage-metrics.ts +101 -0
  206. package/src/rigs/index.ts +179 -63
  207. package/src/rigs/provider-images.ts +2 -3
  208. package/src/sandbox/fleet.ts +471 -59
  209. package/src/sandbox/routing.ts +160 -33
  210. package/src/sandbox/runtime-settings.ts +281 -0
  211. package/src/session-authorization.ts +262 -78
  212. package/src/site-session-origin.ts +22 -0
  213. package/src/transcription.ts +13 -1
  214. package/src/turn-surface.ts +45 -0
  215. package/src/user-presence.ts +134 -0
  216. package/dist/chunk-6WKPWE5S.js.map +0 -1
  217. package/dist/chunk-IBOEYG6N.js +0 -34
  218. package/dist/chunk-IBOEYG6N.js.map +0 -1
  219. package/dist/chunk-JJU6I5XD.js.map +0 -1
  220. package/dist/chunk-NYPMQ7NO.js.map +0 -1
  221. package/dist/domain/durable-learning-slack-publication.d.ts +0 -72
  222. package/dist/domain/packs.d.ts +0 -55
  223. package/src/domain/durable-learning-slack-publication.ts +0 -202
  224. package/src/domain/packs.ts +0 -556
@@ -33,6 +33,63 @@ export type EditableArtifactMaterializationFormat =
33
33
  | "png"
34
34
  | "webp";
35
35
 
36
+ /**
37
+ * Stock production export formats. Custom deployments advertise their own
38
+ * capabilities on the profile port; this list never overrides their resolver.
39
+ * Extend the stock list only together with a real deployed codec.
40
+ */
41
+ export const EDITABLE_ARTIFACT_EXPORT_FORMATS: Readonly<
42
+ Record<EditableArtifactModality, readonly EditableArtifactMaterializationFormat[]>
43
+ > = Object.freeze({
44
+ spreadsheet: Object.freeze(["xlsx"] as const),
45
+ document: Object.freeze([] as const),
46
+ presentation: Object.freeze([] as const),
47
+ });
48
+
49
+ /** Human/agent-readable summary of {@link EDITABLE_ARTIFACT_EXPORT_FORMATS}. */
50
+ export function describeEditableArtifactExportFormats(): string {
51
+ return describeConfiguredExportFormats(EDITABLE_ARTIFACT_EXPORT_FORMATS);
52
+ }
53
+
54
+ export function describeConfiguredExportFormats(
55
+ configuredFormats: Readonly<
56
+ Record<EditableArtifactModality, readonly EditableArtifactMaterializationFormat[]>
57
+ >,
58
+ ): string {
59
+ const entries = Object.entries(configuredFormats) as Array<
60
+ [EditableArtifactModality, readonly EditableArtifactMaterializationFormat[]]
61
+ >;
62
+ const supported = entries
63
+ .filter(([, formats]) => formats.length > 0)
64
+ .map(([modality, formats]) => `${modality} → ${formats.join(", ")}`);
65
+ const none = entries.filter(([, formats]) => formats.length === 0).map(([modality]) => modality);
66
+ return [
67
+ supported.length > 0
68
+ ? `Supported exports: ${supported.join("; ")}.`
69
+ : "No exports are available.",
70
+ none.length > 0
71
+ ? `${none.join(" and ")} artifacts cannot be exported yet (no PDF, DOCX, PPTX, or image export); share the live artifact with its artifactReference link instead.`
72
+ : "",
73
+ ]
74
+ .filter(Boolean)
75
+ .join(" ");
76
+ }
77
+
78
+ /** Typed refusal that tells the caller which formats do exist. */
79
+ export function unsupportedEditableArtifactExport(
80
+ modality: EditableArtifactModality,
81
+ format: string,
82
+ formats = EDITABLE_ARTIFACT_EXPORT_FORMATS,
83
+ ): EditableArtifactDurableExportError {
84
+ const listed = (formats[modality] as readonly string[]).includes(format);
85
+ return new EditableArtifactDurableExportError(
86
+ "unsupported_format",
87
+ listed
88
+ ? `unsupported_format: this deployment does not serve a ${format} export of a ${modality} artifact with these options; retry with options omitted. ${describeConfiguredExportFormats(formats)}`
89
+ : `unsupported_format: a ${modality} artifact cannot be exported as ${format}. ${describeConfiguredExportFormats(formats)}`,
90
+ );
91
+ }
92
+
36
93
  export type EditableArtifactMaterializationMimeType =
37
94
  | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
38
95
  | "application/vnd.openxmlformats-officedocument.presentationml.presentation"
@@ -98,6 +155,11 @@ export type EditableArtifactMaterializationProfile = Readonly<{
98
155
 
99
156
  /** Server-owned profile lookup. Clients never supply executable identity facts. */
100
157
  export interface EditableArtifactMaterializationProfilePort {
158
+ /** Deployment-owned discovery; resolve remains the authority for exact options.
159
+ * Optional for existing custom adapters, whose support is resolved dynamically. */
160
+ supportedFormats?: Readonly<
161
+ Record<EditableArtifactModality, readonly EditableArtifactMaterializationFormat[]>
162
+ >;
101
163
  resolve(
102
164
  input: Readonly<{
103
165
  modality: EditableArtifactModality;
@@ -223,8 +285,11 @@ export type EditableArtifactDurableExportErrorCode =
223
285
  | "unavailable";
224
286
 
225
287
  export class EditableArtifactDurableExportError extends Error {
226
- constructor(readonly code: EditableArtifactDurableExportErrorCode) {
227
- super(code);
288
+ constructor(
289
+ readonly code: EditableArtifactDurableExportErrorCode,
290
+ message?: string,
291
+ ) {
292
+ super(message ?? code);
228
293
  this.name = "EditableArtifactDurableExportError";
229
294
  }
230
295
  }
@@ -247,6 +312,38 @@ const PROFILE_TEXT = /^[\x21-\x7e]+$/u;
247
312
  export class EditableArtifactDurableExportService {
248
313
  constructor(private readonly dependencies: EditableArtifactDurableExportServiceDependencies) {}
249
314
 
315
+ describeFormats(): string {
316
+ const formats = this.dependencies.profiles.supportedFormats;
317
+ return formats
318
+ ? describeConfiguredExportFormats(formats)
319
+ : "Export formats are determined by this deployment's configured exporter; unsupported format/options combinations are refused before pinning.";
320
+ }
321
+
322
+ /** Read-only exact preflight: no snapshot, ID allocation, or store write. */
323
+ async preflight(input: {
324
+ modality: EditableArtifactModality;
325
+ format: EditableArtifactMaterializationFormat;
326
+ options?: Readonly<Record<string, unknown>>;
327
+ }): Promise<EditableArtifactMaterializationProfile> {
328
+ const format = materializationFormat(input.format);
329
+ const options = exactOptions(input.options ?? Object.freeze({}));
330
+ const profile = await this.dependencies.profiles.resolve({
331
+ modality: input.modality,
332
+ format,
333
+ options,
334
+ });
335
+ if (!profile) {
336
+ const formats = this.dependencies.profiles.supportedFormats;
337
+ if (formats) throw unsupportedEditableArtifactExport(input.modality, format, formats);
338
+ throw new EditableArtifactDurableExportError(
339
+ "unsupported_format",
340
+ `unsupported_format: this deployment does not serve ${input.modality} → ${format} with these options. ${this.describeFormats()}`,
341
+ );
342
+ }
343
+ validateProfile(profile, input.modality, format);
344
+ return profile;
345
+ }
346
+
250
347
  async pinVersion(
251
348
  input: DurableExportContext & {
252
349
  idempotencyKey: string;
@@ -307,13 +404,11 @@ export class EditableArtifactDurableExportService {
307
404
  const format = materializationFormat(input.format);
308
405
  const options = exactOptions(input.options ?? Object.freeze({}));
309
406
  const version = await this.getVersion(context, versionId);
310
- const profile = await this.dependencies.profiles.resolve({
407
+ const profile = await this.preflight({
311
408
  modality: version.modality,
312
409
  format,
313
410
  options,
314
411
  });
315
- if (!profile) throw new EditableArtifactDurableExportError("unsupported_format");
316
- validateProfile(profile, version.modality, format);
317
412
  const requestHash = hashPublicRequest("materialize", [
318
413
  idempotencyKey,
319
414
  versionId,
@@ -429,6 +429,9 @@ export class InMemoryEditableArtifactStore
429
429
  artifactId: EditableArtifactId,
430
430
  request: ReadEditableArtifactTransactionBasisRequest,
431
431
  ): Promise<EditableArtifactTransactionBasis> {
432
+ if (request.authorizeCommit) {
433
+ throw new TypeError("Transactional artifact authority requires a PostgreSQL store");
434
+ }
432
435
  const normalizedScope = editableArtifactScope(scope);
433
436
  const normalizedArtifactId = editableArtifactId(artifactId);
434
437
  const key = aggregateKey(normalizedScope, normalizedArtifactId);
@@ -496,6 +499,9 @@ export class InMemoryEditableArtifactStore
496
499
  async tryCommitAppliedTransaction(
497
500
  request: TryCommitAppliedEditableArtifactTransactionRequest,
498
501
  ): Promise<TryCommitAppliedEditableArtifactTransactionResult> {
502
+ if (request.authorizeCommit) {
503
+ throw new TypeError("Transactional artifact authority requires a PostgreSQL store");
504
+ }
499
505
  const scope = editableArtifactScope(request.scope);
500
506
  const artifactId = editableArtifactId(request.artifactId);
501
507
  const key = aggregateKey(scope, artifactId);
@@ -1,3 +1,4 @@
1
+ import type { Database } from "@opengeni/db";
1
2
  import type {
2
3
  EditableArtifact,
3
4
  EditableArtifactActor,
@@ -288,6 +289,8 @@ export type ReadEditableArtifactTransactionBasisRequest = Readonly<{
288
289
  clientTransactionId: EditableArtifactReceipt["clientTransactionId"];
289
290
  previousLocalTransactionId: EditableArtifactReceipt["previousLocalTransactionId"];
290
291
  selectiveUndoOperationIds: readonly EditableArtifactOperationId[];
292
+ /** Backend-only live authority; checked transactionally even on receipt replay. */
293
+ authorizeCommit?: EditableArtifactCommitAuthorizer;
291
294
  }>;
292
295
 
293
296
  export type EditableArtifactTransactionUndoBasis = Readonly<{
@@ -324,6 +327,13 @@ export type ExpectedEditableArtifactPredecessor = Readonly<{
324
327
  replicaCounter: number;
325
328
  }>;
326
329
 
330
+ /**
331
+ * Receives the actual persistence transaction, never a separate pool handle.
332
+ * May run repeatedly for replay, lock admission and final persistence; must not
333
+ * perform external mutations. Stores without this transaction capability deny.
334
+ */
335
+ export type EditableArtifactCommitAuthorizer = (tx: Database) => Promise<void>;
336
+
327
337
  export type TryCommitAppliedEditableArtifactTransactionRequest =
328
338
  CommitAppliedEditableArtifactTransaction &
329
339
  Readonly<{
@@ -333,6 +343,7 @@ export type TryCommitAppliedEditableArtifactTransactionRequest =
333
343
  expectedAuthorizationRevision: number;
334
344
  /** Rechecked by durable production stores in the commit transaction. */
335
345
  authorizationActor: EditableArtifactActor;
346
+ authorizeCommit?: EditableArtifactCommitAuthorizer;
336
347
  actorKey: string;
337
348
  clientTransactionId: EditableArtifactReceipt["clientTransactionId"];
338
349
  requestHash: EditableArtifactReceipt["requestHash"];
@@ -13,6 +13,7 @@ import {
13
13
  EditableArtifactUndoTargetError,
14
14
  } from "./errors";
15
15
  import type {
16
+ EditableArtifactCommitAuthorizer,
16
17
  AuthoritativeEditableArtifactKernelPort,
17
18
  EditableArtifactAuthorizationPort,
18
19
  EditableArtifactClockPort,
@@ -28,9 +29,11 @@ import { hashEditableArtifactCreateRequest, hashEditableArtifactImportRequest }
28
29
  import {
29
30
  EDITABLE_ARTIFACT_COMMAND_MAX_BYTES,
30
31
  EDITABLE_ARTIFACT_INTENT_MAX_BYTES,
32
+ EDITABLE_ARTIFACT_INTENT_PROTOCOL_VERSION,
31
33
  EDITABLE_ARTIFACT_INTENT_MAX_UNDO_TARGETS,
32
34
  EDITABLE_ARTIFACT_INTENT_VERSION,
33
35
  EDITABLE_ARTIFACT_KERNEL_VERSION_MAX_BYTES,
36
+ EDITABLE_ARTIFACT_CODEC_REGISTRY,
34
37
  } from "@opengeni/contracts/editable-artifacts";
35
38
  import {
36
39
  COMMITTED_TRANSACTION_PROTOCOL_VERSION,
@@ -111,7 +114,6 @@ export const EDITABLE_ARTIFACT_MAX_COMMAND_BYTES_PER_TRANSACTION =
111
114
  EDITABLE_ARTIFACT_COMMAND_MAX_BYTES;
112
115
  export const EDITABLE_ARTIFACT_MAX_INTENT_BYTES_PER_TRANSACTION =
113
116
  EDITABLE_ARTIFACT_INTENT_MAX_BYTES;
114
- export const EDITABLE_ARTIFACT_INTENT_PROTOCOL_VERSION = 1;
115
117
  export const EDITABLE_ARTIFACT_MAX_OPERATION_BYTES_PER_TRANSACTION =
116
118
  MAX_COMMITTED_TRANSACTION_BYTES;
117
119
  /** Canonical OGACO envelope bound. Kept as an alias for existing consumers. */
@@ -546,6 +548,8 @@ export class EditableArtifactService {
546
548
  artifactId: EditableArtifact["id"];
547
549
  actor: EditableArtifactActor;
548
550
  request: ApplyEditableArtifactTransactionRequest;
551
+ /** Authenticated backend authority, not part of the client mutation envelope. */
552
+ authorizeCommit?: EditableArtifactCommitAuthorizer;
549
553
  }): Promise<ApplyEditableArtifactTransactionResult> {
550
554
  const context = normalizeBoundaryContext(input);
551
555
  const request = normalizeTransactionEnvelope(input.request);
@@ -569,6 +573,19 @@ export class EditableArtifactService {
569
573
  }
570
574
  const intentBytes = request.intentBytes.slice();
571
575
  const actorKey = editableArtifactActorKey(context.actor);
576
+ // A source-bound caller must prove its own authority on commit/replay.
577
+ // Sharing another caller's in-flight result would skip that proof.
578
+ if (input.authorizeCommit) {
579
+ return await this.applyTransactionOptimistically({
580
+ context,
581
+ actorKey,
582
+ request,
583
+ intent,
584
+ intentBytes,
585
+ authorizationRevision,
586
+ authorizeCommit: input.authorizeCommit,
587
+ });
588
+ }
572
589
  const inFlightKey = transactionInFlightKey(
573
590
  context.scope,
574
591
  context.artifactId,
@@ -630,6 +647,7 @@ export class EditableArtifactService {
630
647
  intent: ValidatedEditableArtifactMutationIntent;
631
648
  intentBytes: Uint8Array;
632
649
  authorizationRevision: number;
650
+ authorizeCommit?: EditableArtifactCommitAuthorizer;
633
651
  }): Promise<ApplyEditableArtifactTransactionResult> {
634
652
  const { context, actorKey, request, intent, intentBytes } = input;
635
653
  let authorizationRevision = input.authorizationRevision;
@@ -642,6 +660,7 @@ export class EditableArtifactService {
642
660
  clientTransactionId: intent.clientTransactionId,
643
661
  previousLocalTransactionId: intent.previousLocalTransactionId,
644
662
  selectiveUndoOperationIds: intent.selectiveUndoOperationIds,
663
+ ...(input.authorizeCommit ? { authorizeCommit: input.authorizeCommit } : {}),
645
664
  },
646
665
  );
647
666
  if (basisRead.kind === "existing") {
@@ -796,6 +815,7 @@ export class EditableArtifactService {
796
815
  expectedLifecycle: "active",
797
816
  expectedAuthorizationRevision: authorizationRevision,
798
817
  authorizationActor: context.actor,
818
+ ...(input.authorizeCommit ? { authorizeCommit: input.authorizeCommit } : {}),
799
819
  expectedHeadSequence: artifact.headSequence,
800
820
  actorKey,
801
821
  clientTransactionId: intent.clientTransactionId,
@@ -1036,6 +1056,7 @@ export class EditableArtifactService {
1036
1056
  expectedLifecycle: "active",
1037
1057
  expectedAuthorizationRevision: authorizationRevision,
1038
1058
  authorizationActor: context.actor,
1059
+ ...(input.authorizeCommit ? { authorizeCommit: input.authorizeCommit } : {}),
1039
1060
  expectedHeadSequence: artifact.headSequence,
1040
1061
  actorKey,
1041
1062
  clientTransactionId: intent.clientTransactionId,
@@ -2266,6 +2287,10 @@ function validateSnapshotRequest(
2266
2287
  assertNonnegativeSafeInteger(rawCoveredHeadSequence, "snapshot covered head sequence");
2267
2288
  const stateHash = editableArtifactStateHash(rawStateHash);
2268
2289
  assertPositiveSafeInteger(rawModelSchemaVersion, "snapshot model schema version");
2290
+ const currentCodec = EDITABLE_ARTIFACT_CODEC_REGISTRY[rawModality];
2291
+ if (rawModelSchemaVersion !== currentCodec.modelSchemaVersion) {
2292
+ throw new TypeError("Snapshot model schema version is unsupported");
2293
+ }
2269
2294
  assertBoundedKernelVersion(rawKernelVersion);
2270
2295
  assertIsoTimestamp(rawVerifiedAt, "snapshot verified timestamp");
2271
2296
  const common = {
@@ -2294,6 +2319,12 @@ function validateSnapshotRequest(
2294
2319
  }
2295
2320
  assertPositiveSafeInteger(rawOperationProtocolVersion, "snapshot operation protocol version");
2296
2321
  assertPositiveSafeInteger(rawCrdtStateVersion, "snapshot CRDT state version");
2322
+ if (
2323
+ rawOperationProtocolVersion !== COMMITTED_TRANSACTION_PROTOCOL_VERSION ||
2324
+ rawCrdtStateVersion !== currentCodec.snapshotVersion
2325
+ ) {
2326
+ throw new TypeError("Spreadsheet snapshot version is unsupported");
2327
+ }
2297
2328
  return Object.freeze({
2298
2329
  ...common,
2299
2330
  modality: "spreadsheet",
@@ -1,4 +1,6 @@
1
1
  import { type BoundedImmutableObjectWritePort, BoundedObjectWriteError } from "@opengeni/storage";
2
+ import { EDITABLE_ARTIFACT_CODEC_REGISTRY } from "@opengeni/contracts/editable-artifacts";
3
+ import { COMMITTED_TRANSACTION_PROTOCOL_VERSION } from "@opengeni/contracts/editable-artifact-committed-transaction";
2
4
 
3
5
  import type { EditableArtifactCompactionPort, EditableArtifactKernelState } from "./ports";
4
6
  import {
@@ -177,6 +179,7 @@ function validateGenerated(
177
179
  scope: EditableArtifactScope,
178
180
  artifactId: EditableArtifactId,
179
181
  ): void {
182
+ const currentCodec = EDITABLE_ARTIFACT_CODEC_REGISTRY[value.modality];
180
183
  if (
181
184
  value.scope.accountId !== scope.accountId ||
182
185
  value.scope.workspaceId !== scope.workspaceId ||
@@ -207,11 +210,12 @@ function validateGenerated(
207
210
  throw new EditableArtifactSnapshotVerificationError("limit_exceeded");
208
211
  }
209
212
  editableArtifactContentHash(value.canonicalContentHash);
210
- const versions =
211
- value.modality === "spreadsheet"
212
- ? [value.modelSchemaVersion, value.operationProtocolVersion, value.crdtStateVersion]
213
- : [value.modelSchemaVersion];
214
- if (versions.some((version) => !Number.isSafeInteger(version) || version <= 0)) {
213
+ if (
214
+ value.modelSchemaVersion !== currentCodec.modelSchemaVersion ||
215
+ (value.modality === "spreadsheet" &&
216
+ (value.operationProtocolVersion !== COMMITTED_TRANSACTION_PROTOCOL_VERSION ||
217
+ value.crdtStateVersion !== currentCodec.snapshotVersion))
218
+ ) {
215
219
  throw new EditableArtifactSnapshotVerificationError("version_mismatch");
216
220
  }
217
221
  try {
@@ -1,4 +1,6 @@
1
1
  import { type BoundedImmutableObjectWritePort, BoundedObjectWriteError } from "@opengeni/storage";
2
+ import { EDITABLE_ARTIFACT_CODEC_REGISTRY } from "@opengeni/contracts/editable-artifacts";
3
+ import { COMMITTED_TRANSACTION_PROTOCOL_VERSION } from "@opengeni/contracts/editable-artifact-committed-transaction";
2
4
  import type { EditableArtifactGenesisPort } from "./ports";
3
5
  import {
4
6
  MAX_EDITABLE_ARTIFACT_SNAPSHOT_BYTES,
@@ -173,6 +175,7 @@ function validateGeneratedGenesis(
173
175
  artifactId: EditableArtifactId,
174
176
  modality: EditableArtifactModality,
175
177
  ): void {
178
+ const currentCodec = EDITABLE_ARTIFACT_CODEC_REGISTRY[value.modality];
176
179
  if (
177
180
  value.scope.accountId !== scope.accountId ||
178
181
  value.scope.workspaceId !== scope.workspaceId ||
@@ -198,14 +201,13 @@ function validateGeneratedGenesis(
198
201
  }
199
202
  editableArtifactContentHash(value.canonicalContentHash);
200
203
  editableArtifactStateHash(value.stateHash);
201
- const versions =
202
- value.modality === "spreadsheet"
203
- ? [value.modelSchemaVersion, value.operationProtocolVersion, value.crdtStateVersion]
204
- : [value.modelSchemaVersion];
205
- for (const version of versions) {
206
- if (!Number.isSafeInteger(version) || version <= 0) {
207
- throw new EditableArtifactSnapshotVerificationError("version_mismatch");
208
- }
204
+ if (
205
+ value.modelSchemaVersion !== currentCodec.modelSchemaVersion ||
206
+ (value.modality === "spreadsheet" &&
207
+ (value.operationProtocolVersion !== COMMITTED_TRANSACTION_PROTOCOL_VERSION ||
208
+ value.crdtStateVersion !== currentCodec.snapshotVersion))
209
+ ) {
210
+ throw new EditableArtifactSnapshotVerificationError("version_mismatch");
209
211
  }
210
212
  try {
211
213
  assertBoundedKernelVersion(value.kernelVersion);
@@ -7,7 +7,11 @@ import {
7
7
  } from "@opengeni/storage";
8
8
  import type { EditableArtifactSnapshotVerifierPort } from "./ports";
9
9
  import { decodeEditableArtifactSerializedCommit } from "@opengeni/contracts/editable-artifact-serialized-commit";
10
- import { EDITABLE_ARTIFACT_PRODUCT_MAX_SNAPSHOT_BYTES } from "@opengeni/contracts/editable-artifacts";
10
+ import {
11
+ EDITABLE_ARTIFACT_CODEC_REGISTRY,
12
+ EDITABLE_ARTIFACT_PRODUCT_MAX_SNAPSHOT_BYTES,
13
+ } from "@opengeni/contracts/editable-artifacts";
14
+ import { COMMITTED_TRANSACTION_PROTOCOL_VERSION } from "@opengeni/contracts/editable-artifact-committed-transaction";
11
15
  import {
12
16
  causalFrontiersEqual,
13
17
  editableArtifactCausalFrontier,
@@ -520,6 +524,7 @@ function validateKernelResult(
520
524
  >
521
525
  | undefined,
522
526
  ): void {
527
+ const currentCodec = EDITABLE_ARTIFACT_CODEC_REGISTRY[snapshot.modality];
523
528
  if (
524
529
  result.canonicalReencodeVerified !== true ||
525
530
  result.modality !== snapshot.modality ||
@@ -542,8 +547,8 @@ function validateKernelResult(
542
547
  throw new EditableArtifactSnapshotVerificationError("state_hash_mismatch");
543
548
  }
544
549
  if (
545
- result.modelSchemaVersion !== snapshot.modelSchemaVersion ||
546
- result.kernelVersion !== snapshot.kernelVersion
550
+ snapshot.modelSchemaVersion !== currentCodec.modelSchemaVersion ||
551
+ result.modelSchemaVersion !== snapshot.modelSchemaVersion
547
552
  ) {
548
553
  throw new EditableArtifactSnapshotVerificationError("version_mismatch");
549
554
  }
@@ -559,6 +564,8 @@ function validateKernelResult(
559
564
  throw new EditableArtifactSnapshotVerificationError("frontier_mismatch");
560
565
  }
561
566
  if (
567
+ snapshot.operationProtocolVersion !== COMMITTED_TRANSACTION_PROTOCOL_VERSION ||
568
+ snapshot.crdtStateVersion !== currentCodec.snapshotVersion ||
562
569
  result.operationProtocolVersion !== snapshot.operationProtocolVersion ||
563
570
  result.crdtStateVersion !== snapshot.crdtStateVersion
564
571
  ) {
@@ -1,5 +1,9 @@
1
1
  import { environmentsEncryptionKeyBytes, type Settings } from "@opengeni/config";
2
- import type { AccessGrant, VariableSet } from "@opengeni/contracts";
2
+ import {
3
+ variableSetVariableNameReservation,
4
+ type AccessGrant,
5
+ type VariableSet,
6
+ } from "@opengeni/contracts";
3
7
  import { getVariableSet, recordAuditEvent, type Database } from "@opengeni/db";
4
8
  import { HTTPException } from "hono/http-exception";
5
9
  import { requirePermission } from "../access";
@@ -11,40 +15,8 @@ export const MAX_VARIABLES_PER_ENVIRONMENT = 100;
11
15
  // collectGitIdentityEnvironment) plus loader/startup-injection vectors. These
12
16
  // can never be set as variable set variables, so the run-scoped
13
17
  // git auth block and git identity always win without silent collisions.
14
- const reservedExactNames = new Set([
15
- "HOME",
16
- "PATH",
17
- "SHELL",
18
- "USER",
19
- "LOGNAME",
20
- "TMPDIR",
21
- "IFS",
22
- "ENV",
23
- "BASH_ENV",
24
- "NODE_OPTIONS",
25
- "PYTHONPATH",
26
- "PYTHONSTARTUP",
27
- "PERL5OPT",
28
- "PERL5LIB",
29
- "GH_TOKEN",
30
- "GITHUB_TOKEN",
31
- "GITLAB_TOKEN",
32
- "AZURE_DEVOPS_EXT_PAT",
33
- "GIT_ASKPASS",
34
- "GIT_TERMINAL_PROMPT",
35
- ]);
36
-
37
- const reservedPrefixes = [
38
- "OPENGENI_",
39
- "GIT_CONFIG_",
40
- "GIT_AUTHOR_",
41
- "GIT_COMMITTER_",
42
- "LD_",
43
- "DYLD_",
44
- ];
45
-
46
18
  export function assertAllowedVariableSetVariableName(name: string): void {
47
- if (reservedExactNames.has(name) || reservedPrefixes.some((prefix) => name.startsWith(prefix))) {
19
+ if (variableSetVariableNameReservation(name)) {
48
20
  throw new HTTPException(422, {
49
21
  message: `reserved variable set variable name / reserved environment variable name: ${name}`,
50
22
  });
@@ -69,10 +41,18 @@ export const requireEnvironmentEncryption = requireVariableSetEncryption;
69
41
 
70
42
  export async function requireVariableSetForApi(
71
43
  db: Database,
72
- workspaceId: string,
44
+ grant: AccessGrant,
73
45
  variableSetId: string,
74
46
  ): Promise<VariableSet> {
75
- const variableSet = await getVariableSet(db, workspaceId, variableSetId);
47
+ const variableSet = await getVariableSet(
48
+ db,
49
+ {
50
+ accountId: grant.accountId,
51
+ workspaceId: grant.workspaceId,
52
+ subjectId: grant.subjectId,
53
+ },
54
+ variableSetId,
55
+ );
76
56
  if (!variableSet) {
77
57
  throw new HTTPException(404, { message: "variableSet not found" });
78
58
  }
@@ -80,10 +60,8 @@ export async function requireVariableSetForApi(
80
60
  }
81
61
 
82
62
  /**
83
- * Validates an variableSet attachment supplied in a request payload (session
84
- * create, scheduled task create/update, pack enable). Requires the
85
- * `variable-sets:use` permission unless the attachment was already authorized
86
- * (pack-installation-inherited attachments), and maps a missing or
63
+ * Validates a Variable Set attachment supplied in a session or task request.
64
+ * Requires attach and use permissions, and maps a missing or
87
65
  * cross-variable set to 422 because the id is payload, not the route
88
66
  * target. RLS plus the workspace_id clause make cross-workspace ids
89
67
  * indistinguishable from missing ones.
@@ -93,13 +71,15 @@ export async function validateVariableSetAttachment(
93
71
  grant: AccessGrant,
94
72
  workspaceId: string,
95
73
  variableSetId: string,
96
- options: { preauthorized?: boolean } = {},
97
74
  ): Promise<VariableSet> {
98
75
  requireVariableSetEncryption(deps.settings);
99
- if (!options.preauthorized) {
100
- requirePermission(grant, "variable-sets:use");
101
- }
102
- const variableSet = await getVariableSet(deps.db, workspaceId, variableSetId);
76
+ requirePermission(grant, "variable-sets:attach");
77
+ requirePermission(grant, "variable-sets:use");
78
+ const variableSet = await getVariableSet(
79
+ deps.db,
80
+ { accountId: grant.accountId, workspaceId, subjectId: grant.subjectId },
81
+ variableSetId,
82
+ );
103
83
  if (!variableSet) {
104
84
  throw new HTTPException(422, { message: "unknown variableSetId" });
105
85
  }
@@ -0,0 +1,22 @@
1
+ import type { AccessGrant } from "@opengeni/contracts";
2
+ import { HTTPException } from "hono/http-exception";
3
+ import { externalAttributionForAuthorization, type AccessGrantAuthorization } from "../access";
4
+
5
+ export const EXTERNAL_CREATION_ATTRIBUTION_KEY = "opengeniExternalCreationAttribution";
6
+
7
+ /** Retained alongside session.created metadata; this is not a live permission
8
+ * snapshot and must never authorize a follow-up, child, or scheduled turn. */
9
+ export function externalCreationMetadata(
10
+ metadata: Record<string, unknown> | undefined,
11
+ authorization: AccessGrantAuthorization | undefined,
12
+ grant: AccessGrant,
13
+ ): Record<string, unknown> | undefined {
14
+ if (metadata && Object.hasOwn(metadata, EXTERNAL_CREATION_ATTRIBUTION_KEY)) {
15
+ throw new HTTPException(422, {
16
+ message: `${EXTERNAL_CREATION_ATTRIBUTION_KEY} is server-owned`,
17
+ });
18
+ }
19
+ const attribution = externalAttributionForAuthorization(authorization, grant);
20
+ if (!attribution) return metadata;
21
+ return { ...metadata, [EXTERNAL_CREATION_ATTRIBUTION_KEY]: attribution };
22
+ }
@@ -0,0 +1,67 @@
1
+ import { requireLiveAgentAttemptAuthorization } from "../session-authorization";
2
+ import {
3
+ freezeAgentLearningPolicy,
4
+ getSessionAuthorityProjection,
5
+ type SessionRlsActorContext,
6
+ } from "@opengeni/db";
7
+ import type { AccessGrant, Permission } from "@opengeni/contracts";
8
+ import { requirePermission, type AccessGrantAuthorization } from "../access";
9
+ import type { ApiRouteDeps } from "../dependencies";
10
+ import { knowledgeContextForAccess } from "./knowledge";
11
+
12
+ /** Personal original-file access follows verified ownership, never a subject-shaped bearer. */
13
+ export async function fileOwnerContextForAccess(
14
+ deps: Pick<ApiRouteDeps, "db">,
15
+ access: AccessGrantAuthorization,
16
+ permission: Permission,
17
+ ): Promise<SessionRlsActorContext> {
18
+ if (access.grant.principalKind === "agent_attempt")
19
+ return fileOwnerContextForAgent(deps, access.grant, permission);
20
+ const context = await knowledgeContextForAccess(deps, access, permission);
21
+ if (context.actor.kind === "human")
22
+ return {
23
+ subjectId: context.actor.subjectId,
24
+ privateFileOwnerSubjectId: context.actor.subjectId,
25
+ };
26
+ return { subjectId: access.grant.subjectId, privateFileOwnerSubjectId: null };
27
+ }
28
+
29
+ /** Internal MCP/core callers can prove an agent attempt without inventing a human authorization. */
30
+ export async function fileOwnerContextForAgent(
31
+ deps: Pick<ApiRouteDeps, "db">,
32
+ grant: AccessGrant,
33
+ permission: Permission,
34
+ ): Promise<SessionRlsActorContext> {
35
+ requirePermission(grant, permission);
36
+ const sessionId = grant.metadata?.sessionId;
37
+ if (grant.principalKind !== "agent_attempt" || typeof sessionId !== "string")
38
+ throw new Error("File access requires an exact agent attempt");
39
+ const attempt = await requireLiveAgentAttemptAuthorization(deps.db, grant, sessionId);
40
+ const snapshot = await freezeAgentLearningPolicy(deps.db, {
41
+ accountId: grant.accountId,
42
+ workspaceId: grant.workspaceId,
43
+ actor: {
44
+ kind: "agent",
45
+ sessionId,
46
+ turnId: attempt.turnId,
47
+ attemptId: attempt.attemptId,
48
+ executionGeneration: attempt.executionGeneration,
49
+ },
50
+ });
51
+ const sessionAuthority = await getSessionAuthorityProjection(
52
+ deps.db,
53
+ grant.workspaceId,
54
+ sessionId,
55
+ );
56
+ if (!sessionAuthority) throw new Error("File session authority unavailable");
57
+ return {
58
+ sessionAttachmentReadAccess: {
59
+ sessionId,
60
+ authorityEpoch: sessionAuthority.authorityEpoch,
61
+ actor: attempt,
62
+ },
63
+ subjectId: grant.subjectId,
64
+ initiatingHumanSubjectId: attempt.initiatingHumanSubjectId,
65
+ privateFileOwnerSubjectId: snapshot.defaultScope === "personal" ? snapshot.subjectId : null,
66
+ };
67
+ }