@opengeni/core 1.0.1 → 2.1.0-canary.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 (99) hide show
  1. package/dist/access/index.d.ts +37 -0
  2. package/dist/application/new-session-drafts.d.ts +8 -1
  3. package/dist/application/session-commands.d.ts +18 -11
  4. package/dist/application/session-tenancy.d.ts +18 -0
  5. package/dist/application/user-resource-grants.d.ts +20 -0
  6. package/dist/{chunk-6WKPWE5S.js → chunk-H7X52RI2.js} +27 -18
  7. package/dist/chunk-H7X52RI2.js.map +1 -0
  8. package/dist/{chunk-JJU6I5XD.js → chunk-VCZFFEPB.js} +21 -9
  9. package/dist/chunk-VCZFFEPB.js.map +1 -0
  10. package/dist/{chunk-NYPMQ7NO.js → chunk-ZYCNLYGH.js} +29 -27
  11. package/dist/chunk-ZYCNLYGH.js.map +1 -0
  12. package/dist/dependencies.d.ts +12 -1
  13. package/dist/domain/automations.d.ts +51 -0
  14. package/dist/domain/company-brain-governed-writes.d.ts +70 -0
  15. package/dist/domain/company-profile-agent-admin.d.ts +24 -0
  16. package/dist/domain/connection-authority.d.ts +97 -0
  17. package/dist/domain/connection-authority.js +169 -0
  18. package/dist/domain/connection-authority.js.map +1 -0
  19. package/dist/domain/editable-artifacts/service.d.ts +1 -2
  20. package/dist/domain/environments.d.ts +1 -1
  21. package/dist/domain/github-repository-bindings.d.ts +117 -0
  22. package/dist/domain/governed-learning-activation.d.ts +16 -0
  23. package/dist/domain/governed-learning-evaluator.d.ts +6 -0
  24. package/dist/domain/governed-learning-slack-publication.d.ts +45 -0
  25. package/dist/domain/memory-slack-delivery.d.ts +1 -4
  26. package/dist/domain/organization-membership-lifecycle.d.ts +1 -0
  27. package/dist/domain/packs.d.ts +5 -1
  28. package/dist/domain/personal-connection-delegations.d.ts +34 -2
  29. package/dist/domain/pr-review.d.ts +149 -0
  30. package/dist/domain/remember.d.ts +69 -0
  31. package/dist/domain/resources.d.ts +22 -1
  32. package/dist/domain/scheduled-tasks.d.ts +43 -2
  33. package/dist/domain/session-tool-policy.d.ts +1 -1
  34. package/dist/domain/sessions.d.ts +40 -6
  35. package/dist/editable-artifact-live/application.d.ts +10 -2
  36. package/dist/editable-artifact-live/ports.d.ts +0 -1
  37. package/dist/editable-artifact-live/server.d.ts +0 -1
  38. package/dist/editable-artifact-live/ticket.d.ts +0 -1
  39. package/dist/editable-artifact-live/types.d.ts +5 -2
  40. package/dist/editable-artifact-live.js +2 -2
  41. package/dist/editable-artifacts.js +2 -4
  42. package/dist/index.d.ts +13 -1
  43. package/dist/index.js +4827 -1164
  44. package/dist/index.js.map +1 -1
  45. package/dist/rigs/index.d.ts +22 -5
  46. package/dist/sandbox/fleet.d.ts +34 -6
  47. package/dist/sandbox/routing.d.ts +15 -1
  48. package/dist/sandbox/runtime-settings.d.ts +39 -0
  49. package/package.json +16 -11
  50. package/src/access/index.ts +151 -31
  51. package/src/application/new-session-drafts.ts +24 -2
  52. package/src/application/session-commands.ts +305 -71
  53. package/src/application/session-tenancy.ts +321 -0
  54. package/src/application/user-resource-grants.ts +140 -0
  55. package/src/billing/limits.ts +25 -15
  56. package/src/dependencies.ts +12 -1
  57. package/src/domain/automations.ts +196 -0
  58. package/src/domain/capabilities.ts +7 -1
  59. package/src/domain/company-brain-governed-writes.ts +330 -0
  60. package/src/domain/company-profile-agent-admin.ts +55 -0
  61. package/src/domain/connection-authority.ts +329 -0
  62. package/src/domain/editable-artifacts/agent-application.ts +17 -9
  63. package/src/domain/editable-artifacts/service.ts +12 -1
  64. package/src/domain/editable-artifacts/snapshot-compaction.ts +9 -5
  65. package/src/domain/editable-artifacts/snapshot-verifier-genesis.ts +10 -8
  66. package/src/domain/editable-artifacts/snapshot-verifier.ts +10 -3
  67. package/src/domain/environments.ts +16 -3
  68. package/src/domain/github-repository-bindings.ts +295 -0
  69. package/src/domain/governed-learning-activation.ts +44 -0
  70. package/src/domain/governed-learning-evaluator.ts +49 -0
  71. package/src/domain/governed-learning-slack-publication.ts +157 -0
  72. package/src/domain/insights.ts +9 -0
  73. package/src/domain/memory-slack-delivery.ts +0 -30
  74. package/src/domain/organization-membership-lifecycle.ts +9 -0
  75. package/src/domain/packs.ts +106 -3
  76. package/src/domain/personal-connection-delegations.ts +532 -51
  77. package/src/domain/pr-review.ts +671 -0
  78. package/src/domain/remember.ts +549 -0
  79. package/src/domain/resources.ts +131 -4
  80. package/src/domain/scheduled-tasks.ts +497 -76
  81. package/src/domain/session-tool-policy.ts +12 -9
  82. package/src/domain/sessions.ts +613 -166
  83. package/src/editable-artifact-live/application.ts +21 -3
  84. package/src/editable-artifact-live/ports.ts +0 -1
  85. package/src/editable-artifact-live/server.ts +13 -7
  86. package/src/editable-artifact-live/ticket.ts +1 -5
  87. package/src/editable-artifact-live/types.ts +6 -2
  88. package/src/editable-artifact-live/wire.ts +0 -10
  89. package/src/index.ts +13 -1
  90. package/src/rigs/index.ts +165 -48
  91. package/src/sandbox/fleet.ts +376 -30
  92. package/src/sandbox/routing.ts +91 -29
  93. package/src/sandbox/runtime-settings.ts +274 -0
  94. package/src/session-authorization.ts +17 -49
  95. package/dist/chunk-6WKPWE5S.js.map +0 -1
  96. package/dist/chunk-JJU6I5XD.js.map +0 -1
  97. package/dist/chunk-NYPMQ7NO.js.map +0 -1
  98. package/dist/domain/durable-learning-slack-publication.d.ts +0 -72
  99. package/src/domain/durable-learning-slack-publication.ts +0 -202
@@ -0,0 +1,51 @@
1
+ import { AutomationAcceptedExecution, AutomationNormalizedEvent, AutomationSessionTemplate, type AutomationAdapterId, type AutomationSource, type AutomationTrigger } from "@opengeni/contracts";
2
+ export type AutomationAdapterRenderResult = {
3
+ initialMessage: string;
4
+ sessionTemplate: AutomationSessionTemplate;
5
+ provenance: Record<string, unknown>;
6
+ };
7
+ export type AutomationAdapter = {
8
+ id: AutomationAdapterId;
9
+ verify(input: {
10
+ rawBody: Uint8Array;
11
+ headers: Headers;
12
+ secret: string;
13
+ sourceConfiguration: Record<string, unknown>;
14
+ }): boolean;
15
+ deliveryKey(input: {
16
+ headers: Headers;
17
+ requestDigest: string;
18
+ }): string;
19
+ normalize(input: {
20
+ rawBody: Uint8Array;
21
+ headers: Headers;
22
+ sourceConfiguration: Record<string, unknown>;
23
+ }): AutomationNormalizedEvent;
24
+ validateSourceConfiguration(configuration: Record<string, unknown>): void;
25
+ validateTriggerConfiguration(configuration: Record<string, unknown>): void;
26
+ validateTriggerParameters(parameters: Record<string, unknown>): void;
27
+ matches(input: {
28
+ event: AutomationNormalizedEvent;
29
+ trigger: AutomationTrigger;
30
+ }): boolean;
31
+ render(input: {
32
+ event: AutomationNormalizedEvent;
33
+ trigger: AutomationTrigger;
34
+ source: Pick<AutomationSource, "id" | "adapterId" | "version" | "configuration">;
35
+ }): AutomationAdapterRenderResult;
36
+ };
37
+ export declare const SIGNED_JSON_AUTOMATION_ADAPTER_ID: "signed-json.v1";
38
+ export declare const signedJsonAutomationAdapter: AutomationAdapter;
39
+ export declare function registerAutomationAdapter(adapter: AutomationAdapter): void;
40
+ export declare function getAutomationAdapter(adapterId: string): AutomationAdapter | null;
41
+ export declare function requireAutomationAdapter(adapterId: string): AutomationAdapter;
42
+ export declare function automationRequestDigest(adapterId: string, rawBody: Uint8Array): string;
43
+ export declare function buildAutomationAcceptedExecution(input: {
44
+ accountId: string;
45
+ workspaceId: string;
46
+ source: AutomationSource;
47
+ trigger: AutomationTrigger;
48
+ eventId: string;
49
+ event: AutomationNormalizedEvent;
50
+ render: AutomationAdapterRenderResult;
51
+ }): AutomationAcceptedExecution;
@@ -0,0 +1,70 @@
1
+ import { type CompanyBrainGovernedWriteReceipt as CompanyBrainGovernedWriteReceiptType, type CompanyBrainLearningPolicyRouteReceipt as CompanyBrainLearningPolicyRouteReceiptType, type GovernedLearningActivationDestination, type GovernedLearningActivationReceipt } from "@opengeni/contracts";
2
+ import { type Database, activateGovernedLearningDecision, evaluateGovernedLearningProposal, getOrCreateWorkspaceLearningPolicySnapshot, writeCompanyBrainGovernedProposal } from "@opengeni/db";
3
+ import { type GovernedLearningSlackPublicationResult } from "./governed-learning-slack-publication.js";
4
+ export type CompanyBrainGovernedWriteInput = {
5
+ attempt: unknown;
6
+ request: unknown;
7
+ };
8
+ export type CompanyBrainGovernedWriteRouterOptions = {
9
+ db: Database;
10
+ authority?: typeof writeCompanyBrainGovernedProposal;
11
+ learningPolicySnapshot?: typeof getOrCreateWorkspaceLearningPolicySnapshot;
12
+ /** Governed-learning evaluator (migration 0268). Defaults to the DB capability. */
13
+ evaluate?: typeof evaluateGovernedLearningProposal;
14
+ /** Governed-learning activation controller (migration 0269). Defaults to the DB capability. */
15
+ activate?: typeof activateGovernedLearningDecision;
16
+ /**
17
+ * Destinations the router may activate automatically from a final eligible
18
+ * decision. Defaults to preferences only: mandatory instruction policy keeps
19
+ * requiring human-authoritative activation even under `automatic`, so its
20
+ * decision receipt is recorded but the inactive draft stays for review.
21
+ */
22
+ automaticDestinations?: ReadonlyArray<GovernedLearningActivationDestination>;
23
+ /**
24
+ * Optional post-activation notification sink (Slack publication outbox).
25
+ * Notification is never authority: failures are swallowed after the
26
+ * activation receipt is durable and never change the route receipt.
27
+ */
28
+ notifyActivation?: (input: {
29
+ db: Database;
30
+ receipt: GovernedLearningActivationReceipt;
31
+ sessionId: string;
32
+ attemptId: string;
33
+ }) => Promise<GovernedLearningSlackPublicationResult>;
34
+ };
35
+ export declare const DEFAULT_AUTOMATIC_LEARNING_DESTINATIONS: ReadonlyArray<GovernedLearningActivationDestination>;
36
+ /**
37
+ * Transport-neutral facade for explicit governed Company Brain proposals.
38
+ * It intentionally exposes no generic remember call, selector, activation,
39
+ * rollback token, or personal/organization destination.
40
+ */
41
+ export declare function createCompanyBrainGovernedWriteRouter(options: CompanyBrainGovernedWriteRouterOptions): {
42
+ write: (input: CompanyBrainGovernedWriteInput) => Promise<CompanyBrainGovernedWriteReceiptType>;
43
+ };
44
+ /**
45
+ * Deterministic UUIDv5-shaped operation id derived from the caller's proposal
46
+ * operation id, so an exact retry of the same write converges on the same
47
+ * evaluator/activation receipts instead of conflicting.
48
+ */
49
+ export declare function derivedGovernedLearningOperationId(operationId: string, stage: "evaluation" | "activation"): string;
50
+ /**
51
+ * Resolve one derived Company Brain write from the immutable policy snapshot
52
+ * frozen for the exact accepted attempt. The evidence identity is the policy
53
+ * source identity; callers cannot select another override key to widen the
54
+ * decision.
55
+ *
56
+ * After the proposal commits, a Ways-of-working proposal (instruction policy or
57
+ * preference, i.e. one that materialized a `knowledge_change_proposals` row) is
58
+ * evaluated by the governed-learning evaluator against the same accepted
59
+ * snapshot. `suggest` records the content-free decision receipt only. Under
60
+ * `automatic`, a final `automaticEligible` receipt is handed to the activation
61
+ * controller, which revalidates current authority and applies the change only
62
+ * through the destination-owned lifecycle. By default only preferences may
63
+ * activate automatically; mandatory instruction policy always keeps a human
64
+ * activation boundary. Evaluation or activation failure
65
+ * never rolls back the durable proposal; it is reported as a bounded
66
+ * `learningFailure` and the proposal remains for human review.
67
+ */
68
+ export declare function createCompanyBrainLearningPolicyRouter(options: CompanyBrainGovernedWriteRouterOptions): {
69
+ write: (input: CompanyBrainGovernedWriteInput) => Promise<CompanyBrainLearningPolicyRouteReceiptType>;
70
+ };
@@ -0,0 +1,24 @@
1
+ import { type CompanyProfileAgentConfirmationReceipt, type CompanyProfileAgentProposalReceipt } from "@opengeni/contracts";
2
+ import { confirmCompanyProfileForAgent, proposeCompanyProfileForAgent, type Database } from "@opengeni/db";
3
+ export { CompanyProfileAgentAdminError } from "@opengeni/db";
4
+ export type CompanyProfileAgentAdminRouterOptions = {
5
+ db: Database;
6
+ propose?: typeof proposeCompanyProfileForAgent;
7
+ confirm?: typeof confirmCompanyProfileForAgent;
8
+ };
9
+ /**
10
+ * Explicit organization administration is intentionally separate from derived
11
+ * workspace learning. The database capabilities own exact-attempt admission,
12
+ * current organization-owner authority, canonical human confirmation,
13
+ * tenant isolation, CAS, and immutable receipts.
14
+ */
15
+ export declare function createCompanyProfileAgentAdminRouter(options: CompanyProfileAgentAdminRouterOptions): {
16
+ propose: (input: {
17
+ attempt: unknown;
18
+ request: unknown;
19
+ }) => Promise<CompanyProfileAgentProposalReceipt>;
20
+ confirm: (input: {
21
+ attempt: unknown;
22
+ request: unknown;
23
+ }) => Promise<CompanyProfileAgentConfirmationReceipt>;
24
+ };
@@ -0,0 +1,97 @@
1
+ import type { ConnectionKind, ConnectionStatus, SessionTenancyVisibility, UserResourceGrantMode, UserResourceGrantStatus } from "@opengeni/contracts";
2
+ import { ConnectionUseAuthoritySnapshot, type ConnectionUseAuthorizationResult, type ConnectionUseSelectionSource } from "@opengeni/contracts/connection-authority";
3
+ export type ConnectionAuthorityCandidate = {
4
+ id: string;
5
+ organizationId: string;
6
+ workspaceId: string;
7
+ generation: number;
8
+ status: ConnectionStatus;
9
+ providerDomain: string;
10
+ kind: ConnectionKind;
11
+ subjectId: string | null;
12
+ ownerOrganizationMembershipId: string | null;
13
+ };
14
+ export type UserConnectionAuthorityCandidate = {
15
+ id: string;
16
+ organizationId: string;
17
+ resourceKind: "connection";
18
+ resourceId: string;
19
+ originWorkspaceId: string;
20
+ ownerSubjectId: string;
21
+ ownerOrganizationMembershipId: string;
22
+ ownerMembershipAuthorizationRevision: number;
23
+ generation: number;
24
+ status: "active" | "retained" | "revoked";
25
+ };
26
+ export declare class ConnectionAuthorityInvariantError extends Error {
27
+ readonly code: string;
28
+ constructor(code: string);
29
+ }
30
+ export declare function captureConnectionUseAuthority(input: {
31
+ organizationId: string;
32
+ targetWorkspaceId: string;
33
+ targetSessionId: string;
34
+ targetSessionVisibility: SessionTenancyVisibility;
35
+ targetSessionAuthorityEpoch: number;
36
+ acceptedWork: {
37
+ kind: "turn";
38
+ turnId: string;
39
+ } | {
40
+ kind: "scheduled_task";
41
+ taskId: string;
42
+ taskAuthorityRevision: number;
43
+ runId: string;
44
+ };
45
+ connection: ConnectionAuthorityCandidate;
46
+ authority?: unknown;
47
+ authorityWasOmitted?: boolean;
48
+ userAuthority?: UserConnectionAuthorityCandidate | null;
49
+ selectionSources: ConnectionUseSelectionSource[];
50
+ }): ConnectionUseAuthoritySnapshot;
51
+ export type LiveConnectionUseState = {
52
+ organizationId: string;
53
+ targetWorkspaceId: string;
54
+ targetWorkspaceAccessActive: boolean;
55
+ session: {
56
+ id: string;
57
+ organizationId: string;
58
+ workspaceId: string;
59
+ visibility: SessionTenancyVisibility;
60
+ authorityEpoch: number;
61
+ active: boolean;
62
+ };
63
+ connection: ConnectionAuthorityCandidate | null;
64
+ ownerMembership: {
65
+ id: string;
66
+ organizationId: string;
67
+ subjectId: string;
68
+ status: "provisioning" | "active" | "suspended" | "revoked";
69
+ authorizationRevision: number;
70
+ } | null;
71
+ userAuthority: UserConnectionAuthorityCandidate | null;
72
+ grant: {
73
+ id: string;
74
+ authorityId: string;
75
+ organizationId: string;
76
+ workspaceId: string;
77
+ sessionId: string | null;
78
+ action: string;
79
+ mode: UserResourceGrantMode;
80
+ context: SessionTenancyVisibility;
81
+ authorityEpoch: number | null;
82
+ generation: number;
83
+ status: UserResourceGrantStatus;
84
+ expiresAt: string | null;
85
+ consumed: boolean;
86
+ } | null;
87
+ };
88
+ /**
89
+ * Revalidates every mutable fence immediately before provider use. Callers must
90
+ * resolve the live state in one DB authority boundary; this pure function does
91
+ * not cache, refresh, decrypt, or invoke a provider.
92
+ */
93
+ export declare function revalidateConnectionUseAuthority(input: {
94
+ snapshot: ConnectionUseAuthoritySnapshot;
95
+ live: LiveConnectionUseState;
96
+ now?: Date;
97
+ }): ConnectionUseAuthorizationResult;
@@ -0,0 +1,169 @@
1
+ // src/domain/connection-authority.ts
2
+ import {
3
+ ConnectionAuthorityEnvelope,
4
+ ConnectionUseAttribution,
5
+ ConnectionUseAuthoritySnapshot
6
+ } from "@opengeni/contracts/connection-authority";
7
+ var ConnectionAuthorityInvariantError = class extends Error {
8
+ constructor(code) {
9
+ super(`connection authority invariant failed: ${code}`);
10
+ this.code = code;
11
+ this.name = "ConnectionAuthorityInvariantError";
12
+ }
13
+ };
14
+ function captureConnectionUseAuthority(input) {
15
+ const authority = ConnectionAuthorityEnvelope.parse(input.authority ?? {});
16
+ const connection = input.connection;
17
+ if (connection.organizationId !== input.organizationId) {
18
+ throw new ConnectionAuthorityInvariantError("tenant_mismatch");
19
+ }
20
+ if (connection.status !== "active") {
21
+ throw new ConnectionAuthorityInvariantError("connection_status_inactive");
22
+ }
23
+ let authoritySource;
24
+ let userDelegation = null;
25
+ if (authority.scope === "workspace") {
26
+ if (connection.subjectId !== null || connection.ownerOrganizationMembershipId !== null) {
27
+ throw new ConnectionAuthorityInvariantError("workspace_borrowed_personal_connection");
28
+ }
29
+ if (connection.workspaceId !== input.targetWorkspaceId) {
30
+ throw new ConnectionAuthorityInvariantError("workspace_connection_scope_mismatch");
31
+ }
32
+ authoritySource = input.authorityWasOmitted ? "legacy_workspace_omission" : "explicit_workspace";
33
+ } else {
34
+ const delegation = authority.userDelegation;
35
+ const resourceAuthority = input.userAuthority;
36
+ if (!delegation || !resourceAuthority) {
37
+ throw new ConnectionAuthorityInvariantError("user_authority_missing");
38
+ }
39
+ if (!connection.subjectId || !connection.ownerOrganizationMembershipId) {
40
+ throw new ConnectionAuthorityInvariantError("personal_owner_missing");
41
+ }
42
+ if (resourceAuthority.id !== delegation.authorityId || resourceAuthority.organizationId !== input.organizationId || resourceAuthority.resourceKind !== "connection" || resourceAuthority.resourceId !== connection.id || resourceAuthority.originWorkspaceId !== connection.workspaceId || resourceAuthority.ownerSubjectId !== connection.subjectId || resourceAuthority.ownerOrganizationMembershipId !== connection.ownerOrganizationMembershipId || resourceAuthority.generation !== delegation.authorityGeneration || resourceAuthority.status !== "active") {
43
+ throw new ConnectionAuthorityInvariantError("user_authority_mismatch");
44
+ }
45
+ userDelegation = delegation;
46
+ authoritySource = "user_delegation";
47
+ }
48
+ return ConnectionUseAuthoritySnapshot.parse({
49
+ organizationId: input.organizationId,
50
+ originWorkspaceId: connection.workspaceId,
51
+ targetWorkspaceId: input.targetWorkspaceId,
52
+ targetSessionId: input.targetSessionId,
53
+ targetSessionVisibility: input.targetSessionVisibility,
54
+ targetSessionAuthorityEpoch: input.targetSessionAuthorityEpoch,
55
+ acceptedWork: input.acceptedWork,
56
+ connectionId: connection.id,
57
+ connectionGeneration: connection.generation,
58
+ connectionStatus: "active",
59
+ providerDomain: connection.providerDomain.toLowerCase(),
60
+ connectionKind: connection.kind,
61
+ scope: authority.scope,
62
+ ownerSubjectId: connection.subjectId,
63
+ ownerOrganizationMembershipId: connection.ownerOrganizationMembershipId,
64
+ ownerMembershipAuthorizationRevision: authority.scope === "user" ? input.userAuthority.ownerMembershipAuthorizationRevision : null,
65
+ authoritySource,
66
+ selectionSources: input.selectionSources,
67
+ userDelegation
68
+ });
69
+ }
70
+ function denied(reason) {
71
+ return { status: "denied", reason };
72
+ }
73
+ function revalidateConnectionUseAuthority(input) {
74
+ const snapshot = ConnectionUseAuthoritySnapshot.parse(input.snapshot);
75
+ const live = input.live;
76
+ const now = input.now ?? /* @__PURE__ */ new Date();
77
+ if (live.organizationId !== snapshot.organizationId || live.targetWorkspaceId !== snapshot.targetWorkspaceId) {
78
+ return denied("tenant_mismatch");
79
+ }
80
+ if (!live.targetWorkspaceAccessActive) return denied("workspace_access_inactive");
81
+ if (!live.session.active) return denied("session_inactive");
82
+ if (live.session.id !== snapshot.targetSessionId || live.session.organizationId !== snapshot.organizationId || live.session.workspaceId !== snapshot.targetWorkspaceId) {
83
+ return denied("session_identity_changed");
84
+ }
85
+ if (live.session.visibility !== snapshot.targetSessionVisibility) {
86
+ return denied("session_visibility_changed");
87
+ }
88
+ if (live.session.authorityEpoch !== snapshot.targetSessionAuthorityEpoch) {
89
+ return denied("session_authority_epoch_changed");
90
+ }
91
+ const connection = live.connection;
92
+ if (!connection) return denied("connection_missing");
93
+ if (connection.id !== snapshot.connectionId || connection.organizationId !== snapshot.organizationId || connection.workspaceId !== snapshot.originWorkspaceId || connection.providerDomain.toLowerCase() !== snapshot.providerDomain || connection.kind !== snapshot.connectionKind) {
94
+ return denied("connection_identity_changed");
95
+ }
96
+ if (connection.generation !== snapshot.connectionGeneration) {
97
+ return denied("connection_generation_changed");
98
+ }
99
+ if (connection.status !== "active") return denied("connection_status_inactive");
100
+ if (connection.subjectId !== snapshot.ownerSubjectId || connection.ownerOrganizationMembershipId !== snapshot.ownerOrganizationMembershipId) {
101
+ return denied("connection_owner_changed");
102
+ }
103
+ if (snapshot.scope === "workspace") {
104
+ if (connection.subjectId !== null || connection.workspaceId !== snapshot.targetWorkspaceId) {
105
+ return denied("connection_owner_changed");
106
+ }
107
+ return {
108
+ status: "authorized",
109
+ attribution: ConnectionUseAttribution.parse({
110
+ organizationId: snapshot.organizationId,
111
+ workspaceId: snapshot.targetWorkspaceId,
112
+ sessionId: snapshot.targetSessionId,
113
+ connectionId: snapshot.connectionId,
114
+ connectionGeneration: snapshot.connectionGeneration,
115
+ scope: "workspace",
116
+ ownerSubjectId: null,
117
+ authorityId: null,
118
+ grantId: null
119
+ })
120
+ };
121
+ }
122
+ const delegation = snapshot.userDelegation;
123
+ const membership = live.ownerMembership;
124
+ if (!membership || membership.status !== "active" || membership.id !== snapshot.ownerOrganizationMembershipId || membership.organizationId !== snapshot.organizationId || membership.subjectId !== snapshot.ownerSubjectId || membership.authorizationRevision !== snapshot.ownerMembershipAuthorizationRevision) {
125
+ return denied("owner_membership_inactive");
126
+ }
127
+ const authority = live.userAuthority;
128
+ if (!authority) return denied("authority_missing");
129
+ if (authority.id !== delegation.authorityId || authority.organizationId !== snapshot.organizationId || authority.resourceKind !== "connection" || authority.resourceId !== snapshot.connectionId || authority.originWorkspaceId !== snapshot.originWorkspaceId || authority.ownerSubjectId !== snapshot.ownerSubjectId || authority.ownerOrganizationMembershipId !== snapshot.ownerOrganizationMembershipId) {
130
+ return denied("authority_identity_changed");
131
+ }
132
+ if (authority.generation !== delegation.authorityGeneration) {
133
+ return denied("authority_generation_changed");
134
+ }
135
+ if (authority.status !== "active") return denied("authority_status_inactive");
136
+ const grant = live.grant;
137
+ if (!grant) return denied("grant_missing");
138
+ if (grant.id !== delegation.grantId || grant.authorityId !== delegation.authorityId || grant.organizationId !== snapshot.organizationId || grant.workspaceId !== snapshot.targetWorkspaceId || grant.sessionId !== delegation.sessionId || grant.action !== "connection.use" || grant.mode !== delegation.mode || grant.context !== snapshot.targetSessionVisibility || grant.authorityEpoch !== delegation.authorityEpoch) {
139
+ return denied("grant_identity_changed");
140
+ }
141
+ if (grant.generation !== delegation.grantGeneration) {
142
+ return denied("grant_generation_changed");
143
+ }
144
+ if (grant.status !== "active") return denied("grant_status_inactive");
145
+ if (grant.expiresAt !== null && Date.parse(grant.expiresAt) <= now.getTime()) {
146
+ return denied("grant_expired");
147
+ }
148
+ if (grant.mode === "once" && grant.consumed) return denied("grant_already_consumed");
149
+ return {
150
+ status: "authorized",
151
+ attribution: ConnectionUseAttribution.parse({
152
+ organizationId: snapshot.organizationId,
153
+ workspaceId: snapshot.targetWorkspaceId,
154
+ sessionId: snapshot.targetSessionId,
155
+ connectionId: snapshot.connectionId,
156
+ connectionGeneration: snapshot.connectionGeneration,
157
+ scope: "user",
158
+ ownerSubjectId: snapshot.ownerSubjectId,
159
+ authorityId: delegation.authorityId,
160
+ grantId: delegation.grantId
161
+ })
162
+ };
163
+ }
164
+ export {
165
+ ConnectionAuthorityInvariantError,
166
+ captureConnectionUseAuthority,
167
+ revalidateConnectionUseAuthority
168
+ };
169
+ //# sourceMappingURL=connection-authority.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/domain/connection-authority.ts"],"sourcesContent":["import type {\n ConnectionKind,\n ConnectionStatus,\n SessionTenancyVisibility,\n UserResourceDelegation,\n UserResourceGrantMode,\n UserResourceGrantStatus,\n} from \"@opengeni/contracts\";\nimport {\n ConnectionAuthorityEnvelope,\n ConnectionUseAttribution,\n ConnectionUseAuthoritySnapshot,\n type ConnectionUseAuthorizationResult,\n type ConnectionAuthoritySelectionSource,\n type ConnectionUseDenialReason,\n type ConnectionUseSelectionSource,\n} from \"@opengeni/contracts/connection-authority\";\n\nexport type ConnectionAuthorityCandidate = {\n id: string;\n organizationId: string;\n workspaceId: string;\n generation: number;\n status: ConnectionStatus;\n providerDomain: string;\n kind: ConnectionKind;\n subjectId: string | null;\n ownerOrganizationMembershipId: string | null;\n};\n\nexport type UserConnectionAuthorityCandidate = {\n id: string;\n organizationId: string;\n resourceKind: \"connection\";\n resourceId: string;\n originWorkspaceId: string;\n ownerSubjectId: string;\n ownerOrganizationMembershipId: string;\n ownerMembershipAuthorizationRevision: number;\n generation: number;\n status: \"active\" | \"retained\" | \"revoked\";\n};\n\nexport class ConnectionAuthorityInvariantError extends Error {\n constructor(readonly code: string) {\n super(`connection authority invariant failed: ${code}`);\n this.name = \"ConnectionAuthorityInvariantError\";\n }\n}\n\nexport function captureConnectionUseAuthority(input: {\n organizationId: string;\n targetWorkspaceId: string;\n targetSessionId: string;\n targetSessionVisibility: SessionTenancyVisibility;\n targetSessionAuthorityEpoch: number;\n acceptedWork:\n | { kind: \"turn\"; turnId: string }\n | {\n kind: \"scheduled_task\";\n taskId: string;\n taskAuthorityRevision: number;\n runId: string;\n };\n connection: ConnectionAuthorityCandidate;\n authority?: unknown;\n authorityWasOmitted?: boolean;\n userAuthority?: UserConnectionAuthorityCandidate | null;\n selectionSources: ConnectionUseSelectionSource[];\n}): ConnectionUseAuthoritySnapshot {\n const authority = ConnectionAuthorityEnvelope.parse(input.authority ?? {});\n const connection = input.connection;\n if (connection.organizationId !== input.organizationId) {\n throw new ConnectionAuthorityInvariantError(\"tenant_mismatch\");\n }\n if (connection.status !== \"active\") {\n throw new ConnectionAuthorityInvariantError(\"connection_status_inactive\");\n }\n\n let authoritySource: ConnectionAuthoritySelectionSource;\n let userDelegation: UserResourceDelegation | null = null;\n if (authority.scope === \"workspace\") {\n if (connection.subjectId !== null || connection.ownerOrganizationMembershipId !== null) {\n throw new ConnectionAuthorityInvariantError(\"workspace_borrowed_personal_connection\");\n }\n if (connection.workspaceId !== input.targetWorkspaceId) {\n throw new ConnectionAuthorityInvariantError(\"workspace_connection_scope_mismatch\");\n }\n authoritySource = input.authorityWasOmitted\n ? \"legacy_workspace_omission\"\n : \"explicit_workspace\";\n } else {\n const delegation = authority.userDelegation;\n const resourceAuthority = input.userAuthority;\n if (!delegation || !resourceAuthority) {\n throw new ConnectionAuthorityInvariantError(\"user_authority_missing\");\n }\n if (!connection.subjectId || !connection.ownerOrganizationMembershipId) {\n throw new ConnectionAuthorityInvariantError(\"personal_owner_missing\");\n }\n if (\n resourceAuthority.id !== delegation.authorityId ||\n resourceAuthority.organizationId !== input.organizationId ||\n resourceAuthority.resourceKind !== \"connection\" ||\n resourceAuthority.resourceId !== connection.id ||\n resourceAuthority.originWorkspaceId !== connection.workspaceId ||\n resourceAuthority.ownerSubjectId !== connection.subjectId ||\n resourceAuthority.ownerOrganizationMembershipId !==\n connection.ownerOrganizationMembershipId ||\n resourceAuthority.generation !== delegation.authorityGeneration ||\n resourceAuthority.status !== \"active\"\n ) {\n throw new ConnectionAuthorityInvariantError(\"user_authority_mismatch\");\n }\n userDelegation = delegation;\n authoritySource = \"user_delegation\";\n }\n\n return ConnectionUseAuthoritySnapshot.parse({\n organizationId: input.organizationId,\n originWorkspaceId: connection.workspaceId,\n targetWorkspaceId: input.targetWorkspaceId,\n targetSessionId: input.targetSessionId,\n targetSessionVisibility: input.targetSessionVisibility,\n targetSessionAuthorityEpoch: input.targetSessionAuthorityEpoch,\n acceptedWork: input.acceptedWork,\n connectionId: connection.id,\n connectionGeneration: connection.generation,\n connectionStatus: \"active\",\n providerDomain: connection.providerDomain.toLowerCase(),\n connectionKind: connection.kind,\n scope: authority.scope,\n ownerSubjectId: connection.subjectId,\n ownerOrganizationMembershipId: connection.ownerOrganizationMembershipId,\n ownerMembershipAuthorizationRevision:\n authority.scope === \"user\" ? input.userAuthority!.ownerMembershipAuthorizationRevision : null,\n authoritySource,\n selectionSources: input.selectionSources,\n userDelegation,\n });\n}\n\nexport type LiveConnectionUseState = {\n organizationId: string;\n targetWorkspaceId: string;\n targetWorkspaceAccessActive: boolean;\n session: {\n id: string;\n organizationId: string;\n workspaceId: string;\n visibility: SessionTenancyVisibility;\n authorityEpoch: number;\n active: boolean;\n };\n connection: ConnectionAuthorityCandidate | null;\n ownerMembership: {\n id: string;\n organizationId: string;\n subjectId: string;\n status: \"provisioning\" | \"active\" | \"suspended\" | \"revoked\";\n authorizationRevision: number;\n } | null;\n userAuthority: UserConnectionAuthorityCandidate | null;\n grant: {\n id: string;\n authorityId: string;\n organizationId: string;\n workspaceId: string;\n sessionId: string | null;\n action: string;\n mode: UserResourceGrantMode;\n context: SessionTenancyVisibility;\n authorityEpoch: number | null;\n generation: number;\n status: UserResourceGrantStatus;\n expiresAt: string | null;\n consumed: boolean;\n } | null;\n};\n\nfunction denied(reason: ConnectionUseDenialReason): ConnectionUseAuthorizationResult {\n return { status: \"denied\", reason };\n}\n\n/**\n * Revalidates every mutable fence immediately before provider use. Callers must\n * resolve the live state in one DB authority boundary; this pure function does\n * not cache, refresh, decrypt, or invoke a provider.\n */\nexport function revalidateConnectionUseAuthority(input: {\n snapshot: ConnectionUseAuthoritySnapshot;\n live: LiveConnectionUseState;\n now?: Date;\n}): ConnectionUseAuthorizationResult {\n const snapshot = ConnectionUseAuthoritySnapshot.parse(input.snapshot);\n const live = input.live;\n const now = input.now ?? new Date();\n if (\n live.organizationId !== snapshot.organizationId ||\n live.targetWorkspaceId !== snapshot.targetWorkspaceId\n ) {\n return denied(\"tenant_mismatch\");\n }\n if (!live.targetWorkspaceAccessActive) return denied(\"workspace_access_inactive\");\n if (!live.session.active) return denied(\"session_inactive\");\n if (\n live.session.id !== snapshot.targetSessionId ||\n live.session.organizationId !== snapshot.organizationId ||\n live.session.workspaceId !== snapshot.targetWorkspaceId\n ) {\n return denied(\"session_identity_changed\");\n }\n if (live.session.visibility !== snapshot.targetSessionVisibility) {\n return denied(\"session_visibility_changed\");\n }\n if (live.session.authorityEpoch !== snapshot.targetSessionAuthorityEpoch) {\n return denied(\"session_authority_epoch_changed\");\n }\n const connection = live.connection;\n if (!connection) return denied(\"connection_missing\");\n if (\n connection.id !== snapshot.connectionId ||\n connection.organizationId !== snapshot.organizationId ||\n connection.workspaceId !== snapshot.originWorkspaceId ||\n connection.providerDomain.toLowerCase() !== snapshot.providerDomain ||\n connection.kind !== snapshot.connectionKind\n ) {\n return denied(\"connection_identity_changed\");\n }\n if (connection.generation !== snapshot.connectionGeneration) {\n return denied(\"connection_generation_changed\");\n }\n if (connection.status !== \"active\") return denied(\"connection_status_inactive\");\n if (\n connection.subjectId !== snapshot.ownerSubjectId ||\n connection.ownerOrganizationMembershipId !== snapshot.ownerOrganizationMembershipId\n ) {\n return denied(\"connection_owner_changed\");\n }\n\n if (snapshot.scope === \"workspace\") {\n if (connection.subjectId !== null || connection.workspaceId !== snapshot.targetWorkspaceId) {\n return denied(\"connection_owner_changed\");\n }\n return {\n status: \"authorized\",\n attribution: ConnectionUseAttribution.parse({\n organizationId: snapshot.organizationId,\n workspaceId: snapshot.targetWorkspaceId,\n sessionId: snapshot.targetSessionId,\n connectionId: snapshot.connectionId,\n connectionGeneration: snapshot.connectionGeneration,\n scope: \"workspace\",\n ownerSubjectId: null,\n authorityId: null,\n grantId: null,\n }),\n };\n }\n\n const delegation = snapshot.userDelegation!;\n const membership = live.ownerMembership;\n if (\n !membership ||\n membership.status !== \"active\" ||\n membership.id !== snapshot.ownerOrganizationMembershipId ||\n membership.organizationId !== snapshot.organizationId ||\n membership.subjectId !== snapshot.ownerSubjectId ||\n membership.authorizationRevision !== snapshot.ownerMembershipAuthorizationRevision\n ) {\n return denied(\"owner_membership_inactive\");\n }\n const authority = live.userAuthority;\n if (!authority) return denied(\"authority_missing\");\n if (\n authority.id !== delegation.authorityId ||\n authority.organizationId !== snapshot.organizationId ||\n authority.resourceKind !== \"connection\" ||\n authority.resourceId !== snapshot.connectionId ||\n authority.originWorkspaceId !== snapshot.originWorkspaceId ||\n authority.ownerSubjectId !== snapshot.ownerSubjectId ||\n authority.ownerOrganizationMembershipId !== snapshot.ownerOrganizationMembershipId\n ) {\n return denied(\"authority_identity_changed\");\n }\n if (authority.generation !== delegation.authorityGeneration) {\n return denied(\"authority_generation_changed\");\n }\n if (authority.status !== \"active\") return denied(\"authority_status_inactive\");\n\n const grant = live.grant;\n if (!grant) return denied(\"grant_missing\");\n if (\n grant.id !== delegation.grantId ||\n grant.authorityId !== delegation.authorityId ||\n grant.organizationId !== snapshot.organizationId ||\n grant.workspaceId !== snapshot.targetWorkspaceId ||\n grant.sessionId !== delegation.sessionId ||\n grant.action !== \"connection.use\" ||\n grant.mode !== delegation.mode ||\n grant.context !== snapshot.targetSessionVisibility ||\n grant.authorityEpoch !== delegation.authorityEpoch\n ) {\n return denied(\"grant_identity_changed\");\n }\n if (grant.generation !== delegation.grantGeneration) {\n return denied(\"grant_generation_changed\");\n }\n if (grant.status !== \"active\") return denied(\"grant_status_inactive\");\n if (grant.expiresAt !== null && Date.parse(grant.expiresAt) <= now.getTime()) {\n return denied(\"grant_expired\");\n }\n if (grant.mode === \"once\" && grant.consumed) return denied(\"grant_already_consumed\");\n\n return {\n status: \"authorized\",\n attribution: ConnectionUseAttribution.parse({\n organizationId: snapshot.organizationId,\n workspaceId: snapshot.targetWorkspaceId,\n sessionId: snapshot.targetSessionId,\n connectionId: snapshot.connectionId,\n connectionGeneration: snapshot.connectionGeneration,\n scope: \"user\",\n ownerSubjectId: snapshot.ownerSubjectId,\n authorityId: delegation.authorityId,\n grantId: delegation.grantId,\n }),\n };\n}\n"],"mappings":"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","names":[]}
@@ -4,11 +4,10 @@ export declare const EDITABLE_ARTIFACT_MAX_OPERATIONS_PER_TRANSACTION = 4096;
4
4
  export declare const EDITABLE_ARTIFACT_MAX_UNDO_TARGETS_PER_TRANSACTION: number;
5
5
  export declare const EDITABLE_ARTIFACT_MAX_COMMAND_BYTES_PER_TRANSACTION: number;
6
6
  export declare const EDITABLE_ARTIFACT_MAX_INTENT_BYTES_PER_TRANSACTION: number;
7
- export declare const EDITABLE_ARTIFACT_INTENT_PROTOCOL_VERSION = 1;
8
7
  export declare const EDITABLE_ARTIFACT_MAX_OPERATION_BYTES_PER_TRANSACTION: number;
9
8
  /** Canonical OGACO envelope bound. Kept as an alias for existing consumers. */
10
9
  export declare const EDITABLE_ARTIFACT_MAX_COMMITTED_TRANSACTION_BYTES: number;
11
- export declare const EDITABLE_ARTIFACT_OPERATION_PROTOCOL_VERSION: 1;
10
+ export declare const EDITABLE_ARTIFACT_OPERATION_PROTOCOL_VERSION: 2;
12
11
  export declare const EDITABLE_ARTIFACT_MAX_COMMIT_ATTEMPTS = 4;
13
12
  /** Compact well before hard replay ceilings so one maximum transaction cannot cross them. */
14
13
  export declare const EDITABLE_ARTIFACT_COMPACTION_TRANSACTION_THRESHOLD = 1024;
@@ -9,7 +9,7 @@ export declare const assertAllowedEnvironmentVariableName: typeof assertAllowedV
9
9
  export declare function requireVariableSetEncryption(settings: Settings): Uint8Array;
10
10
  /** @deprecated use requireVariableSetEncryption */
11
11
  export declare const requireEnvironmentEncryption: typeof requireVariableSetEncryption;
12
- export declare function requireVariableSetForApi(db: Database, workspaceId: string, variableSetId: string): Promise<VariableSet>;
12
+ export declare function requireVariableSetForApi(db: Database, grant: AccessGrant, variableSetId: string): Promise<VariableSet>;
13
13
  /**
14
14
  * Validates an variableSet attachment supplied in a request payload (session
15
15
  * create, scheduled task create/update, pack enable). Requires the
@@ -0,0 +1,117 @@
1
+ import type { RepositoryResourceRef, ResourceRef } from "@opengeni/contracts";
2
+ import { type Database } from "@opengeni/db";
3
+ /**
4
+ * GitHub App repository-binding resolution for bare repository resources.
5
+ *
6
+ * A repository resource that names a GitHub repository by URI alone (no
7
+ * `githubInstallationId`/`githubRepositoryId`) never mints an installation
8
+ * token, so the sandbox clones it anonymously and cannot push or use `gh`.
9
+ * The workspace allowlist (`github_installation_repositories`) stores only
10
+ * numeric repository ids, so resolving a URI needs the installation's owner
11
+ * login from the durable binding plus one provider read for the repository id.
12
+ * This module owns the pure parts: URI parsing, candidate selection under the
13
+ * workspace's auditable bindings, and the exactly-one-match stamping rule. The
14
+ * provider read is injected so the worker can supply the live GitHub lookup
15
+ * while tests stay deterministic.
16
+ */
17
+ export type GitHubRepositoryCoordinates = {
18
+ owner: string;
19
+ name: string;
20
+ };
21
+ /**
22
+ * Parse a GitHub repository reference into owner/name, else null. Accepts
23
+ * `https://github.com/<owner>/<repo>[.git][/]`, `www.github.com`,
24
+ * `ssh://git@github.com/<owner>/<repo>[.git]`, and the scp-like
25
+ * `git@github.com:<owner>/<repo>[.git]`.
26
+ */
27
+ export declare function parseGitHubRepositoryCoordinates(uri: string): GitHubRepositoryCoordinates | null;
28
+ export type UnboundGitHubRepositoryResource = {
29
+ resource: RepositoryResourceRef;
30
+ owner: string;
31
+ name: string;
32
+ };
33
+ /**
34
+ * Repository resources that name a github.com repository without any
35
+ * platform identity: no GitHub App ids, no credential binding, no personal
36
+ * connection, and either the explicit `github` provider or no provider at all.
37
+ * Resources that already carry ids, legacy provider ids, a binding, or a
38
+ * connection are someone else's authority and are never rewritten here.
39
+ */
40
+ export declare function unboundGitHubRepositoryResources(resources: readonly ResourceRef[]): UnboundGitHubRepositoryResource[];
41
+ export type GitHubRepositoryBindingCandidate = {
42
+ installationId: number;
43
+ accountLogin: string;
44
+ repositoryIds: number[];
45
+ };
46
+ /**
47
+ * The workspace's auditable GitHub App bindings, under workspace RLS. Only
48
+ * bindings that could authorize a token mint are candidates; legacy
49
+ * `unverified` rows never resolve a URI.
50
+ */
51
+ export declare function listGitHubRepositoryBindingCandidates(db: Database, workspaceId: string): Promise<GitHubRepositoryBindingCandidate[]>;
52
+ export type GitHubRepositoryBindingLookup = (input: {
53
+ installationId: number;
54
+ owner: string;
55
+ name: string;
56
+ }) => Promise<{
57
+ id: number;
58
+ } | null>;
59
+ export type GitHubRepositoryBinding = {
60
+ installationId: number;
61
+ repositoryId: number;
62
+ };
63
+ export type GitHubRepositoryBindingOutcome = {
64
+ status: "resolved";
65
+ binding: GitHubRepositoryBinding;
66
+ }
67
+ /** No workspace binding owns this repository's account: anonymous, as before. */
68
+ | {
69
+ status: "unbound";
70
+ }
71
+ /** The account is bound, but no installation's allowlist holds this repository. */
72
+ | {
73
+ status: "not_allowlisted";
74
+ installationIds: number[];
75
+ }
76
+ /** More than one bound installation's allowlist holds this repository. */
77
+ | {
78
+ status: "ambiguous";
79
+ installationIds: number[];
80
+ }
81
+ /** The provider lookup failed (suspended/deleted installation, outage). */
82
+ | {
83
+ status: "unavailable";
84
+ installationIds: number[];
85
+ message: string;
86
+ };
87
+ export type GitHubRepositoryBindingResolution = {
88
+ uri: string;
89
+ owner: string;
90
+ name: string;
91
+ outcome: GitHubRepositoryBindingOutcome;
92
+ };
93
+ export type GitHubRepositoryBindingResolutionResult = {
94
+ resources: ResourceRef[];
95
+ bindings: Map<string, GitHubRepositoryBinding>;
96
+ resolutions: GitHubRepositoryBindingResolution[];
97
+ };
98
+ /**
99
+ * Resolve every bare github.com repository resource against the workspace's
100
+ * auditable bindings. A resource is stamped only when exactly one bound
101
+ * installation's allowlist contains the repository id GitHub reports for that
102
+ * URI through that installation. Every other case leaves the resource bare
103
+ * and reports why, so the caller can surface a warning without ever failing
104
+ * the turn or the session. Distinct URIs resolve concurrently, as do the
105
+ * candidate installations of one owner.
106
+ */
107
+ export declare function resolveGitHubRepositoryBindings(input: {
108
+ resources: readonly ResourceRef[];
109
+ candidates: readonly GitHubRepositoryBindingCandidate[];
110
+ lookup: GitHubRepositoryBindingLookup;
111
+ }): Promise<GitHubRepositoryBindingResolutionResult>;
112
+ /**
113
+ * Stamp resolved GitHub App ids onto the matching bare resources of any
114
+ * resource list (turn resources and runtime resources must agree, because the
115
+ * credential binding id and token filename derive from the installation id).
116
+ */
117
+ export declare function applyGitHubRepositoryBindings(resources: readonly ResourceRef[], bindings: ReadonlyMap<string, GitHubRepositoryBinding>): ResourceRef[];
@@ -0,0 +1,16 @@
1
+ import { type GovernedLearningActivationReceipt, type GovernedLearningActivationUndoReceipt } from "@opengeni/contracts";
2
+ import { activateGovernedLearningDecision, undoGovernedLearningActivation, type Database } from "@opengeni/db";
3
+ export declare function createGovernedLearningActivationController(options: {
4
+ db: Database;
5
+ activate?: typeof activateGovernedLearningDecision;
6
+ undo?: typeof undoGovernedLearningActivation;
7
+ }): {
8
+ activate(input: {
9
+ caller: unknown;
10
+ request: unknown;
11
+ }): Promise<GovernedLearningActivationReceipt>;
12
+ undo(input: {
13
+ caller: unknown;
14
+ request: unknown;
15
+ }): Promise<GovernedLearningActivationUndoReceipt>;
16
+ };
@@ -0,0 +1,6 @@
1
+ import { type GovernedLearningDecision as GovernedLearningDecisionType, type GovernedLearningDecisionFacts as GovernedLearningDecisionFactsType } from "@opengeni/contracts";
2
+ /**
3
+ * Pure, closed evaluator mirrored by the 0268 database authority. It never
4
+ * receives source content and cannot activate a destination authority.
5
+ */
6
+ export declare function deriveGovernedLearningDecision(input: GovernedLearningDecisionFactsType): GovernedLearningDecisionType;