@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.
- package/README.md +1 -1
- package/dist/access/external-actor-authority.d.ts +47 -0
- package/dist/access/index.d.ts +182 -3
- package/dist/access/service-initiator.d.ts +3 -0
- package/dist/application/api-integration-servers.d.ts +20 -0
- package/dist/application/archived-session-imports.d.ts +6 -0
- package/dist/application/composer-submit.d.ts +24 -0
- package/dist/application/connect-authority.d.ts +13 -0
- package/dist/application/connect-operation.d.ts +31 -0
- package/dist/application/external-continuation.d.ts +15 -0
- package/dist/application/external-identity-lifecycle.d.ts +20 -0
- package/dist/application/external-link-work-admission.d.ts +12 -0
- package/dist/application/external-workspace-members.d.ts +34 -0
- package/dist/application/modal-native-worker-host-transport.d.ts +297 -0
- package/dist/application/new-session-drafts.d.ts +39 -2
- package/dist/application/sandbox-recovery.d.ts +58 -0
- package/dist/application/session-commands.d.ts +26 -13
- package/dist/application/session-mcp-credential-rotation.d.ts +29 -0
- package/dist/application/session-tenancy.d.ts +19 -0
- package/dist/application/user-resource-grants.d.ts +20 -0
- package/dist/billing/limits.d.ts +11 -0
- package/dist/canonical-human-identities.d.ts +7 -2
- package/dist/canonical-human-identities.js +17 -4
- package/dist/canonical-human-identities.js.map +1 -1
- package/dist/{chunk-6WKPWE5S.js → chunk-2MKMRGSY.js} +108 -24
- package/dist/chunk-2MKMRGSY.js.map +1 -0
- package/dist/{chunk-NYPMQ7NO.js → chunk-GVO3N37G.js} +205 -37
- package/dist/chunk-GVO3N37G.js.map +1 -0
- package/dist/{chunk-JJU6I5XD.js → chunk-LCQXNR62.js} +36 -10
- package/dist/chunk-LCQXNR62.js.map +1 -0
- package/dist/chunk-MM6N7CTO.js +387 -0
- package/dist/chunk-MM6N7CTO.js.map +1 -0
- package/dist/chunk-QO5GVFFO.js +235 -0
- package/dist/chunk-QO5GVFFO.js.map +1 -0
- package/dist/codex-model-availability.d.ts +14 -0
- package/dist/default-session-model.d.ts +139 -0
- package/dist/dependencies.d.ts +87 -4
- package/dist/developer-setup-credential-ceiling.d.ts +10 -0
- package/dist/domain/agent-config-resolution.d.ts +49 -0
- package/dist/domain/automations.d.ts +58 -0
- package/dist/domain/capabilities.d.ts +22 -4
- package/dist/domain/company-profile-agent-admin.d.ts +25 -0
- package/dist/domain/connection-account-selection-error.d.ts +8 -0
- package/dist/domain/connector-tool-permissions.d.ts +36 -0
- package/dist/domain/direct-model-provider.d.ts +3 -0
- package/dist/domain/editable-artifacts/agent-application.d.ts +10 -1
- package/dist/domain/editable-artifacts/durable-export.d.ts +22 -1
- package/dist/domain/editable-artifacts/ports.d.ts +10 -0
- package/dist/domain/editable-artifacts/service.d.ts +4 -3
- package/dist/domain/environments.d.ts +5 -9
- package/dist/domain/external-creation-attribution.d.ts +6 -0
- package/dist/domain/file-owner.d.ts +8 -0
- package/dist/domain/github-action-policies.d.ts +36 -0
- package/dist/domain/github-repository-bindings.d.ts +117 -0
- package/dist/domain/github-skill-source.d.ts +5 -0
- package/dist/domain/governed-learning-slack-publication.d.ts +45 -0
- package/dist/domain/insights-timing.d.ts +16 -0
- package/dist/domain/insights-usage.d.ts +27 -0
- package/dist/domain/insights.d.ts +28 -1
- package/dist/domain/knowledge-files.d.ts +20 -0
- package/dist/domain/knowledge-messages.d.ts +39 -0
- package/dist/domain/knowledge-messages.test.d.ts +1 -0
- package/dist/domain/knowledge-preparation.d.ts +10 -0
- package/dist/domain/knowledge-preparation.test.d.ts +1 -0
- package/dist/domain/knowledge-search.d.ts +15 -0
- package/dist/domain/knowledge-search.test.d.ts +1 -0
- package/dist/domain/knowledge.d.ts +9 -0
- package/dist/domain/mcp-account-bindings.d.ts +19 -0
- package/dist/domain/mcp-account-routes.d.ts +21 -0
- package/dist/domain/native-mcp-connection-admission.d.ts +3 -0
- package/dist/domain/organization-integration-catalog.d.ts +15 -0
- package/dist/domain/organization-membership-lifecycle.d.ts +1 -0
- package/dist/domain/personal-connection-delegations.d.ts +87 -4
- package/dist/domain/pr-review.d.ts +249 -0
- package/dist/domain/resources.d.ts +24 -3
- package/dist/domain/scheduled-task-access.d.ts +247 -0
- package/dist/domain/scheduled-tasks.d.ts +103 -6
- package/dist/domain/session-connection-accounts.d.ts +16 -0
- package/dist/domain/session-file-access.d.ts +4 -0
- package/dist/domain/session-native-connection-replacement.d.ts +14 -0
- package/dist/domain/session-start-timing.d.ts +7 -0
- package/dist/domain/session-tool-policy.d.ts +93 -4
- package/dist/domain/sessions.d.ts +275 -28
- package/dist/domain/skill-imports.d.ts +1 -0
- package/dist/domain/skill-search.d.ts +30 -0
- package/dist/domain/skills.d.ts +117 -0
- package/dist/domain/slack-bot.d.ts +46 -1
- package/dist/domain/workspace-members.d.ts +8 -1
- package/dist/editable-artifact-live/application.d.ts +12 -3
- package/dist/editable-artifact-live/ports.d.ts +0 -1
- package/dist/editable-artifact-live/server.d.ts +5 -2
- package/dist/editable-artifact-live/ticket.d.ts +2 -2
- package/dist/editable-artifact-live/types.d.ts +14 -2
- package/dist/editable-artifact-live.js +2 -2
- package/dist/editable-artifacts.js +10 -4
- package/dist/index.d.ts +49 -3
- package/dist/index.js +18826 -6847
- package/dist/index.js.map +1 -1
- package/dist/managed-auth-session-sets.d.ts +131 -0
- package/dist/managed-auth-session-sets.js +51 -0
- package/dist/managed-auth-session-sets.js.map +1 -0
- package/dist/managed-session.d.ts +98 -11
- package/dist/model-catalog.d.ts +117 -0
- package/dist/product-usage-metrics.d.ts +30 -0
- package/dist/rigs/index.d.ts +22 -5
- package/dist/rigs/provider-images.d.ts +1 -1
- package/dist/sandbox/fleet.d.ts +59 -10
- package/dist/sandbox/routing.d.ts +22 -2
- package/dist/sandbox/runtime-settings.d.ts +56 -0
- package/dist/session-authorization.d.ts +38 -1
- package/dist/site-session-origin.d.ts +9 -0
- package/dist/transcription.d.ts +12 -1
- package/dist/turn-surface.d.ts +18 -0
- package/dist/user-presence.d.ts +32 -0
- package/package.json +18 -11
- package/src/access/external-actor-authority.ts +94 -0
- package/src/access/index.ts +1386 -44
- package/src/access/service-initiator.ts +50 -0
- package/src/application/api-integration-servers.ts +215 -0
- package/src/application/archived-session-imports.ts +161 -0
- package/src/application/composer-submit.ts +77 -0
- package/src/application/connect-authority.ts +77 -0
- package/src/application/connect-operation.ts +80 -0
- package/src/application/external-continuation.ts +129 -0
- package/src/application/external-identity-lifecycle.ts +48 -0
- package/src/application/external-link-work-admission.ts +90 -0
- package/src/application/external-workspace-members.ts +232 -0
- package/src/application/modal-native-worker-host-transport.ts +263 -0
- package/src/application/new-session-drafts.ts +222 -29
- package/src/application/sandbox-recovery.ts +78 -0
- package/src/application/session-commands.ts +430 -73
- package/src/application/session-mcp-credential-rotation.ts +217 -0
- package/src/application/session-tenancy.ts +445 -0
- package/src/application/user-resource-grants.ts +154 -0
- package/src/billing/limits.ts +103 -24
- package/src/canonical-human-identities.ts +22 -3
- package/src/codex-model-availability.ts +136 -0
- package/src/default-session-model.ts +656 -0
- package/src/dependencies.ts +97 -5
- package/src/developer-setup-credential-ceiling.ts +60 -0
- package/src/domain/agent-config-resolution.ts +148 -0
- package/src/domain/automations.ts +270 -0
- package/src/domain/capabilities.ts +244 -151
- package/src/domain/company-profile-agent-admin.ts +56 -0
- package/src/domain/connection-account-selection-error.ts +19 -0
- package/src/domain/connector-tool-permissions.ts +371 -0
- package/src/domain/direct-model-provider.ts +62 -0
- package/src/domain/editable-artifacts/agent-application.ts +61 -15
- package/src/domain/editable-artifacts/durable-export.ts +100 -5
- package/src/domain/editable-artifacts/in-memory.ts +6 -0
- package/src/domain/editable-artifacts/ports.ts +11 -0
- package/src/domain/editable-artifacts/service.ts +32 -1
- package/src/domain/editable-artifacts/snapshot-compaction.ts +9 -5
- package/src/domain/editable-artifacts/snapshot-verifier-genesis.ts +10 -8
- package/src/domain/editable-artifacts/snapshot-verifier.ts +10 -3
- package/src/domain/environments.ts +25 -45
- package/src/domain/external-creation-attribution.ts +22 -0
- package/src/domain/file-owner.ts +67 -0
- package/src/domain/github-action-policies.ts +169 -0
- package/src/domain/github-repository-bindings.ts +295 -0
- package/src/domain/github-skill-source.ts +233 -0
- package/src/domain/governed-learning-slack-publication.ts +157 -0
- package/src/domain/insights-timing.ts +95 -0
- package/src/domain/insights-usage.ts +67 -0
- package/src/domain/insights.ts +270 -169
- package/src/domain/knowledge-files.ts +46 -0
- package/src/domain/knowledge-messages.test.ts +91 -0
- package/src/domain/knowledge-messages.ts +106 -0
- package/src/domain/knowledge-preparation.test.ts +276 -0
- package/src/domain/knowledge-preparation.ts +147 -0
- package/src/domain/knowledge-search.test.ts +561 -0
- package/src/domain/knowledge-search.ts +256 -0
- package/src/domain/knowledge.ts +133 -0
- package/src/domain/mcp-account-bindings.ts +171 -0
- package/src/domain/mcp-account-routes.ts +121 -0
- package/src/domain/native-mcp-connection-admission.ts +14 -0
- package/src/domain/organization-integration-catalog.ts +88 -0
- package/src/domain/organization-membership-lifecycle.ts +10 -0
- package/src/domain/personal-connection-delegations.ts +758 -115
- package/src/domain/pr-review.ts +687 -0
- package/src/domain/resources.ts +182 -25
- package/src/domain/scheduled-task-access.ts +1052 -0
- package/src/domain/scheduled-tasks.ts +1952 -215
- package/src/domain/session-connection-accounts.ts +67 -0
- package/src/domain/session-file-access.ts +47 -0
- package/src/domain/session-native-connection-replacement.ts +64 -0
- package/src/domain/session-start-timing.ts +55 -0
- package/src/domain/session-tool-policy.ts +443 -19
- package/src/domain/sessions.ts +3417 -619
- package/src/domain/skill-imports.ts +137 -17
- package/src/domain/skill-search.ts +221 -0
- package/src/domain/skills.ts +104 -0
- package/src/domain/slack-bot.ts +128 -3
- package/src/domain/workspace-members.ts +34 -2
- package/src/editable-artifact-live/application.ts +29 -4
- package/src/editable-artifact-live/ports.ts +0 -1
- package/src/editable-artifact-live/server.ts +167 -13
- package/src/editable-artifact-live/ticket.ts +87 -6
- package/src/editable-artifact-live/types.ts +16 -2
- package/src/editable-artifact-live/wire.ts +0 -10
- package/src/index.ts +69 -4
- package/src/managed-auth-session-sets.ts +371 -0
- package/src/managed-session.ts +520 -2
- package/src/model-catalog.ts +873 -0
- package/src/product-usage-metrics.ts +101 -0
- package/src/rigs/index.ts +179 -63
- package/src/rigs/provider-images.ts +2 -3
- package/src/sandbox/fleet.ts +471 -59
- package/src/sandbox/routing.ts +160 -33
- package/src/sandbox/runtime-settings.ts +281 -0
- package/src/session-authorization.ts +262 -78
- package/src/site-session-origin.ts +22 -0
- package/src/transcription.ts +13 -1
- package/src/turn-surface.ts +45 -0
- package/src/user-presence.ts +134 -0
- package/dist/chunk-6WKPWE5S.js.map +0 -1
- package/dist/chunk-IBOEYG6N.js +0 -34
- package/dist/chunk-IBOEYG6N.js.map +0 -1
- package/dist/chunk-JJU6I5XD.js.map +0 -1
- package/dist/chunk-NYPMQ7NO.js.map +0 -1
- package/dist/domain/durable-learning-slack-publication.d.ts +0 -72
- package/dist/domain/packs.d.ts +0 -55
- package/src/domain/durable-learning-slack-publication.ts +0 -202
- 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(
|
|
227
|
-
|
|
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.
|
|
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
|
-
|
|
211
|
-
value.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
|
|
202
|
-
value.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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 {
|
|
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
|
-
|
|
546
|
-
result.
|
|
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
|
|
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 (
|
|
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
|
-
|
|
44
|
+
grant: AccessGrant,
|
|
73
45
|
variableSetId: string,
|
|
74
46
|
): Promise<VariableSet> {
|
|
75
|
-
const variableSet = await getVariableSet(
|
|
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
|
|
84
|
-
*
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
+
}
|