@opengeni/db 0.28.9 → 0.36.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/dist/attached-browser-devices.d.ts +42 -0
- package/dist/attempt-tool-catalogs.d.ts +16 -0
- package/dist/browser-auth.d.ts +299 -0
- package/dist/browser-downloads.d.ts +51 -0
- package/dist/browser-identities.d.ts +137 -0
- package/dist/browser-sessions.d.ts +354 -0
- package/dist/browser-state-artifacts.d.ts +91 -0
- package/dist/canonical-human-identities.d.ts +48 -0
- package/dist/canonical-human-identities.js +28 -0
- package/dist/canonical-human-identities.js.map +1 -0
- package/dist/capability-components.d.ts +37 -0
- package/dist/capability-integrations.d.ts +194 -0
- package/dist/chunk-5KTIEDX7.js +13200 -0
- package/dist/chunk-5KTIEDX7.js.map +1 -0
- package/dist/chunk-CQZ4QSP5.js +995 -0
- package/dist/chunk-CQZ4QSP5.js.map +1 -0
- package/dist/chunk-F32YT7MP.js +117 -0
- package/dist/chunk-F32YT7MP.js.map +1 -0
- package/dist/chunk-JC5QPLUM.js +666 -0
- package/dist/chunk-JC5QPLUM.js.map +1 -0
- package/dist/chunk-P3RE4D2B.js +3535 -0
- package/dist/chunk-P3RE4D2B.js.map +1 -0
- package/dist/chunk-TAUTWOS5.js +259 -0
- package/dist/chunk-TAUTWOS5.js.map +1 -0
- package/dist/chunk-VKLB2ZGI.js +2389 -0
- package/dist/chunk-VKLB2ZGI.js.map +1 -0
- package/dist/codemode-operations.d.ts +117 -0
- package/dist/codex-token-resolver.d.ts +2 -2
- package/dist/company-profile-schema.d.ts +798 -0
- package/dist/company-profile.d.ts +268 -0
- package/dist/computer-sessions.d.ts +109 -0
- package/dist/connection-token-resolver.d.ts +9 -4
- package/dist/database.d.ts +57 -3
- package/dist/editable-artifact-durable-export.d.ts +153 -0
- package/dist/editable-artifact-durable-export.js +788 -0
- package/dist/editable-artifact-durable-export.js.map +1 -0
- package/dist/editable-artifact-materialization.d.ts +123 -0
- package/dist/editable-artifacts-schema.d.ts +4152 -0
- package/dist/editable-artifacts.d.ts +610 -0
- package/dist/editable-artifacts.js +26 -0
- package/dist/editable-artifacts.js.map +1 -0
- package/dist/generated-images.d.ts +139 -0
- package/dist/index.d.ts +1122 -126
- package/dist/index.js +42329 -18407
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +73 -10
- package/dist/integration-bindings.d.ts +70 -0
- package/dist/integration-facets.d.ts +99 -0
- package/dist/interaction-revisions.d.ts +14 -0
- package/dist/interaction-schema.d.ts +6036 -0
- package/dist/knowledge-source-sync-schema.d.ts +1679 -0
- package/dist/knowledge-source-sync.d.ts +274 -0
- package/dist/memory-governance.d.ts +2 -2
- package/dist/memory-slack-delivery.d.ts +101 -0
- package/dist/new-session-drafts.d.ts +2 -2
- package/dist/pack-components.d.ts +76 -0
- package/dist/pack-installations.d.ts +86 -0
- package/dist/persistence-errors.d.ts +3 -3
- package/dist/plugin-packages.d.ts +126 -0
- package/dist/preference-registry.d.ts +1 -1
- package/dist/provision-roles.d.ts +9 -1
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +19 -6
- package/dist/schema.d.ts +22654 -9359
- package/dist/schema.js +209 -3
- package/dist/scoped-knowledge.d.ts +97 -1
- package/dist/session-control.d.ts +33 -7
- package/dist/session-queue-commands.d.ts +19 -11
- package/dist/session-realtime-context.d.ts +2 -2
- package/dist/session-realtime-ledger.d.ts +4 -4
- package/dist/session-realtime-mirror.d.ts +2 -2
- package/dist/session-realtime-state.d.ts +1 -1
- package/dist/session-realtime-terminal.d.ts +1 -1
- package/dist/session-realtime.d.ts +7 -7
- package/dist/session-tenancy.d.ts +44 -0
- package/dist/session-tenancy.js +16 -0
- package/dist/session-tenancy.js.map +1 -0
- package/dist/session-tool-call-settlement.d.ts +1 -1
- package/dist/slack-task-policy-schema.d.ts +847 -0
- package/dist/slack-task-policy.d.ts +65 -0
- package/dist/slack-user-link-access.d.ts +54 -0
- package/dist/task-notes-schema.d.ts +924 -0
- package/dist/task-notes.d.ts +85 -0
- package/dist/transcription-recordings.d.ts +2 -2
- package/dist/turn-initiator.d.ts +2 -2
- package/dist/video-generation.d.ts +225 -0
- package/dist/video-generation.js +68 -0
- package/dist/video-generation.js.map +1 -0
- package/dist/workspace-artifacts.d.ts +1 -1
- package/dist/workspace-instruction-policies.d.ts +1 -1
- package/dist/workspace-learning-policy-schema.d.ts +932 -0
- package/dist/workspace-learning-policy.d.ts +161 -0
- package/dist/xai-subscription.d.ts +326 -0
- package/drizzle/0183_model_call_provider_cost_estimates.sql +39 -0
- package/drizzle/0184_sandbox_drain_teardown_fence.sql +682 -0
- package/drizzle/0185_temporal_schedule_cleanup_outbox.sql +168 -0
- package/drizzle/0186_sandbox_capture_provider_contract.sql +400 -0
- package/drizzle/0187_generated_image_artifacts.sql +173 -0
- package/drizzle/0188_image_generation_retention_failure.sql +25 -0
- package/drizzle/0189_retained_session_image_formats.sql +13 -0
- package/drizzle/0190_timeline_annotations.sql +31 -0
- package/drizzle/0191_editable_artifact_engine.sql +3853 -0
- package/drizzle/0192_editable_artifact_live_tickets.sql +338 -0
- package/drizzle/0193_editable_artifact_authorization.sql +190 -0
- package/drizzle/0194_editable_artifact_durable_exports.sql +160 -0
- package/drizzle/0195_editable_artifact_import_authorization.sql +203 -0
- package/drizzle/0196_rig_provider_images.sql +152 -0
- package/drizzle/0197_knowledge_source_sync_schedules.sql +523 -0
- package/drizzle/0198_memory_slack_publication_delivery.sql +393 -0
- package/drizzle/0199_workspace_learning_policy.sql +980 -0
- package/drizzle/0201_company_profile_authority.sql +849 -0
- package/drizzle/0202_document_index_checkpoints.sql +85 -0
- package/drizzle/0203_durable_video_generation.sql +353 -0
- package/drizzle/0204_video_generation_funding.sql +44 -0
- package/drizzle/0205_attempt_tool_catalogs.sql +230 -0
- package/drizzle/0206_browser_sessions.sql +646 -0
- package/drizzle/0207_browser_identities.sql +379 -0
- package/drizzle/0208_attached_browser_devices.sql +165 -0
- package/drizzle/0209_computer_sessions.sql +575 -0
- package/drizzle/0210_browser_auth_network_interventions.sql +441 -0
- package/drizzle/0211_editable_artifact_session_links.sql +58 -0
- package/drizzle/0212_browser_state_transfer_hardening.sql +560 -0
- package/drizzle/0212_slack_installation_bindings.sql +348 -0
- package/drizzle/0213_browser_interaction_authority.sql +67 -0
- package/drizzle/0213_slack_user_link_access_requests.sql +177 -0
- package/drizzle/0214_browser_download_saves.sql +34 -0
- package/drizzle/0214_session_activity_commit_gate.sql +262 -0
- package/drizzle/0215_browser_controller_host.sql +385 -0
- package/drizzle/0215_capabilities_platform.sql +980 -0
- package/drizzle/0216_browser_auth_health_evidence.sql +137 -0
- package/drizzle/0216_pack_component_ownership.sql +235 -0
- package/drizzle/0217_capability_definition_delete_authority.sql +32 -0
- package/drizzle/0217_external_browser_auth_operations.sql +22 -0
- package/drizzle/0218_organization_tenancy_foundation.sql +299 -0
- package/drizzle/0219_organization_tenancy_managed_human_provisioning.sql +317 -0
- package/drizzle/0219_site_auth_maintenance_sessions.sql +234 -0
- package/drizzle/0220_memory_slack_append_only_cascade.sql +38 -0
- package/drizzle/0220_session_channels.sql +54 -0
- package/drizzle/0221_sessions_channel_index.sql +5 -0
- package/drizzle/0222_session_visibility_authority_epochs.sql +158 -0
- package/drizzle/0222_sessions_channel_fk.sql +89 -0
- package/drizzle/0223_pending_tool_event_output.sql +55 -0
- package/drizzle/0223_sessions_channel_fk_validate.sql +118 -0
- package/drizzle/0224_slack_post_outcome_reconciliation.sql +123 -0
- package/drizzle/0225_session_visibility_fork_activation.sql +1363 -0
- package/drizzle/0226_personal_codex_authority_foundation.sql +365 -0
- package/drizzle/0227_slack_native_actions.sql +145 -0
- package/drizzle/0228_interaction_controller_data_plane.sql +14 -0
- package/drizzle/0228_slack_task_policy.sql +400 -0
- package/drizzle/0229_slack_inbox_file_fact.sql +10 -0
- package/drizzle/0230_user_scoped_variable_sets_rigs.sql +84 -0
- package/drizzle/0231_integration_definition_identity_cutover.sql +271 -0
- package/drizzle/0232_integration_facet_authority_cutover.sql +610 -0
- package/drizzle/0233_skill_and_integration_authority_cutover.sql +1162 -0
- package/drizzle/0234_xai_subscription_authority.sql +1139 -0
- package/drizzle/0235_canonical_human_login_bindings.sql +920 -0
- package/drizzle/0236_browser_identity_lifecycle.sql +39 -0
- package/drizzle/0236_session_visibility_slack_policy.sql +72 -0
- package/drizzle/0237_interaction_transition_reaper.sql +263 -0
- package/drizzle/0238_goal_persistence_policy.sql +471 -0
- package/drizzle/0238_supergrok_realtime_model.sql +29 -0
- package/drizzle/0239_supergrok_video_funding.sql +50 -0
- package/drizzle/0239_task_tree_notes.sql +990 -0
- package/package.json +26 -5
- package/src/attached-browser-devices.ts +371 -0
- package/src/attempt-tool-catalogs.ts +131 -0
- package/src/browser-auth.ts +4373 -0
- package/src/browser-downloads.ts +517 -0
- package/src/browser-identities.ts +1385 -0
- package/src/browser-sessions.ts +2294 -0
- package/src/browser-state-artifacts.ts +449 -0
- package/src/canonical-human-identities.ts +317 -0
- package/src/capability-components.ts +166 -0
- package/src/capability-integrations.ts +1571 -0
- package/src/codemode-operations.ts +674 -0
- package/src/company-profile-schema.ts +143 -0
- package/src/company-profile.ts +968 -0
- package/src/computer-sessions.ts +1130 -0
- package/src/connection-token-resolver.ts +270 -25
- package/src/database.ts +468 -24
- package/src/editable-artifact-durable-export.ts +1201 -0
- package/src/editable-artifact-materialization.ts +893 -0
- package/src/editable-artifacts-schema.ts +879 -0
- package/src/editable-artifacts.ts +5404 -0
- package/src/generated-images.ts +740 -0
- package/src/index.ts +9963 -1465
- package/src/insights.ts +251 -23
- package/src/integration-bindings.ts +455 -0
- package/src/integration-facets.ts +862 -0
- package/src/interaction-revisions.ts +70 -0
- package/src/interaction-schema.ts +1606 -0
- package/src/knowledge-source-sync-schema.ts +192 -0
- package/src/knowledge-source-sync.ts +1922 -0
- package/src/memory-slack-delivery.ts +801 -0
- package/src/pack-components.ts +954 -0
- package/src/pack-installations.ts +886 -0
- package/src/persistence-errors.ts +4 -7
- package/src/plugin-packages.ts +1186 -0
- package/src/provision-roles.ts +534 -1
- package/src/runtime-posture-cli.ts +9 -0
- package/src/runtime-posture.ts +721 -5
- package/src/schema.ts +6270 -3159
- package/src/scoped-knowledge-schema.ts +1 -1
- package/src/scoped-knowledge.ts +655 -3
- package/src/session-control.ts +130 -42
- package/src/session-queue-commands.ts +265 -15
- package/src/session-realtime-context.ts +2 -2
- package/src/session-realtime-ledger.ts +3 -3
- package/src/session-realtime.ts +7 -7
- package/src/session-tenancy.ts +188 -0
- package/src/session-tool-call-settlement.ts +18 -3
- package/src/slack-task-policy-schema.ts +130 -0
- package/src/slack-task-policy.ts +341 -0
- package/src/slack-user-link-access.ts +702 -0
- package/src/task-notes-schema.ts +135 -0
- package/src/task-notes.ts +178 -0
- package/src/video-generation.ts +1530 -0
- package/src/workspace-artifacts.ts +1 -1
- package/src/workspace-learning-policy-schema.ts +139 -0
- package/src/workspace-learning-policy.ts +595 -0
- package/src/xai-subscription.ts +1887 -0
- package/dist/chunk-P6CUHXQZ.js +0 -7248
- package/dist/chunk-P6CUHXQZ.js.map +0 -1
- package/dist/chunk-VHBFHMPC.js +0 -1219
- package/dist/chunk-VHBFHMPC.js.map +0 -1
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
WorkspaceLearningMode,
|
|
4
|
+
WorkspaceLearningPolicySnapshot,
|
|
5
|
+
WORKSPACE_LEARNING_POLICY_REASON_MAX_CHARS,
|
|
6
|
+
canonicalizeWorkspaceLearningSourceOverrides,
|
|
7
|
+
type WorkspaceLearningPolicyActivationEvent,
|
|
8
|
+
type WorkspaceLearningPolicyActivationType,
|
|
9
|
+
type WorkspaceLearningPolicyHead,
|
|
10
|
+
type WorkspaceLearningPolicyRevision,
|
|
11
|
+
type WorkspaceLearningSourceOverrideInput,
|
|
12
|
+
} from "@opengeni/contracts";
|
|
13
|
+
import { and, asc, desc, eq, sql } from "drizzle-orm";
|
|
14
|
+
import type { Database } from "./database";
|
|
15
|
+
import { rawRows, withRlsContext } from "./database";
|
|
16
|
+
import { nestedPostgresSqlState } from "./persistence-errors";
|
|
17
|
+
import * as schema from "./schema";
|
|
18
|
+
|
|
19
|
+
export class WorkspaceLearningPolicyConflictError extends Error {
|
|
20
|
+
readonly name = "WorkspaceLearningPolicyConflictError";
|
|
21
|
+
readonly code = "WORKSPACE_LEARNING_POLICY_CONFLICT";
|
|
22
|
+
|
|
23
|
+
constructor(readonly currentHead: WorkspaceLearningPolicyHead | null) {
|
|
24
|
+
super("The active workspace learning policy changed in another request");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class WorkspaceLearningPolicyOperationReuseError extends Error {
|
|
29
|
+
readonly name = "WorkspaceLearningPolicyOperationReuseError";
|
|
30
|
+
readonly code = "WORKSPACE_LEARNING_POLICY_OPERATION_REUSED";
|
|
31
|
+
|
|
32
|
+
constructor() {
|
|
33
|
+
super("The workspace learning-policy operation id was already used for another request");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class WorkspaceLearningPolicyNotFoundError extends Error {
|
|
38
|
+
readonly name = "WorkspaceLearningPolicyNotFoundError";
|
|
39
|
+
|
|
40
|
+
constructor(message = "Workspace learning-policy revision was not found") {
|
|
41
|
+
super(message);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class WorkspaceLearningPolicyInvalidOperationError extends Error {
|
|
46
|
+
readonly name = "WorkspaceLearningPolicyInvalidOperationError";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class WorkspaceLearningPolicyAuthorityError extends Error {
|
|
50
|
+
readonly name = "WorkspaceLearningPolicyAuthorityError";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type WorkspaceLearningPolicyAttemptClaims = {
|
|
54
|
+
accountId: string;
|
|
55
|
+
workspaceId: string;
|
|
56
|
+
sessionId: string;
|
|
57
|
+
turnId: string;
|
|
58
|
+
attemptId: string;
|
|
59
|
+
executionGeneration: number;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
type RevisionRow = typeof schema.workspaceLearningPolicyRevisions.$inferSelect;
|
|
63
|
+
type HeadRow = typeof schema.workspaceLearningPolicyHeads.$inferSelect;
|
|
64
|
+
type EventRow = typeof schema.workspaceLearningPolicyActivationEvents.$inferSelect;
|
|
65
|
+
type SnapshotRow = typeof schema.workspaceLearningPolicySnapshots.$inferSelect;
|
|
66
|
+
|
|
67
|
+
function iso(value: Date | string): string {
|
|
68
|
+
return (value instanceof Date ? value : new Date(value)).toISOString();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function revisionIdentity(row: RevisionRow) {
|
|
72
|
+
return { id: row.id, revision: row.revision, policyHash: row.policyHash };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function revisionFromRow(row: RevisionRow): WorkspaceLearningPolicyRevision {
|
|
76
|
+
return {
|
|
77
|
+
...revisionIdentity(row),
|
|
78
|
+
operationId: row.operationId,
|
|
79
|
+
accountId: row.accountId,
|
|
80
|
+
workspaceId: row.workspaceId,
|
|
81
|
+
workspaceMode: WorkspaceLearningMode.parse(row.workspaceMode),
|
|
82
|
+
sourceOverrides: canonicalizeWorkspaceLearningSourceOverrides(row.sourceOverrides),
|
|
83
|
+
supersedesRevisionId: row.supersedesRevisionId,
|
|
84
|
+
createdBySubjectId: row.createdBySubjectId,
|
|
85
|
+
createdAt: iso(row.createdAt),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function headFromRow(row: HeadRow): WorkspaceLearningPolicyHead {
|
|
90
|
+
return {
|
|
91
|
+
accountId: row.accountId,
|
|
92
|
+
workspaceId: row.workspaceId,
|
|
93
|
+
revisionId: row.revisionId,
|
|
94
|
+
revision: row.revision,
|
|
95
|
+
policyHash: row.policyHash,
|
|
96
|
+
activationVersion: row.activationVersion,
|
|
97
|
+
activatedAt: iso(row.activatedAt),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function headFromEventRow(row: EventRow): WorkspaceLearningPolicyHead {
|
|
102
|
+
return {
|
|
103
|
+
accountId: row.accountId,
|
|
104
|
+
workspaceId: row.workspaceId,
|
|
105
|
+
revisionId: row.newRevisionId,
|
|
106
|
+
revision: row.newRevision,
|
|
107
|
+
policyHash: row.newPolicyHash,
|
|
108
|
+
activationVersion: row.activationVersion,
|
|
109
|
+
activatedAt: iso(row.createdAt),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function eventFromRow(row: EventRow): WorkspaceLearningPolicyActivationEvent {
|
|
114
|
+
return {
|
|
115
|
+
id: row.id,
|
|
116
|
+
operationId: row.operationId,
|
|
117
|
+
accountId: row.accountId,
|
|
118
|
+
workspaceId: row.workspaceId,
|
|
119
|
+
type: row.type as WorkspaceLearningPolicyActivationType,
|
|
120
|
+
activationVersion: row.activationVersion,
|
|
121
|
+
oldRevision:
|
|
122
|
+
row.oldRevisionId === null
|
|
123
|
+
? null
|
|
124
|
+
: {
|
|
125
|
+
id: row.oldRevisionId,
|
|
126
|
+
revision: row.oldRevision!,
|
|
127
|
+
policyHash: row.oldPolicyHash!,
|
|
128
|
+
},
|
|
129
|
+
newRevision: {
|
|
130
|
+
id: row.newRevisionId,
|
|
131
|
+
revision: row.newRevision,
|
|
132
|
+
policyHash: row.newPolicyHash,
|
|
133
|
+
},
|
|
134
|
+
actorSubjectId: row.actorSubjectId,
|
|
135
|
+
reason: row.reason,
|
|
136
|
+
createdAt: iso(row.createdAt),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function snapshotFromRow(row: SnapshotRow): WorkspaceLearningPolicySnapshot {
|
|
141
|
+
return WorkspaceLearningPolicySnapshot.parse({
|
|
142
|
+
id: row.id,
|
|
143
|
+
accountId: row.accountId,
|
|
144
|
+
workspaceId: row.workspaceId,
|
|
145
|
+
sessionId: row.sessionId,
|
|
146
|
+
turnId: row.turnId,
|
|
147
|
+
attemptId: row.attemptId,
|
|
148
|
+
executionGeneration: row.executionGeneration,
|
|
149
|
+
revision:
|
|
150
|
+
row.revisionId === null
|
|
151
|
+
? null
|
|
152
|
+
: { id: row.revisionId, revision: row.revision, policyHash: row.policyHash },
|
|
153
|
+
activationVersion: row.activationVersion,
|
|
154
|
+
activatedAt: row.activatedAt === null ? null : iso(row.activatedAt),
|
|
155
|
+
workspaceMode: row.workspaceMode,
|
|
156
|
+
sourceOverrides: row.sourceOverrides,
|
|
157
|
+
snapshotHash: row.snapshotHash,
|
|
158
|
+
createdAt: iso(row.createdAt),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function operationFingerprint(operation: string, fields: readonly unknown[]): string {
|
|
163
|
+
return createHash("sha256")
|
|
164
|
+
.update(JSON.stringify(["workspace_learning_policy_operation", 1, operation, fields]), "utf8")
|
|
165
|
+
.digest("hex");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function requireHumanAuthority(input: {
|
|
169
|
+
actorSubjectId: string;
|
|
170
|
+
principalKind: string | undefined;
|
|
171
|
+
}): void {
|
|
172
|
+
if (input.principalKind !== "human_session" || input.actorSubjectId.trim().length === 0) {
|
|
173
|
+
throw new WorkspaceLearningPolicyAuthorityError(
|
|
174
|
+
"Workspace learning-policy changes require an exact authenticated human actor",
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function requireActivationInput(input: {
|
|
180
|
+
expectedActivationVersion: number;
|
|
181
|
+
actorSubjectId: string;
|
|
182
|
+
reason: string;
|
|
183
|
+
}): void {
|
|
184
|
+
if (
|
|
185
|
+
!Number.isSafeInteger(input.expectedActivationVersion) ||
|
|
186
|
+
input.expectedActivationVersion < 0
|
|
187
|
+
) {
|
|
188
|
+
throw new WorkspaceLearningPolicyInvalidOperationError(
|
|
189
|
+
"Workspace learning-policy activation version must be a nonnegative safe integer",
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
if (input.actorSubjectId.trim() !== input.actorSubjectId || input.actorSubjectId.length > 1_024) {
|
|
193
|
+
throw new WorkspaceLearningPolicyInvalidOperationError(
|
|
194
|
+
"Workspace learning-policy actor identity is invalid",
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
if (
|
|
198
|
+
input.reason.trim().length === 0 ||
|
|
199
|
+
input.reason.trim() !== input.reason ||
|
|
200
|
+
input.reason.length > WORKSPACE_LEARNING_POLICY_REASON_MAX_CHARS
|
|
201
|
+
) {
|
|
202
|
+
throw new WorkspaceLearningPolicyInvalidOperationError(
|
|
203
|
+
`Workspace learning-policy reason must contain 1-${WORKSPACE_LEARNING_POLICY_REASON_MAX_CHARS} non-edge-whitespace characters`,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function setHumanAuthority(
|
|
209
|
+
db: Database,
|
|
210
|
+
input: { actorSubjectId: string; principalKind: string },
|
|
211
|
+
): Promise<void> {
|
|
212
|
+
await db.execute(sql`select set_config('opengeni.subject_id', ${input.actorSubjectId}, true)`);
|
|
213
|
+
await db.execute(sql`select set_config('opengeni.principal_kind', ${input.principalKind}, true)`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function currentHeadInTransaction(
|
|
217
|
+
db: Database,
|
|
218
|
+
accountId: string,
|
|
219
|
+
workspaceId: string,
|
|
220
|
+
): Promise<WorkspaceLearningPolicyHead | null> {
|
|
221
|
+
const [row] = await db
|
|
222
|
+
.select()
|
|
223
|
+
.from(schema.workspaceLearningPolicyHeads)
|
|
224
|
+
.where(
|
|
225
|
+
and(
|
|
226
|
+
eq(schema.workspaceLearningPolicyHeads.accountId, accountId),
|
|
227
|
+
eq(schema.workspaceLearningPolicyHeads.workspaceId, workspaceId),
|
|
228
|
+
),
|
|
229
|
+
)
|
|
230
|
+
.limit(1);
|
|
231
|
+
return row ? headFromRow(row) : null;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async function lockWorkspace(
|
|
235
|
+
db: Database,
|
|
236
|
+
input: { accountId: string; workspaceId: string },
|
|
237
|
+
): Promise<void> {
|
|
238
|
+
const [workspace] = await db
|
|
239
|
+
.select({ id: schema.workspaces.id })
|
|
240
|
+
.from(schema.workspaces)
|
|
241
|
+
.where(
|
|
242
|
+
and(
|
|
243
|
+
eq(schema.workspaces.id, input.workspaceId),
|
|
244
|
+
eq(schema.workspaces.accountId, input.accountId),
|
|
245
|
+
),
|
|
246
|
+
)
|
|
247
|
+
.for("update")
|
|
248
|
+
.limit(1);
|
|
249
|
+
if (!workspace) throw new WorkspaceLearningPolicyNotFoundError("Workspace was not found");
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async function revisionByOperationInTransaction(
|
|
253
|
+
db: Database,
|
|
254
|
+
workspaceId: string,
|
|
255
|
+
operationId: string,
|
|
256
|
+
): Promise<RevisionRow | null> {
|
|
257
|
+
const [row] = await db
|
|
258
|
+
.select()
|
|
259
|
+
.from(schema.workspaceLearningPolicyRevisions)
|
|
260
|
+
.where(
|
|
261
|
+
and(
|
|
262
|
+
eq(schema.workspaceLearningPolicyRevisions.workspaceId, workspaceId),
|
|
263
|
+
eq(schema.workspaceLearningPolicyRevisions.operationId, operationId),
|
|
264
|
+
),
|
|
265
|
+
)
|
|
266
|
+
.limit(1);
|
|
267
|
+
return row ?? null;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function eventByOperationInTransaction(
|
|
271
|
+
db: Database,
|
|
272
|
+
workspaceId: string,
|
|
273
|
+
operationId: string,
|
|
274
|
+
): Promise<EventRow | null> {
|
|
275
|
+
const [row] = await db
|
|
276
|
+
.select()
|
|
277
|
+
.from(schema.workspaceLearningPolicyActivationEvents)
|
|
278
|
+
.where(
|
|
279
|
+
and(
|
|
280
|
+
eq(schema.workspaceLearningPolicyActivationEvents.workspaceId, workspaceId),
|
|
281
|
+
eq(schema.workspaceLearningPolicyActivationEvents.operationId, operationId),
|
|
282
|
+
),
|
|
283
|
+
)
|
|
284
|
+
.limit(1);
|
|
285
|
+
return row ?? null;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export async function createWorkspaceLearningPolicyRevision(
|
|
289
|
+
db: Database,
|
|
290
|
+
input: {
|
|
291
|
+
operationId?: string;
|
|
292
|
+
accountId: string;
|
|
293
|
+
workspaceId: string;
|
|
294
|
+
workspaceMode: "off" | "suggest" | "automatic";
|
|
295
|
+
sourceOverrides?: WorkspaceLearningSourceOverrideInput[];
|
|
296
|
+
supersedesRevisionId?: string | null;
|
|
297
|
+
actorSubjectId: string;
|
|
298
|
+
principalKind: string | undefined;
|
|
299
|
+
},
|
|
300
|
+
): Promise<WorkspaceLearningPolicyRevision> {
|
|
301
|
+
requireHumanAuthority(input);
|
|
302
|
+
const operationId = input.operationId ?? randomUUID();
|
|
303
|
+
const workspaceMode = WorkspaceLearningMode.parse(input.workspaceMode);
|
|
304
|
+
const sourceOverrides = canonicalizeWorkspaceLearningSourceOverrides(input.sourceOverrides ?? []);
|
|
305
|
+
const supersedesRevisionId = input.supersedesRevisionId ?? null;
|
|
306
|
+
const requestFingerprint = operationFingerprint("create_revision", [
|
|
307
|
+
input.accountId,
|
|
308
|
+
input.workspaceId,
|
|
309
|
+
workspaceMode,
|
|
310
|
+
sourceOverrides,
|
|
311
|
+
supersedesRevisionId,
|
|
312
|
+
input.actorSubjectId,
|
|
313
|
+
]);
|
|
314
|
+
|
|
315
|
+
return await withRlsContext(
|
|
316
|
+
db,
|
|
317
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
318
|
+
async (scopedDb) => {
|
|
319
|
+
// Revision creation and activation share this lock, making their
|
|
320
|
+
// workspace-local operation-id namespace exact under concurrency.
|
|
321
|
+
await lockWorkspace(scopedDb, input);
|
|
322
|
+
const existing = await revisionByOperationInTransaction(
|
|
323
|
+
scopedDb,
|
|
324
|
+
input.workspaceId,
|
|
325
|
+
operationId,
|
|
326
|
+
);
|
|
327
|
+
if (existing) {
|
|
328
|
+
if (existing.requestFingerprint !== requestFingerprint) {
|
|
329
|
+
throw new WorkspaceLearningPolicyOperationReuseError();
|
|
330
|
+
}
|
|
331
|
+
return revisionFromRow(existing);
|
|
332
|
+
}
|
|
333
|
+
const eventReuse = await eventByOperationInTransaction(
|
|
334
|
+
scopedDb,
|
|
335
|
+
input.workspaceId,
|
|
336
|
+
operationId,
|
|
337
|
+
);
|
|
338
|
+
if (eventReuse) throw new WorkspaceLearningPolicyOperationReuseError();
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
const [row] = await scopedDb
|
|
342
|
+
.insert(schema.workspaceLearningPolicyRevisions)
|
|
343
|
+
.values({
|
|
344
|
+
operationId,
|
|
345
|
+
requestFingerprint,
|
|
346
|
+
accountId: input.accountId,
|
|
347
|
+
workspaceId: input.workspaceId,
|
|
348
|
+
workspaceMode,
|
|
349
|
+
sourceOverrides,
|
|
350
|
+
policyHash: sql<string>`workspace_learning_policy_hash(${workspaceMode}, ${JSON.stringify(
|
|
351
|
+
sourceOverrides,
|
|
352
|
+
)}::jsonb)`,
|
|
353
|
+
supersedesRevisionId,
|
|
354
|
+
createdBySubjectId: input.actorSubjectId,
|
|
355
|
+
})
|
|
356
|
+
.onConflictDoNothing()
|
|
357
|
+
.returning();
|
|
358
|
+
if (row) return revisionFromRow(row);
|
|
359
|
+
const winner = await revisionByOperationInTransaction(
|
|
360
|
+
scopedDb,
|
|
361
|
+
input.workspaceId,
|
|
362
|
+
operationId,
|
|
363
|
+
);
|
|
364
|
+
if (!winner || winner.requestFingerprint !== requestFingerprint) {
|
|
365
|
+
throw new WorkspaceLearningPolicyOperationReuseError();
|
|
366
|
+
}
|
|
367
|
+
return revisionFromRow(winner);
|
|
368
|
+
} catch (error) {
|
|
369
|
+
if (nestedPostgresSqlState(error) === "23514") {
|
|
370
|
+
throw new WorkspaceLearningPolicyInvalidOperationError(
|
|
371
|
+
"Workspace learning-policy revision failed immutable scope validation",
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
throw error;
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
async function changeActiveWorkspaceLearningPolicy(
|
|
381
|
+
db: Database,
|
|
382
|
+
input: {
|
|
383
|
+
operationId?: string;
|
|
384
|
+
accountId: string;
|
|
385
|
+
workspaceId: string;
|
|
386
|
+
targetRevisionId: string;
|
|
387
|
+
expectedCurrentRevisionId: string | null;
|
|
388
|
+
expectedActivationVersion: number;
|
|
389
|
+
actorSubjectId: string;
|
|
390
|
+
principalKind: string | undefined;
|
|
391
|
+
reason: string;
|
|
392
|
+
type: WorkspaceLearningPolicyActivationType;
|
|
393
|
+
},
|
|
394
|
+
) {
|
|
395
|
+
requireHumanAuthority(input);
|
|
396
|
+
requireActivationInput(input);
|
|
397
|
+
const operationId = input.operationId ?? randomUUID();
|
|
398
|
+
const requestFingerprint = operationFingerprint(`change_active:${input.type}`, [
|
|
399
|
+
input.accountId,
|
|
400
|
+
input.workspaceId,
|
|
401
|
+
input.targetRevisionId,
|
|
402
|
+
input.expectedCurrentRevisionId,
|
|
403
|
+
input.expectedActivationVersion,
|
|
404
|
+
input.actorSubjectId,
|
|
405
|
+
input.reason,
|
|
406
|
+
]);
|
|
407
|
+
try {
|
|
408
|
+
return await withRlsContext(
|
|
409
|
+
db,
|
|
410
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
411
|
+
async (scopedDb) => {
|
|
412
|
+
await setHumanAuthority(scopedDb, {
|
|
413
|
+
actorSubjectId: input.actorSubjectId,
|
|
414
|
+
principalKind: input.principalKind!,
|
|
415
|
+
});
|
|
416
|
+
const [receipt] = await rawRows<{ eventId: string }>(
|
|
417
|
+
scopedDb,
|
|
418
|
+
sql`select event_id as "eventId"
|
|
419
|
+
from workspace_learning_policy_apply_activation(
|
|
420
|
+
${operationId}::uuid,
|
|
421
|
+
${requestFingerprint},
|
|
422
|
+
${input.accountId}::uuid,
|
|
423
|
+
${input.workspaceId}::uuid,
|
|
424
|
+
${input.targetRevisionId}::uuid,
|
|
425
|
+
${input.expectedCurrentRevisionId}::uuid,
|
|
426
|
+
${input.expectedActivationVersion}::bigint,
|
|
427
|
+
${input.type},
|
|
428
|
+
${input.actorSubjectId},
|
|
429
|
+
${input.reason}
|
|
430
|
+
)`,
|
|
431
|
+
);
|
|
432
|
+
if (!receipt) throw new Error("Workspace learning-policy activation returned no receipt");
|
|
433
|
+
const [event] = await scopedDb
|
|
434
|
+
.select()
|
|
435
|
+
.from(schema.workspaceLearningPolicyActivationEvents)
|
|
436
|
+
.where(eq(schema.workspaceLearningPolicyActivationEvents.id, receipt.eventId))
|
|
437
|
+
.limit(1);
|
|
438
|
+
if (!event) throw new Error("Workspace learning-policy activation was incomplete");
|
|
439
|
+
return { head: headFromEventRow(event), event: eventFromRow(event) };
|
|
440
|
+
},
|
|
441
|
+
);
|
|
442
|
+
} catch (error) {
|
|
443
|
+
const state = nestedPostgresSqlState(error);
|
|
444
|
+
if (state === "40001") {
|
|
445
|
+
const currentHead = await withRlsContext(
|
|
446
|
+
db,
|
|
447
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
448
|
+
async (scopedDb) =>
|
|
449
|
+
await currentHeadInTransaction(scopedDb, input.accountId, input.workspaceId),
|
|
450
|
+
);
|
|
451
|
+
throw new WorkspaceLearningPolicyConflictError(currentHead);
|
|
452
|
+
}
|
|
453
|
+
if (state === "42501") {
|
|
454
|
+
throw new WorkspaceLearningPolicyAuthorityError(
|
|
455
|
+
"Workspace learning-policy activation was not authorized",
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
if (state === "23503") throw new WorkspaceLearningPolicyNotFoundError();
|
|
459
|
+
if (state === "P1471") throw new WorkspaceLearningPolicyOperationReuseError();
|
|
460
|
+
if (state === "22023") {
|
|
461
|
+
throw new WorkspaceLearningPolicyInvalidOperationError(
|
|
462
|
+
"Workspace learning-policy activation input is invalid",
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
if (state === "23514") {
|
|
466
|
+
throw new WorkspaceLearningPolicyInvalidOperationError(
|
|
467
|
+
input.type === "rollback"
|
|
468
|
+
? "Workspace learning-policy rollback requires a distinct previously active revision"
|
|
469
|
+
: "Workspace learning-policy revision cannot be activated",
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
throw error;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export async function activateWorkspaceLearningPolicyRevision(
|
|
477
|
+
db: Database,
|
|
478
|
+
input: Omit<
|
|
479
|
+
Parameters<typeof changeActiveWorkspaceLearningPolicy>[1],
|
|
480
|
+
"type" | "targetRevisionId"
|
|
481
|
+
> & {
|
|
482
|
+
revisionId: string;
|
|
483
|
+
},
|
|
484
|
+
) {
|
|
485
|
+
return await changeActiveWorkspaceLearningPolicy(db, {
|
|
486
|
+
...input,
|
|
487
|
+
targetRevisionId: input.revisionId,
|
|
488
|
+
type: "activate",
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
export async function rollbackWorkspaceLearningPolicyRevision(
|
|
493
|
+
db: Database,
|
|
494
|
+
input: Omit<Parameters<typeof changeActiveWorkspaceLearningPolicy>[1], "type">,
|
|
495
|
+
) {
|
|
496
|
+
return await changeActiveWorkspaceLearningPolicy(db, { ...input, type: "rollback" });
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
export async function listWorkspaceLearningPolicyHistory(
|
|
500
|
+
db: Database,
|
|
501
|
+
input: { accountId: string; workspaceId: string; limit?: number },
|
|
502
|
+
): Promise<{
|
|
503
|
+
head: WorkspaceLearningPolicyHead | null;
|
|
504
|
+
revisions: WorkspaceLearningPolicyRevision[];
|
|
505
|
+
events: WorkspaceLearningPolicyActivationEvent[];
|
|
506
|
+
}> {
|
|
507
|
+
const limit = Math.max(1, Math.min(input.limit ?? 50, 100));
|
|
508
|
+
return await withRlsContext(
|
|
509
|
+
db,
|
|
510
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
511
|
+
async (scopedDb) => {
|
|
512
|
+
const [heads, revisions, events] = await Promise.all([
|
|
513
|
+
scopedDb
|
|
514
|
+
.select()
|
|
515
|
+
.from(schema.workspaceLearningPolicyHeads)
|
|
516
|
+
.where(eq(schema.workspaceLearningPolicyHeads.workspaceId, input.workspaceId))
|
|
517
|
+
.limit(1),
|
|
518
|
+
scopedDb
|
|
519
|
+
.select()
|
|
520
|
+
.from(schema.workspaceLearningPolicyRevisions)
|
|
521
|
+
.where(eq(schema.workspaceLearningPolicyRevisions.workspaceId, input.workspaceId))
|
|
522
|
+
.orderBy(desc(schema.workspaceLearningPolicyRevisions.revision))
|
|
523
|
+
.limit(limit),
|
|
524
|
+
scopedDb
|
|
525
|
+
.select()
|
|
526
|
+
.from(schema.workspaceLearningPolicyActivationEvents)
|
|
527
|
+
.where(eq(schema.workspaceLearningPolicyActivationEvents.workspaceId, input.workspaceId))
|
|
528
|
+
.orderBy(
|
|
529
|
+
desc(schema.workspaceLearningPolicyActivationEvents.activationVersion),
|
|
530
|
+
asc(schema.workspaceLearningPolicyActivationEvents.id),
|
|
531
|
+
)
|
|
532
|
+
.limit(limit),
|
|
533
|
+
]);
|
|
534
|
+
return {
|
|
535
|
+
head: heads[0] ? headFromRow(heads[0]) : null,
|
|
536
|
+
revisions: revisions.map(revisionFromRow),
|
|
537
|
+
events: events.map(eventFromRow),
|
|
538
|
+
};
|
|
539
|
+
},
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
export async function getOrCreateWorkspaceLearningPolicySnapshot(
|
|
544
|
+
db: Database,
|
|
545
|
+
claims: WorkspaceLearningPolicyAttemptClaims,
|
|
546
|
+
): Promise<WorkspaceLearningPolicySnapshot> {
|
|
547
|
+
try {
|
|
548
|
+
return await withRlsContext(
|
|
549
|
+
db,
|
|
550
|
+
{ accountId: claims.accountId, workspaceId: claims.workspaceId },
|
|
551
|
+
async (scopedDb) => {
|
|
552
|
+
const [receipt] = await rawRows<{ snapshotId: string }>(
|
|
553
|
+
scopedDb,
|
|
554
|
+
sql`select snapshot_id as "snapshotId"
|
|
555
|
+
from workspace_learning_policy_get_or_create_snapshot(
|
|
556
|
+
${claims.accountId}::uuid,
|
|
557
|
+
${claims.workspaceId}::uuid,
|
|
558
|
+
${claims.sessionId}::uuid,
|
|
559
|
+
${claims.turnId}::uuid,
|
|
560
|
+
${claims.attemptId}::uuid,
|
|
561
|
+
${claims.executionGeneration}::integer
|
|
562
|
+
)`,
|
|
563
|
+
);
|
|
564
|
+
if (!receipt) throw new Error("Workspace learning-policy snapshot returned no receipt");
|
|
565
|
+
const [row] = await scopedDb
|
|
566
|
+
.select()
|
|
567
|
+
.from(schema.workspaceLearningPolicySnapshots)
|
|
568
|
+
.where(
|
|
569
|
+
and(
|
|
570
|
+
eq(schema.workspaceLearningPolicySnapshots.id, receipt.snapshotId),
|
|
571
|
+
eq(schema.workspaceLearningPolicySnapshots.accountId, claims.accountId),
|
|
572
|
+
eq(schema.workspaceLearningPolicySnapshots.workspaceId, claims.workspaceId),
|
|
573
|
+
eq(schema.workspaceLearningPolicySnapshots.attemptId, claims.attemptId),
|
|
574
|
+
),
|
|
575
|
+
)
|
|
576
|
+
.limit(1);
|
|
577
|
+
if (!row) throw new Error("Workspace learning-policy snapshot was not recorded");
|
|
578
|
+
return snapshotFromRow(row);
|
|
579
|
+
},
|
|
580
|
+
);
|
|
581
|
+
} catch (error) {
|
|
582
|
+
const state = nestedPostgresSqlState(error);
|
|
583
|
+
if (state === "42501") {
|
|
584
|
+
throw new WorkspaceLearningPolicyAuthorityError(
|
|
585
|
+
"Workspace learning-policy snapshot was not authorized",
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
if (state === "23514") {
|
|
589
|
+
throw new WorkspaceLearningPolicyInvalidOperationError(
|
|
590
|
+
"Workspace learning-policy snapshot requires the exact active attempt",
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
throw error;
|
|
594
|
+
}
|
|
595
|
+
}
|