@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
@@ -26,10 +26,10 @@ import {
26
26
  type SpreadsheetArtifactKernelQuery,
27
27
  } from "@opengeni/contracts/editable-artifacts";
28
28
 
29
- import type {
30
- EditableArtifactDurableExportService,
31
- EditableArtifactMaterializationFormat,
32
- EditableArtifactMaterializationJob,
29
+ import {
30
+ type EditableArtifactDurableExportService,
31
+ type EditableArtifactMaterializationFormat,
32
+ type EditableArtifactMaterializationJob,
33
33
  } from "./durable-export";
34
34
  import {
35
35
  EditableArtifactDomainError,
@@ -76,6 +76,7 @@ export type EditableArtifactAgentContext = Readonly<{
76
76
 
77
77
  export type EditableArtifactAgentMetadata = Readonly<{
78
78
  id: string;
79
+ artifactReference: string;
79
80
  modality: EditableArtifactModality;
80
81
  title: string;
81
82
  lifecycle: EditableArtifact["lifecycle"];
@@ -174,6 +175,14 @@ export type EditableArtifactAgentApplicationDependencies = Readonly<{
174
175
  exports: EditableArtifactDurableExportService;
175
176
  associations: EditableArtifactAgentAssociationPort;
176
177
  inspector: EditableArtifactAgentInspectionKernelPort;
178
+ /** Production persists proof only after a successful native query. */
179
+ recordInspection?: (
180
+ input: EditableArtifactAgentContext & {
181
+ artifact: EditableArtifactAgentMetadata;
182
+ queryHash: string;
183
+ queryKind: string;
184
+ },
185
+ ) => Promise<string>;
177
186
  officeImports: EditableArtifactOfficeImportPort;
178
187
  workspaceFiles: EditableArtifactAgentWorkspaceFilePort;
179
188
  }>;
@@ -183,6 +192,9 @@ export type EditableArtifactAgentApplicationDependencies = Readonly<{
183
192
  * not another artifact engine: every mutation terminates at EditableArtifactService.
184
193
  */
185
194
  export class EditableArtifactAgentApplication {
195
+ describeExportFormats(): string {
196
+ return this.dependencies.exports.describeFormats();
197
+ }
186
198
  constructor(private readonly dependencies: EditableArtifactAgentApplicationDependencies) {}
187
199
 
188
200
  async list(
@@ -291,7 +303,13 @@ export class EditableArtifactAgentApplication {
291
303
  async inspect(
292
304
  input: EditableArtifactAgentContext &
293
305
  Readonly<{ artifactId: EditableArtifactId; request: EditableArtifactAgentQuery }>,
294
- ): Promise<Readonly<{ artifact: EditableArtifactAgentMetadata; projection: unknown }>> {
306
+ ): Promise<
307
+ Readonly<{
308
+ artifact: EditableArtifactAgentMetadata;
309
+ projection: unknown;
310
+ inspectionReceiptId?: string;
311
+ }>
312
+ > {
295
313
  const context = agentContext(input);
296
314
  const artifactId = editableArtifactId(input.artifactId);
297
315
  const state = await this.dependencies.domain.readCurrentKernelState({
@@ -304,8 +322,21 @@ export class EditableArtifactAgentApplication {
304
322
  const queryBytes = encodeQuery(input.request);
305
323
  const responseBytes = await this.dependencies.inspector.query({ state, queryBytes });
306
324
  const projection = decodeQueryResponse(input.request.modality, responseBytes);
325
+ const inspectionReceiptId =
326
+ state.modality === "document"
327
+ ? await this.dependencies.recordInspection?.({
328
+ ...context,
329
+ artifact: projectArtifact(state.artifact),
330
+ queryHash: createHash("sha256").update(queryBytes).digest("hex"),
331
+ queryKind: input.request.query.kind,
332
+ })
333
+ : undefined;
307
334
  await this.touch(context, artifactId);
308
- return Object.freeze({ artifact: projectArtifact(state.artifact), projection });
335
+ return Object.freeze({
336
+ artifact: projectArtifact(state.artifact),
337
+ projection,
338
+ ...(inspectionReceiptId ? { inspectionReceiptId } : {}),
339
+ });
309
340
  }
310
341
 
311
342
  async apply(
@@ -445,6 +476,12 @@ export class EditableArtifactAgentApplication {
445
476
  actor: context.actor,
446
477
  artifactId,
447
478
  });
479
+ // Refuse before pinning a version: the agent learns what does exist.
480
+ await this.dependencies.exports.preflight({
481
+ modality: artifact.modality,
482
+ format: input.format,
483
+ ...(input.options ? { options: input.options } : {}),
484
+ });
448
485
  const version = await this.dependencies.exports.pinVersion({
449
486
  scope: context.scope,
450
487
  actor: context.actor,
@@ -603,6 +640,7 @@ function commandProtocolVersion(modality: EditableArtifactModality): number {
603
640
  function projectArtifact(artifact: EditableArtifact): EditableArtifactAgentMetadata {
604
641
  return Object.freeze({
605
642
  id: artifact.id,
643
+ artifactReference: `[Open report](/workspaces/${artifact.scope.workspaceId}/artifacts/editable/${artifact.id})`,
606
644
  modality: artifact.modality,
607
645
  title: artifact.title,
608
646
  lifecycle: artifact.lifecycle,
@@ -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,
@@ -547,6 +548,8 @@ export class EditableArtifactService {
547
548
  artifactId: EditableArtifact["id"];
548
549
  actor: EditableArtifactActor;
549
550
  request: ApplyEditableArtifactTransactionRequest;
551
+ /** Authenticated backend authority, not part of the client mutation envelope. */
552
+ authorizeCommit?: EditableArtifactCommitAuthorizer;
550
553
  }): Promise<ApplyEditableArtifactTransactionResult> {
551
554
  const context = normalizeBoundaryContext(input);
552
555
  const request = normalizeTransactionEnvelope(input.request);
@@ -570,6 +573,19 @@ export class EditableArtifactService {
570
573
  }
571
574
  const intentBytes = request.intentBytes.slice();
572
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
+ }
573
589
  const inFlightKey = transactionInFlightKey(
574
590
  context.scope,
575
591
  context.artifactId,
@@ -631,6 +647,7 @@ export class EditableArtifactService {
631
647
  intent: ValidatedEditableArtifactMutationIntent;
632
648
  intentBytes: Uint8Array;
633
649
  authorizationRevision: number;
650
+ authorizeCommit?: EditableArtifactCommitAuthorizer;
634
651
  }): Promise<ApplyEditableArtifactTransactionResult> {
635
652
  const { context, actorKey, request, intent, intentBytes } = input;
636
653
  let authorizationRevision = input.authorizationRevision;
@@ -643,6 +660,7 @@ export class EditableArtifactService {
643
660
  clientTransactionId: intent.clientTransactionId,
644
661
  previousLocalTransactionId: intent.previousLocalTransactionId,
645
662
  selectiveUndoOperationIds: intent.selectiveUndoOperationIds,
663
+ ...(input.authorizeCommit ? { authorizeCommit: input.authorizeCommit } : {}),
646
664
  },
647
665
  );
648
666
  if (basisRead.kind === "existing") {
@@ -797,6 +815,7 @@ export class EditableArtifactService {
797
815
  expectedLifecycle: "active",
798
816
  expectedAuthorizationRevision: authorizationRevision,
799
817
  authorizationActor: context.actor,
818
+ ...(input.authorizeCommit ? { authorizeCommit: input.authorizeCommit } : {}),
800
819
  expectedHeadSequence: artifact.headSequence,
801
820
  actorKey,
802
821
  clientTransactionId: intent.clientTransactionId,
@@ -1037,6 +1056,7 @@ export class EditableArtifactService {
1037
1056
  expectedLifecycle: "active",
1038
1057
  expectedAuthorizationRevision: authorizationRevision,
1039
1058
  authorizationActor: context.actor,
1059
+ ...(input.authorizeCommit ? { authorizeCommit: input.authorizeCommit } : {}),
1040
1060
  expectedHeadSequence: artifact.headSequence,
1041
1061
  actorKey,
1042
1062
  clientTransactionId: intent.clientTransactionId,
@@ -7,7 +7,7 @@ import { EDITABLE_ARTIFACT_KERNEL_VERSION_MAX_BYTES } from "@opengeni/contracts/
7
7
  /**
8
8
  * Domain vocabulary for collaborative, editable Office-like artifacts.
9
9
  *
10
- * This is intentionally unrelated to OpenGeni's published HTML artifacts and
10
+ * This is intentionally unrelated to Opengeni's published HTML artifacts and
11
11
  * document-ingestion records. An editable artifact is mutable canonical model
12
12
  * state plus a causally ordered operation history.
13
13
  */
@@ -0,0 +1,26 @@
1
+ import { environmentsEncryptionKeyBytes, type Settings } from "@opengeni/config";
2
+ import { HTTPException } from "hono/http-exception";
3
+
4
+ /**
5
+ * The deployment's at-rest encryption key (`OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY`),
6
+ * shared by vault values and stored connection credentials.
7
+ */
8
+ export function requireEnvironmentEncryption(settings: Settings): Uint8Array {
9
+ const key = environmentsEncryptionKeyBytes(settings);
10
+ if (!key) {
11
+ throw new HTTPException(503, {
12
+ message: "secrets require OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY",
13
+ });
14
+ }
15
+ return key;
16
+ }
17
+
18
+ /**
19
+ * RM-1 for internal callers: Variable Sets were replaced by vaults, so a
20
+ * request that still selects one is refused instead of silently ignored.
21
+ */
22
+ export function variableSetsRemovedError(field: string): HTTPException {
23
+ return new HTTPException(422, {
24
+ message: `variable_sets_removed: ${field} is no longer supported; Variable Sets were replaced by vaults (use vaultPolicy)`,
25
+ });
26
+ }
@@ -1,5 +1,9 @@
1
1
  import { requireLiveAgentAttemptAuthorization } from "../session-authorization";
2
- import { freezeAgentLearningPolicy, type SessionRlsActorContext } from "@opengeni/db";
2
+ import {
3
+ freezeAgentLearningPolicy,
4
+ getSessionAuthorityProjection,
5
+ type SessionRlsActorContext,
6
+ } from "@opengeni/db";
3
7
  import type { AccessGrant, Permission } from "@opengeni/contracts";
4
8
  import { requirePermission, type AccessGrantAuthorization } from "../access";
5
9
  import type { ApiRouteDeps } from "../dependencies";
@@ -44,7 +48,18 @@ export async function fileOwnerContextForAgent(
44
48
  executionGeneration: attempt.executionGeneration,
45
49
  },
46
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");
47
57
  return {
58
+ sessionAttachmentReadAccess: {
59
+ sessionId,
60
+ authorityEpoch: sessionAuthority.authorityEpoch,
61
+ actor: attempt,
62
+ },
48
63
  subjectId: grant.subjectId,
49
64
  initiatingHumanSubjectId: attempt.initiatingHumanSubjectId,
50
65
  privateFileOwnerSubjectId: snapshot.defaultScope === "personal" ? snapshot.subjectId : null,
@@ -150,7 +150,7 @@ export function githubAppActionPolicyActor(input: {
150
150
  }): GitHubActionPolicyActorBinding {
151
151
  return {
152
152
  actor: { kind: "workspace_app", installationId: input.installationId },
153
- label: input.accountLogin ? `OpenGeni bot on ${input.accountLogin}` : "OpenGeni bot",
153
+ label: input.accountLogin ? `Opengeni bot on ${input.accountLogin}` : "Opengeni bot",
154
154
  connectionId: `github-app:${input.installationId}`,
155
155
  serverId: GITHUB_REST_MCP_APP_SERVER_ID,
156
156
  };
@@ -4,6 +4,7 @@ import type { GitHubSkillSourceClient, GitHubSkillTreeEntry } from "./skill-impo
4
4
  import { pinnedFetch, readResponseJsonBounded, readResponseTextBounded } from "@opengeni/network";
5
5
  import {
6
6
  PORTABLE_SKILL_MAX_FILES,
7
+ PORTABLE_SKILL_MAX_FILE_BYTES,
7
8
  PORTABLE_SKILL_MAX_TOTAL_BYTES,
8
9
  type SkillLibraryFile,
9
10
  } from "@opengeni/runtime/skill-library";
@@ -11,7 +12,8 @@ import {
11
12
  const githubApiBase = "https://api.github.com";
12
13
  const githubRequestTimeoutMs = 15_000;
13
14
  const githubMetadataMaxBytes = 4 * 1024 * 1024;
14
- const githubBlobResponseMaxBytes = 512 * 1024;
15
+ // GitHub blobs are base64-encoded JSON; allow framing and line wrapping.
16
+ const githubBlobResponseMaxBytes = PORTABLE_SKILL_MAX_FILE_BYTES * 2;
15
17
 
16
18
  type GitHubJsonRequest = (path: string, maxBytes: number, label: string) => Promise<unknown>;
17
19
 
@@ -0,0 +1,95 @@
1
+ import { withDatabaseTimingObserver, type DatabaseTimingObservation } from "@opengeni/db";
2
+ import type { Observability } from "@opengeni/observability";
3
+
4
+ const phases = new Set([
5
+ "auth",
6
+ "require_workspace",
7
+ "model_bundle",
8
+ "usage_bundle",
9
+ "live_warm",
10
+ "scheduled_tasks",
11
+ "session_depth",
12
+ "floor_sessions",
13
+ "online_machines",
14
+ "attached_sessions",
15
+ "scheduled_fires",
16
+ ] as const);
17
+ export type InsightsPhase = typeof phases extends Set<infer T> ? T : never;
18
+ export type InsightsPhaseObservation = {
19
+ phase: InsightsPhase;
20
+ stage: "helper" | DatabaseTimingObservation["stage"];
21
+ outcome: "completed" | "failed";
22
+ durationMs: number;
23
+ };
24
+ export type InsightsPhaseObserver = (observation: InsightsPhaseObservation) => void;
25
+ const stages = new Set([
26
+ "helper",
27
+ "transaction_admission",
28
+ "savepoint_admission",
29
+ "rls_setup",
30
+ "scoped_callback",
31
+ ]);
32
+
33
+ function observeSafely(
34
+ observer: InsightsPhaseObserver,
35
+ observation: InsightsPhaseObservation,
36
+ ): void {
37
+ try {
38
+ void Promise.resolve(observer(observation)).catch(() => undefined);
39
+ } catch {
40
+ // Telemetry is never authoritative for results or authorization.
41
+ }
42
+ }
43
+
44
+ /** Helper durations overlap under Promise.all; they must not be summed as request latency. */
45
+ export async function measureInsightsPhase<T>(
46
+ observer: InsightsPhaseObserver | undefined,
47
+ phase: InsightsPhase,
48
+ work: () => Promise<T>,
49
+ ): Promise<T> {
50
+ if (!observer) return work();
51
+ const started = performance.now();
52
+ let outcome: InsightsPhaseObservation["outcome"] = "failed";
53
+ try {
54
+ const result = await withDatabaseTimingObserver(
55
+ (observation) => observeSafely(observer, { ...observation, phase }),
56
+ work,
57
+ );
58
+ outcome = "completed";
59
+ return result;
60
+ } finally {
61
+ observeSafely(observer, {
62
+ phase,
63
+ stage: "helper",
64
+ outcome,
65
+ durationMs: performance.now() - started,
66
+ });
67
+ }
68
+ }
69
+
70
+ /** Closed runtime label projection: caller input, SQL and identities never become labels. */
71
+ export function workspaceInsightsPhaseMetricObserver(
72
+ observability: Pick<Observability, "observeHistogram"> | null | undefined,
73
+ ): InsightsPhaseObserver | undefined {
74
+ if (!observability) return undefined;
75
+ return (observation) => {
76
+ try {
77
+ const result = observability.observeHistogram({
78
+ name: "opengeni_workspace_insights_phase_duration_seconds",
79
+ help: "Insights helper and scoped DB durations; admission includes BEGIN or SAVEPOINT, scoped callback is not pure SQL time. Concurrent phases overlap.",
80
+ buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2, 3, 5, 10, 30, 60],
81
+ labels: {
82
+ phase: phases.has(observation.phase) ? observation.phase : "unknown",
83
+ stage: stages.has(observation.stage) ? observation.stage : "unknown",
84
+ outcome: observation.outcome === "completed" ? "completed" : "failed",
85
+ },
86
+ value: Number.isFinite(observation.durationMs)
87
+ ? Math.max(0, observation.durationMs) / 1_000
88
+ : 0,
89
+ });
90
+ void Promise.resolve(result).catch(() => undefined);
91
+ } catch {
92
+ // Includes exporter and metric registration failures.
93
+ }
94
+ };
95
+ }
@@ -0,0 +1,67 @@
1
+ import type {
2
+ InsightsCallsQuery,
3
+ InsightsCallsResponse,
4
+ InsightsUsageQuery,
5
+ InsightsUsageRange,
6
+ InsightsUsageResponse,
7
+ } from "@opengeni/contracts/insights-usage";
8
+ import { readInsightsCalls, readInsightsUsage, type Database } from "@opengeni/db";
9
+
10
+ export type InsightsQueryScope = {
11
+ accountId: string;
12
+ /** null means organization scope; it does not grant workspace/session access. */
13
+ workspaceId: string | null;
14
+ /** Server-verified metadata authority; billing permission alone supplies none. */
15
+ detailsWorkspaceIds?: readonly string[];
16
+ /** Existing organization service-key authority applies only to shared workspaces. */
17
+ detailsSharedWorkspaces?: boolean;
18
+ };
19
+
20
+ export async function getInsightsUsage(
21
+ db: Database,
22
+ input: InsightsQueryScope & { query: InsightsUsageQuery; now?: Date },
23
+ ): Promise<InsightsUsageResponse> {
24
+ return await readInsightsUsage(db, { ...input, now: input.now ?? new Date() });
25
+ }
26
+
27
+ export async function listInsightsCalls(
28
+ db: Database,
29
+ input: InsightsQueryScope & { query: InsightsCallsQuery; now?: Date },
30
+ ): Promise<InsightsCallsResponse> {
31
+ return await readInsightsCalls(db, { ...input, now: input.now ?? new Date() });
32
+ }
33
+
34
+ /** UTC windows retain the actual request timestamp, including empty midnight windows. */
35
+ export function insightsUsageWindow(range: InsightsUsageRange, now = new Date()) {
36
+ if (!Number.isFinite(now.getTime())) throw new Error("Invalid Insights request time");
37
+ const until = new Date(now);
38
+ const since = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
39
+ switch (range) {
40
+ case "today":
41
+ break;
42
+ case "week":
43
+ since.setUTCDate(since.getUTCDate() - 6);
44
+ break;
45
+ case "month":
46
+ since.setUTCDate(1);
47
+ break;
48
+ case "30d":
49
+ since.setUTCDate(since.getUTCDate() - 29);
50
+ break;
51
+ case "90d":
52
+ since.setUTCDate(since.getUTCDate() - 89);
53
+ break;
54
+ case "ytd":
55
+ since.setUTCMonth(0, 1);
56
+ break;
57
+ }
58
+ const priorUntil = new Date(since);
59
+ const priorSince = new Date(priorUntil.getTime() - (until.getTime() - since.getTime()));
60
+ return {
61
+ since,
62
+ until,
63
+ priorSince,
64
+ priorUntil,
65
+ bucket: range === "today" ? ("hour" as const) : ("day" as const),
66
+ };
67
+ }