@opengeni/db 0.28.9 → 0.31.1
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 +91 -0
- package/dist/browser-identities.d.ts +128 -0
- package/dist/browser-sessions.d.ts +272 -0
- package/dist/browser-state-artifacts.d.ts +17 -0
- package/dist/capability-components.d.ts +37 -0
- package/dist/capability-integrations.d.ts +192 -0
- package/dist/chunk-F5FT7BEZ.js +3535 -0
- package/dist/chunk-F5FT7BEZ.js.map +1 -0
- package/dist/chunk-ISI2TIUG.js +639 -0
- package/dist/chunk-ISI2TIUG.js.map +1 -0
- package/dist/chunk-MWDVXQOL.js +11639 -0
- package/dist/chunk-MWDVXQOL.js.map +1 -0
- package/dist/chunk-NBE6CHRI.js +955 -0
- package/dist/chunk-NBE6CHRI.js.map +1 -0
- package/dist/{chunk-VHBFHMPC.js → chunk-YHZXQ7HP.js} +754 -11
- package/dist/chunk-YHZXQ7HP.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 +52 -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 +698 -111
- package/dist/index.js +27573 -10322
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +51 -4
- package/dist/integration-bindings.d.ts +70 -0
- package/dist/integration-features.d.ts +99 -0
- package/dist/interaction-revisions.d.ts +3 -0
- package/dist/interaction-schema.d.ts +5372 -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 +18 -6
- package/dist/schema.d.ts +18342 -9099
- package/dist/schema.js +165 -1
- package/dist/scoped-knowledge.d.ts +97 -1
- package/dist/session-control.d.ts +4 -4
- 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-tool-call-settlement.d.ts +1 -1
- package/dist/slack-user-link-access.d.ts +54 -0
- package/dist/transcription-recordings.d.ts +2 -2
- package/dist/turn-initiator.d.ts +2 -2
- package/dist/video-generation.d.ts +216 -0
- package/dist/video-generation.js +66 -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/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_slack_installation_bindings.sql +348 -0
- package/drizzle/0213_slack_user_link_access_requests.sql +177 -0
- package/drizzle/0214_session_activity_commit_gate.sql +262 -0
- package/drizzle/0215_capabilities_platform.sql +980 -0
- package/drizzle/0216_pack_component_ownership.sql +235 -0
- package/drizzle/0217_capability_definition_delete_authority.sql +32 -0
- package/package.json +18 -5
- package/src/attached-browser-devices.ts +371 -0
- package/src/attempt-tool-catalogs.ts +131 -0
- package/src/browser-auth.ts +1896 -0
- package/src/browser-identities.ts +1168 -0
- package/src/browser-sessions.ts +2005 -0
- package/src/browser-state-artifacts.ts +59 -0
- package/src/capability-components.ts +166 -0
- package/src/capability-integrations.ts +1663 -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 +427 -23
- 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 +5917 -1182
- package/src/insights.ts +160 -13
- package/src/integration-bindings.ts +455 -0
- package/src/integration-features.ts +859 -0
- package/src/interaction-revisions.ts +40 -0
- package/src/interaction-schema.ts +1303 -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 +929 -0
- package/src/persistence-errors.ts +4 -7
- package/src/plugin-packages.ts +1186 -0
- package/src/provision-roles.ts +302 -1
- package/src/runtime-posture-cli.ts +9 -0
- package/src/runtime-posture.ts +508 -5
- package/src/schema.ts +5117 -3119
- package/src/scoped-knowledge-schema.ts +1 -1
- package/src/scoped-knowledge.ts +655 -3
- package/src/session-control.ts +8 -5
- package/src/session-queue-commands.ts +187 -14
- 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-tool-call-settlement.ts +9 -0
- package/src/slack-user-link-access.ts +702 -0
- package/src/video-generation.ts +1452 -0
- package/src/workspace-learning-policy-schema.ts +139 -0
- package/src/workspace-learning-policy.ts +595 -0
- package/dist/chunk-P6CUHXQZ.js +0 -7248
- package/dist/chunk-P6CUHXQZ.js.map +0 -1
- package/dist/chunk-VHBFHMPC.js.map +0 -1
|
@@ -0,0 +1,801 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
stableJson,
|
|
4
|
+
type MemorySlackPublication,
|
|
5
|
+
type MemorySlackPublicationConfiguration,
|
|
6
|
+
type MemorySlackPublicationReceipt,
|
|
7
|
+
type MemorySlackPublicationReceiptKind,
|
|
8
|
+
type MemorySlackPublicationState,
|
|
9
|
+
} from "@opengeni/contracts";
|
|
10
|
+
import { and, asc, desc, eq, inArray, sql } from "drizzle-orm";
|
|
11
|
+
import { withWorkspaceRls, type Database } from "./database";
|
|
12
|
+
import * as schema from "./schema";
|
|
13
|
+
|
|
14
|
+
export type MemorySlackPublicationActor = {
|
|
15
|
+
kind: "human" | "agent" | "service";
|
|
16
|
+
subjectId: string;
|
|
17
|
+
initiatingHumanSubjectId?: string | null;
|
|
18
|
+
sessionId?: string | null;
|
|
19
|
+
turnId?: string | null;
|
|
20
|
+
attemptId?: string | null;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type WorkspaceMemorySlackPublicationSnapshot = {
|
|
24
|
+
accountId: string;
|
|
25
|
+
workspaceId: string;
|
|
26
|
+
id: string;
|
|
27
|
+
version: number;
|
|
28
|
+
status: string;
|
|
29
|
+
kind: string;
|
|
30
|
+
scopeType: string;
|
|
31
|
+
namespace: string;
|
|
32
|
+
labels: string[];
|
|
33
|
+
validFrom: string;
|
|
34
|
+
validUntil: string | null;
|
|
35
|
+
supersedesId: string | null;
|
|
36
|
+
supersededById: string | null;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export async function getWorkspaceMemorySlackPublicationSnapshot(
|
|
40
|
+
db: Database,
|
|
41
|
+
workspaceId: string,
|
|
42
|
+
memoryId: string,
|
|
43
|
+
): Promise<WorkspaceMemorySlackPublicationSnapshot | null> {
|
|
44
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
45
|
+
const [row] = await scopedDb
|
|
46
|
+
.select({
|
|
47
|
+
accountId: schema.knowledgeMemories.accountId,
|
|
48
|
+
workspaceId: schema.knowledgeMemories.workspaceId,
|
|
49
|
+
id: schema.knowledgeMemories.id,
|
|
50
|
+
version: schema.knowledgeMemories.memoryVersion,
|
|
51
|
+
status: schema.knowledgeMemories.status,
|
|
52
|
+
kind: schema.knowledgeMemories.kind,
|
|
53
|
+
scopeType: schema.knowledgeMemories.scopeType,
|
|
54
|
+
namespace: schema.knowledgeMemories.namespace,
|
|
55
|
+
labels: schema.knowledgeMemories.labels,
|
|
56
|
+
validFrom: schema.knowledgeMemories.validFrom,
|
|
57
|
+
validUntil: schema.knowledgeMemories.validUntil,
|
|
58
|
+
supersedesId: schema.knowledgeMemories.supersedesId,
|
|
59
|
+
supersededById: schema.knowledgeMemories.supersededById,
|
|
60
|
+
})
|
|
61
|
+
.from(schema.knowledgeMemories)
|
|
62
|
+
.where(
|
|
63
|
+
and(
|
|
64
|
+
eq(schema.knowledgeMemories.workspaceId, workspaceId),
|
|
65
|
+
eq(schema.knowledgeMemories.id, memoryId),
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
.limit(1);
|
|
69
|
+
return row
|
|
70
|
+
? {
|
|
71
|
+
...row,
|
|
72
|
+
labels: [...row.labels],
|
|
73
|
+
validFrom: row.validFrom.toISOString(),
|
|
74
|
+
validUntil: row.validUntil?.toISOString() ?? null,
|
|
75
|
+
}
|
|
76
|
+
: null;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type MemorySlackPublicationConfigurationInput = {
|
|
81
|
+
accountId: string;
|
|
82
|
+
workspaceId: string;
|
|
83
|
+
expectedRevision: number;
|
|
84
|
+
enabled: boolean;
|
|
85
|
+
connectionId: string | null;
|
|
86
|
+
slackTeamId: string | null;
|
|
87
|
+
slackChannelId: string | null;
|
|
88
|
+
slackChannelName: string | null;
|
|
89
|
+
autoImportances: Array<"major" | "normal" | "minor">;
|
|
90
|
+
reviewImportances: Array<"major" | "normal" | "minor">;
|
|
91
|
+
subjectId: string;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export class MemorySlackPublicationRevisionConflictError extends Error {
|
|
95
|
+
constructor(readonly currentRevision: number) {
|
|
96
|
+
super(`Memory Slack publication configuration moved to revision ${currentRevision}`);
|
|
97
|
+
this.name = "MemorySlackPublicationRevisionConflictError";
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export async function listMemorySlackPublicationConfigurations(
|
|
102
|
+
db: Database,
|
|
103
|
+
workspaceId: string,
|
|
104
|
+
limit = 25,
|
|
105
|
+
): Promise<MemorySlackPublicationConfiguration[]> {
|
|
106
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
107
|
+
const rows = await scopedDb
|
|
108
|
+
.select()
|
|
109
|
+
.from(schema.memorySlackPublicationConfigurations)
|
|
110
|
+
.where(eq(schema.memorySlackPublicationConfigurations.workspaceId, workspaceId))
|
|
111
|
+
.orderBy(desc(schema.memorySlackPublicationConfigurations.revision))
|
|
112
|
+
.limit(Math.min(Math.max(limit, 1), 100));
|
|
113
|
+
return rows.map(mapConfiguration);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function getCurrentMemorySlackPublicationConfiguration(
|
|
118
|
+
db: Database,
|
|
119
|
+
workspaceId: string,
|
|
120
|
+
): Promise<MemorySlackPublicationConfiguration | null> {
|
|
121
|
+
const configurations = await listMemorySlackPublicationConfigurations(db, workspaceId, 1);
|
|
122
|
+
return configurations[0] ?? null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export async function createMemorySlackPublicationConfiguration(
|
|
126
|
+
db: Database,
|
|
127
|
+
input: MemorySlackPublicationConfigurationInput,
|
|
128
|
+
): Promise<MemorySlackPublicationConfiguration> {
|
|
129
|
+
return await withWorkspaceRls(db, input.workspaceId, async (scopedDb) => {
|
|
130
|
+
await scopedDb
|
|
131
|
+
.select({ id: schema.workspaces.id })
|
|
132
|
+
.from(schema.workspaces)
|
|
133
|
+
.where(eq(schema.workspaces.id, input.workspaceId))
|
|
134
|
+
.for("update");
|
|
135
|
+
const [current] = await scopedDb
|
|
136
|
+
.select({ revision: schema.memorySlackPublicationConfigurations.revision })
|
|
137
|
+
.from(schema.memorySlackPublicationConfigurations)
|
|
138
|
+
.where(eq(schema.memorySlackPublicationConfigurations.workspaceId, input.workspaceId))
|
|
139
|
+
.orderBy(desc(schema.memorySlackPublicationConfigurations.revision))
|
|
140
|
+
.limit(1);
|
|
141
|
+
const currentRevision = current?.revision ?? 0;
|
|
142
|
+
if (currentRevision !== input.expectedRevision) {
|
|
143
|
+
throw new MemorySlackPublicationRevisionConflictError(currentRevision);
|
|
144
|
+
}
|
|
145
|
+
const [created] = await scopedDb
|
|
146
|
+
.insert(schema.memorySlackPublicationConfigurations)
|
|
147
|
+
.values({
|
|
148
|
+
accountId: input.accountId,
|
|
149
|
+
workspaceId: input.workspaceId,
|
|
150
|
+
revision: currentRevision + 1,
|
|
151
|
+
enabled: input.enabled,
|
|
152
|
+
connectionId: input.connectionId,
|
|
153
|
+
slackTeamId: input.slackTeamId,
|
|
154
|
+
slackChannelId: input.slackChannelId,
|
|
155
|
+
slackChannelName: input.slackChannelName,
|
|
156
|
+
autoImportances: normalizeImportances(input.autoImportances),
|
|
157
|
+
reviewImportances: normalizeImportances(input.reviewImportances),
|
|
158
|
+
createdBySubjectId: boundedSubject(input.subjectId),
|
|
159
|
+
})
|
|
160
|
+
.returning();
|
|
161
|
+
if (!created) throw new Error("Failed to create Memory Slack publication configuration");
|
|
162
|
+
return mapConfiguration(created);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export type EnqueueMemorySlackPublicationInput = {
|
|
167
|
+
accountId: string;
|
|
168
|
+
workspaceId: string;
|
|
169
|
+
sourceType: "workspace_memory" | "durable_learning";
|
|
170
|
+
sourceId: string;
|
|
171
|
+
sourceVersion?: string | null;
|
|
172
|
+
sourceIdempotencyKey: string;
|
|
173
|
+
projection: Record<string, unknown>;
|
|
174
|
+
importance: "major" | "normal" | "minor";
|
|
175
|
+
deliveryMode: "auto" | "review";
|
|
176
|
+
actor: MemorySlackPublicationActor;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export type EnqueueMemorySlackPublicationResult =
|
|
180
|
+
| { kind: "not_configured" }
|
|
181
|
+
| { kind: "disabled"; configuration: MemorySlackPublicationConfiguration }
|
|
182
|
+
| { kind: "enqueued" | "replayed"; publication: MemorySlackPublication };
|
|
183
|
+
|
|
184
|
+
export async function enqueueMemorySlackPublication(
|
|
185
|
+
db: Database,
|
|
186
|
+
input: EnqueueMemorySlackPublicationInput,
|
|
187
|
+
): Promise<EnqueueMemorySlackPublicationResult> {
|
|
188
|
+
return await withWorkspaceRls(db, input.workspaceId, async (scopedDb) => {
|
|
189
|
+
const [configurationRow] = await scopedDb
|
|
190
|
+
.select()
|
|
191
|
+
.from(schema.memorySlackPublicationConfigurations)
|
|
192
|
+
.where(eq(schema.memorySlackPublicationConfigurations.workspaceId, input.workspaceId))
|
|
193
|
+
.orderBy(desc(schema.memorySlackPublicationConfigurations.revision))
|
|
194
|
+
.limit(1);
|
|
195
|
+
if (!configurationRow) return { kind: "not_configured" };
|
|
196
|
+
const configuration = mapConfiguration(configurationRow);
|
|
197
|
+
if (
|
|
198
|
+
!configuration.enabled ||
|
|
199
|
+
!configuration.connectionId ||
|
|
200
|
+
!configuration.slackTeamId ||
|
|
201
|
+
!configuration.slackChannelId
|
|
202
|
+
) {
|
|
203
|
+
return { kind: "disabled", configuration };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const projectionJson = stableJson(input.projection);
|
|
207
|
+
const projectionSha256 = createHash("sha256").update(projectionJson, "utf8").digest("hex");
|
|
208
|
+
const operationId = deterministicUuid(
|
|
209
|
+
`memory-slack:${input.workspaceId}:${input.sourceIdempotencyKey}`,
|
|
210
|
+
);
|
|
211
|
+
const state = input.deliveryMode === "auto" ? "queued" : "review_pending";
|
|
212
|
+
const actor = normalizeActor(input.actor);
|
|
213
|
+
const [created] = await scopedDb
|
|
214
|
+
.insert(schema.memorySlackPublications)
|
|
215
|
+
.values({
|
|
216
|
+
accountId: input.accountId,
|
|
217
|
+
workspaceId: input.workspaceId,
|
|
218
|
+
configurationId: configuration.id,
|
|
219
|
+
configurationRevision: configuration.revision,
|
|
220
|
+
connectionId: configuration.connectionId,
|
|
221
|
+
slackTeamId: configuration.slackTeamId,
|
|
222
|
+
slackChannelId: configuration.slackChannelId,
|
|
223
|
+
sourceType: input.sourceType,
|
|
224
|
+
sourceId: bounded(input.sourceId, 1_024, "publication source id"),
|
|
225
|
+
sourceVersion: input.sourceVersion
|
|
226
|
+
? bounded(input.sourceVersion, 512, "publication source version")
|
|
227
|
+
: null,
|
|
228
|
+
sourceIdempotencyKey: bounded(
|
|
229
|
+
input.sourceIdempotencyKey,
|
|
230
|
+
256,
|
|
231
|
+
"publication source idempotency key",
|
|
232
|
+
),
|
|
233
|
+
projection: input.projection,
|
|
234
|
+
projectionSha256,
|
|
235
|
+
importance: input.importance,
|
|
236
|
+
deliveryMode: input.deliveryMode,
|
|
237
|
+
state,
|
|
238
|
+
operationId,
|
|
239
|
+
initiatorKind: actor.kind,
|
|
240
|
+
initiatorSubjectId: actor.subjectId,
|
|
241
|
+
initiatingHumanSubjectId: actor.initiatingHumanSubjectId,
|
|
242
|
+
sessionId: actor.sessionId,
|
|
243
|
+
turnId: actor.turnId,
|
|
244
|
+
attemptId: actor.attemptId,
|
|
245
|
+
})
|
|
246
|
+
.onConflictDoNothing({
|
|
247
|
+
target: [
|
|
248
|
+
schema.memorySlackPublications.workspaceId,
|
|
249
|
+
schema.memorySlackPublications.sourceIdempotencyKey,
|
|
250
|
+
],
|
|
251
|
+
})
|
|
252
|
+
.returning();
|
|
253
|
+
|
|
254
|
+
let publicationRow = created;
|
|
255
|
+
if (!publicationRow) {
|
|
256
|
+
[publicationRow] = await scopedDb
|
|
257
|
+
.select()
|
|
258
|
+
.from(schema.memorySlackPublications)
|
|
259
|
+
.where(
|
|
260
|
+
and(
|
|
261
|
+
eq(schema.memorySlackPublications.workspaceId, input.workspaceId),
|
|
262
|
+
eq(schema.memorySlackPublications.sourceIdempotencyKey, input.sourceIdempotencyKey),
|
|
263
|
+
),
|
|
264
|
+
)
|
|
265
|
+
.limit(1);
|
|
266
|
+
if (!publicationRow) throw new Error("Memory Slack publication conflict disappeared");
|
|
267
|
+
if (
|
|
268
|
+
publicationRow.projectionSha256 !== projectionSha256 ||
|
|
269
|
+
publicationRow.sourceType !== input.sourceType ||
|
|
270
|
+
publicationRow.sourceId !== input.sourceId ||
|
|
271
|
+
publicationRow.sourceVersion !== (input.sourceVersion ?? null) ||
|
|
272
|
+
publicationRow.importance !== input.importance ||
|
|
273
|
+
publicationRow.initiatorKind !== actor.kind ||
|
|
274
|
+
publicationRow.initiatorSubjectId !== actor.subjectId ||
|
|
275
|
+
publicationRow.initiatingHumanSubjectId !== actor.initiatingHumanSubjectId ||
|
|
276
|
+
publicationRow.sessionId !== actor.sessionId ||
|
|
277
|
+
publicationRow.turnId !== actor.turnId ||
|
|
278
|
+
publicationRow.attemptId !== actor.attemptId
|
|
279
|
+
) {
|
|
280
|
+
throw new Error("Memory Slack publication idempotency key was reused with different input");
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
kind: "replayed",
|
|
284
|
+
publication: await mapPublicationWithReceipts(scopedDb, publicationRow),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
await insertReceipt(scopedDb, publicationRow, {
|
|
289
|
+
kind: "enqueued",
|
|
290
|
+
state,
|
|
291
|
+
actorKind: actor.kind,
|
|
292
|
+
actorSubjectId: actor.subjectId,
|
|
293
|
+
});
|
|
294
|
+
return {
|
|
295
|
+
kind: "enqueued",
|
|
296
|
+
publication: await mapPublicationWithReceipts(scopedDb, publicationRow),
|
|
297
|
+
};
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export type ClaimedMemorySlackPublication = typeof schema.memorySlackPublications.$inferSelect;
|
|
302
|
+
|
|
303
|
+
export async function claimMemorySlackPublication(
|
|
304
|
+
db: Database,
|
|
305
|
+
claimHolderId: string,
|
|
306
|
+
claimLeaseMs: number,
|
|
307
|
+
): Promise<ClaimedMemorySlackPublication | null> {
|
|
308
|
+
const result = await db.execute(
|
|
309
|
+
sql`select * from opengeni_private.claim_memory_slack_publication(
|
|
310
|
+
${claimHolderId}::uuid,
|
|
311
|
+
${claimLeaseMs}
|
|
312
|
+
)`,
|
|
313
|
+
);
|
|
314
|
+
const row = rawRows(result)[0];
|
|
315
|
+
return row ? mapClaimedPublication(row) : null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export async function currentMemorySlackConfigurationMatches(
|
|
319
|
+
db: Database,
|
|
320
|
+
publication: ClaimedMemorySlackPublication,
|
|
321
|
+
): Promise<boolean> {
|
|
322
|
+
return await withWorkspaceRls(db, publication.workspaceId, async (scopedDb) => {
|
|
323
|
+
const [current] = await scopedDb
|
|
324
|
+
.select()
|
|
325
|
+
.from(schema.memorySlackPublicationConfigurations)
|
|
326
|
+
.where(eq(schema.memorySlackPublicationConfigurations.workspaceId, publication.workspaceId))
|
|
327
|
+
.orderBy(desc(schema.memorySlackPublicationConfigurations.revision))
|
|
328
|
+
.limit(1);
|
|
329
|
+
return Boolean(
|
|
330
|
+
current?.enabled &&
|
|
331
|
+
current.revision === publication.configurationRevision &&
|
|
332
|
+
current.id === publication.configurationId &&
|
|
333
|
+
current.connectionId === publication.connectionId &&
|
|
334
|
+
current.slackTeamId === publication.slackTeamId &&
|
|
335
|
+
current.slackChannelId === publication.slackChannelId,
|
|
336
|
+
);
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export async function completeMemorySlackPublication(
|
|
341
|
+
db: Database,
|
|
342
|
+
input: {
|
|
343
|
+
publication: ClaimedMemorySlackPublication;
|
|
344
|
+
claimHolderId: string;
|
|
345
|
+
slackChannelId: string;
|
|
346
|
+
slackMessageTimestamp: string;
|
|
347
|
+
},
|
|
348
|
+
): Promise<boolean> {
|
|
349
|
+
return await transitionClaimedPublication(db, input.publication, input.claimHolderId, {
|
|
350
|
+
state: "delivered",
|
|
351
|
+
kind: "delivered",
|
|
352
|
+
actorKind: "service",
|
|
353
|
+
actorSubjectId: "memory-slack-delivery",
|
|
354
|
+
slackChannelId: input.slackChannelId,
|
|
355
|
+
slackMessageTimestamp: input.slackMessageTimestamp,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export async function deferMemorySlackPublication(
|
|
360
|
+
db: Database,
|
|
361
|
+
input: {
|
|
362
|
+
publication: ClaimedMemorySlackPublication;
|
|
363
|
+
claimHolderId: string;
|
|
364
|
+
retryAt: Date;
|
|
365
|
+
errorCode: string;
|
|
366
|
+
},
|
|
367
|
+
): Promise<boolean> {
|
|
368
|
+
return await transitionClaimedPublication(db, input.publication, input.claimHolderId, {
|
|
369
|
+
state: "retry_wait",
|
|
370
|
+
kind: "retry_scheduled",
|
|
371
|
+
actorKind: "service",
|
|
372
|
+
actorSubjectId: "memory-slack-delivery",
|
|
373
|
+
retryAt: input.retryAt,
|
|
374
|
+
errorCode: input.errorCode,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export async function failMemorySlackPublication(
|
|
379
|
+
db: Database,
|
|
380
|
+
input: {
|
|
381
|
+
publication: ClaimedMemorySlackPublication;
|
|
382
|
+
claimHolderId: string;
|
|
383
|
+
errorCode: string;
|
|
384
|
+
cancelled?: boolean;
|
|
385
|
+
},
|
|
386
|
+
): Promise<boolean> {
|
|
387
|
+
return await transitionClaimedPublication(db, input.publication, input.claimHolderId, {
|
|
388
|
+
state: input.cancelled ? "cancelled" : "failed",
|
|
389
|
+
kind: input.cancelled ? "cancelled" : "failed",
|
|
390
|
+
actorKind: "service",
|
|
391
|
+
actorSubjectId: "memory-slack-delivery",
|
|
392
|
+
errorCode: input.errorCode,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export async function actOnMemorySlackPublication(
|
|
397
|
+
db: Database,
|
|
398
|
+
input: {
|
|
399
|
+
workspaceId: string;
|
|
400
|
+
publicationId: string;
|
|
401
|
+
expectedState: MemorySlackPublicationState;
|
|
402
|
+
action: "approve" | "reject" | "retry";
|
|
403
|
+
subjectId: string;
|
|
404
|
+
},
|
|
405
|
+
): Promise<MemorySlackPublication | null> {
|
|
406
|
+
return await withWorkspaceRls(db, input.workspaceId, async (scopedDb) => {
|
|
407
|
+
const [current] = await scopedDb
|
|
408
|
+
.select()
|
|
409
|
+
.from(schema.memorySlackPublications)
|
|
410
|
+
.where(
|
|
411
|
+
and(
|
|
412
|
+
eq(schema.memorySlackPublications.workspaceId, input.workspaceId),
|
|
413
|
+
eq(schema.memorySlackPublications.id, input.publicationId),
|
|
414
|
+
),
|
|
415
|
+
)
|
|
416
|
+
.for("update")
|
|
417
|
+
.limit(1);
|
|
418
|
+
if (!current || current.state !== input.expectedState) return null;
|
|
419
|
+
const transition = manualTransition(input.action, current.state);
|
|
420
|
+
if (!transition) return null;
|
|
421
|
+
const now = new Date();
|
|
422
|
+
const [updated] = await scopedDb
|
|
423
|
+
.update(schema.memorySlackPublications)
|
|
424
|
+
.set({
|
|
425
|
+
state: transition.state,
|
|
426
|
+
claimHolderId: null,
|
|
427
|
+
claimExpiresAt: null,
|
|
428
|
+
retryAt: null,
|
|
429
|
+
lastErrorCode: null,
|
|
430
|
+
...(transition.terminal ? { terminalAt: now } : { terminalAt: null }),
|
|
431
|
+
updatedAt: now,
|
|
432
|
+
})
|
|
433
|
+
.where(eq(schema.memorySlackPublications.id, current.id))
|
|
434
|
+
.returning();
|
|
435
|
+
if (!updated) return null;
|
|
436
|
+
await insertReceipt(scopedDb, updated, {
|
|
437
|
+
kind: transition.kind,
|
|
438
|
+
state: transition.state,
|
|
439
|
+
actorKind: "human",
|
|
440
|
+
actorSubjectId: boundedSubject(input.subjectId),
|
|
441
|
+
});
|
|
442
|
+
return await mapPublicationWithReceipts(scopedDb, updated);
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
export async function listMemorySlackPublications(
|
|
447
|
+
db: Database,
|
|
448
|
+
workspaceId: string,
|
|
449
|
+
options: { limit?: number; states?: MemorySlackPublicationState[] } = {},
|
|
450
|
+
): Promise<MemorySlackPublication[]> {
|
|
451
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
452
|
+
const conditions = [eq(schema.memorySlackPublications.workspaceId, workspaceId)];
|
|
453
|
+
if (options.states?.length) {
|
|
454
|
+
conditions.push(inArray(schema.memorySlackPublications.state, options.states));
|
|
455
|
+
}
|
|
456
|
+
const rows = await scopedDb
|
|
457
|
+
.select()
|
|
458
|
+
.from(schema.memorySlackPublications)
|
|
459
|
+
.where(and(...conditions))
|
|
460
|
+
.orderBy(
|
|
461
|
+
desc(schema.memorySlackPublications.createdAt),
|
|
462
|
+
desc(schema.memorySlackPublications.id),
|
|
463
|
+
)
|
|
464
|
+
.limit(Math.min(Math.max(options.limit ?? 50, 1), 100));
|
|
465
|
+
return await Promise.all(rows.map((row) => mapPublicationWithReceipts(scopedDb, row)));
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
async function transitionClaimedPublication(
|
|
470
|
+
db: Database,
|
|
471
|
+
publication: ClaimedMemorySlackPublication,
|
|
472
|
+
claimHolderId: string,
|
|
473
|
+
transition: {
|
|
474
|
+
state: "retry_wait" | "delivered" | "failed" | "cancelled";
|
|
475
|
+
kind: "retry_scheduled" | "delivered" | "failed" | "cancelled";
|
|
476
|
+
actorKind: "service";
|
|
477
|
+
actorSubjectId: string;
|
|
478
|
+
retryAt?: Date;
|
|
479
|
+
errorCode?: string;
|
|
480
|
+
slackChannelId?: string;
|
|
481
|
+
slackMessageTimestamp?: string;
|
|
482
|
+
},
|
|
483
|
+
): Promise<boolean> {
|
|
484
|
+
return await withWorkspaceRls(db, publication.workspaceId, async (scopedDb) => {
|
|
485
|
+
const terminal = ["delivered", "failed", "cancelled"].includes(transition.state);
|
|
486
|
+
const now = new Date();
|
|
487
|
+
const [updated] = await scopedDb
|
|
488
|
+
.update(schema.memorySlackPublications)
|
|
489
|
+
.set({
|
|
490
|
+
state: transition.state,
|
|
491
|
+
claimHolderId: null,
|
|
492
|
+
claimExpiresAt: null,
|
|
493
|
+
retryAt: transition.retryAt ?? null,
|
|
494
|
+
lastErrorCode: transition.errorCode
|
|
495
|
+
? bounded(transition.errorCode, 128, "publication error code")
|
|
496
|
+
: null,
|
|
497
|
+
slackMessageTimestamp: transition.slackMessageTimestamp ?? null,
|
|
498
|
+
deliveredAt: transition.state === "delivered" ? now : null,
|
|
499
|
+
terminalAt: terminal ? now : null,
|
|
500
|
+
updatedAt: now,
|
|
501
|
+
})
|
|
502
|
+
.where(
|
|
503
|
+
and(
|
|
504
|
+
eq(schema.memorySlackPublications.id, publication.id),
|
|
505
|
+
eq(schema.memorySlackPublications.workspaceId, publication.workspaceId),
|
|
506
|
+
eq(schema.memorySlackPublications.state, "delivering"),
|
|
507
|
+
eq(schema.memorySlackPublications.claimHolderId, claimHolderId),
|
|
508
|
+
),
|
|
509
|
+
)
|
|
510
|
+
.returning();
|
|
511
|
+
if (!updated) return false;
|
|
512
|
+
await insertReceipt(scopedDb, updated, {
|
|
513
|
+
kind: transition.kind,
|
|
514
|
+
state: transition.state,
|
|
515
|
+
actorKind: transition.actorKind,
|
|
516
|
+
actorSubjectId: transition.actorSubjectId,
|
|
517
|
+
retryAt: transition.retryAt ?? null,
|
|
518
|
+
errorCode: transition.errorCode ?? null,
|
|
519
|
+
slackChannelId: transition.slackChannelId ?? null,
|
|
520
|
+
slackMessageTimestamp: transition.slackMessageTimestamp ?? null,
|
|
521
|
+
});
|
|
522
|
+
return true;
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
async function insertReceipt(
|
|
527
|
+
db: Database,
|
|
528
|
+
publication: typeof schema.memorySlackPublications.$inferSelect,
|
|
529
|
+
input: {
|
|
530
|
+
kind: MemorySlackPublicationReceiptKind;
|
|
531
|
+
state: MemorySlackPublicationState;
|
|
532
|
+
actorKind: "human" | "agent" | "service";
|
|
533
|
+
actorSubjectId: string;
|
|
534
|
+
retryAt?: Date | null;
|
|
535
|
+
errorCode?: string | null;
|
|
536
|
+
slackChannelId?: string | null;
|
|
537
|
+
slackMessageTimestamp?: string | null;
|
|
538
|
+
},
|
|
539
|
+
) {
|
|
540
|
+
const [{ nextSequence } = { nextSequence: 1 }] = await db
|
|
541
|
+
.select({
|
|
542
|
+
nextSequence: sql<number>`coalesce(max(${schema.memorySlackPublicationReceipts.sequence}), 0)::int + 1`,
|
|
543
|
+
})
|
|
544
|
+
.from(schema.memorySlackPublicationReceipts)
|
|
545
|
+
.where(eq(schema.memorySlackPublicationReceipts.publicationId, publication.id));
|
|
546
|
+
await db.insert(schema.memorySlackPublicationReceipts).values({
|
|
547
|
+
accountId: publication.accountId,
|
|
548
|
+
workspaceId: publication.workspaceId,
|
|
549
|
+
publicationId: publication.id,
|
|
550
|
+
sequence: Number(nextSequence),
|
|
551
|
+
kind: input.kind,
|
|
552
|
+
state: input.state,
|
|
553
|
+
attemptNumber: publication.attemptCount,
|
|
554
|
+
actorKind: input.actorKind,
|
|
555
|
+
actorSubjectId: boundedSubject(input.actorSubjectId),
|
|
556
|
+
operationId: publication.operationId,
|
|
557
|
+
errorCode: input.errorCode
|
|
558
|
+
? bounded(input.errorCode, 128, "publication receipt error code")
|
|
559
|
+
: null,
|
|
560
|
+
retryAt: input.retryAt ?? null,
|
|
561
|
+
slackChannelId: input.slackChannelId ?? null,
|
|
562
|
+
slackMessageTimestamp: input.slackMessageTimestamp ?? null,
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function manualTransition(
|
|
567
|
+
action: "approve" | "reject" | "retry",
|
|
568
|
+
state: MemorySlackPublicationState,
|
|
569
|
+
): {
|
|
570
|
+
state: "queued" | "rejected";
|
|
571
|
+
kind: "review_approved" | "review_rejected" | "manual_retry";
|
|
572
|
+
terminal: boolean;
|
|
573
|
+
} | null {
|
|
574
|
+
if (action === "approve" && state === "review_pending") {
|
|
575
|
+
return { state: "queued", kind: "review_approved", terminal: false };
|
|
576
|
+
}
|
|
577
|
+
if (action === "reject" && state === "review_pending") {
|
|
578
|
+
return { state: "rejected", kind: "review_rejected", terminal: true };
|
|
579
|
+
}
|
|
580
|
+
if (action === "retry" && state === "failed") {
|
|
581
|
+
return { state: "queued", kind: "manual_retry", terminal: false };
|
|
582
|
+
}
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
async function mapPublicationWithReceipts(
|
|
587
|
+
db: Database,
|
|
588
|
+
row: typeof schema.memorySlackPublications.$inferSelect,
|
|
589
|
+
): Promise<MemorySlackPublication> {
|
|
590
|
+
const receiptRows = await db
|
|
591
|
+
.select()
|
|
592
|
+
.from(schema.memorySlackPublicationReceipts)
|
|
593
|
+
.where(eq(schema.memorySlackPublicationReceipts.publicationId, row.id))
|
|
594
|
+
.orderBy(asc(schema.memorySlackPublicationReceipts.sequence))
|
|
595
|
+
.limit(100);
|
|
596
|
+
const projection = row.projection;
|
|
597
|
+
const summary = typeof projection["summary"] === "string" ? projection["summary"] : "";
|
|
598
|
+
const authoritativePath = publicationAuthoritativePath(row, projection);
|
|
599
|
+
return {
|
|
600
|
+
id: row.id,
|
|
601
|
+
workspaceId: row.workspaceId,
|
|
602
|
+
configurationRevision: row.configurationRevision,
|
|
603
|
+
connectionId: row.connectionId,
|
|
604
|
+
slackTeamId: row.slackTeamId,
|
|
605
|
+
slackChannelId: row.slackChannelId,
|
|
606
|
+
sourceType: row.sourceType,
|
|
607
|
+
sourceId: row.sourceId,
|
|
608
|
+
sourceVersion: row.sourceVersion,
|
|
609
|
+
importance: row.importance,
|
|
610
|
+
deliveryMode: row.deliveryMode,
|
|
611
|
+
state: row.state,
|
|
612
|
+
summary: summary.slice(0, 512),
|
|
613
|
+
sourceLabel: row.sourceType === "workspace_memory" ? "Workspace Memory" : "Governed learning",
|
|
614
|
+
authoritativePath,
|
|
615
|
+
initiatorKind: row.initiatorKind,
|
|
616
|
+
initiatorSubjectId: row.initiatorSubjectId,
|
|
617
|
+
initiatingHumanSubjectId: row.initiatingHumanSubjectId,
|
|
618
|
+
attemptCount: row.attemptCount,
|
|
619
|
+
retryAt: row.retryAt?.toISOString() ?? null,
|
|
620
|
+
lastErrorCode: row.lastErrorCode,
|
|
621
|
+
slackMessageTimestamp: row.slackMessageTimestamp,
|
|
622
|
+
deliveredAt: row.deliveredAt?.toISOString() ?? null,
|
|
623
|
+
terminalAt: row.terminalAt?.toISOString() ?? null,
|
|
624
|
+
createdAt: row.createdAt.toISOString(),
|
|
625
|
+
updatedAt: row.updatedAt.toISOString(),
|
|
626
|
+
receipts: receiptRows.map(mapReceipt),
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function publicationAuthoritativePath(
|
|
631
|
+
row: typeof schema.memorySlackPublications.$inferSelect,
|
|
632
|
+
projection: Record<string, unknown>,
|
|
633
|
+
): string | null {
|
|
634
|
+
if (row.sourceType === "workspace_memory") {
|
|
635
|
+
const authoritative = record(projection["authoritativeRecord"]);
|
|
636
|
+
const memoryId = authoritative?.["memoryId"];
|
|
637
|
+
return typeof memoryId === "string"
|
|
638
|
+
? `/workspaces/${row.workspaceId}/memory?memoryId=${encodeURIComponent(memoryId)}`
|
|
639
|
+
: null;
|
|
640
|
+
}
|
|
641
|
+
return `/workspaces/${row.workspaceId}/workspace-state`;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function mapConfiguration(
|
|
645
|
+
row: typeof schema.memorySlackPublicationConfigurations.$inferSelect,
|
|
646
|
+
): MemorySlackPublicationConfiguration {
|
|
647
|
+
return {
|
|
648
|
+
id: row.id,
|
|
649
|
+
workspaceId: row.workspaceId,
|
|
650
|
+
revision: row.revision,
|
|
651
|
+
enabled: row.enabled,
|
|
652
|
+
connectionId: row.connectionId,
|
|
653
|
+
slackTeamId: row.slackTeamId,
|
|
654
|
+
slackChannelId: row.slackChannelId,
|
|
655
|
+
slackChannelName: row.slackChannelName,
|
|
656
|
+
autoImportances: normalizeImportances(row.autoImportances),
|
|
657
|
+
reviewImportances: normalizeImportances(row.reviewImportances),
|
|
658
|
+
createdBySubjectId: row.createdBySubjectId,
|
|
659
|
+
createdAt: row.createdAt.toISOString(),
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
function mapReceipt(
|
|
664
|
+
row: typeof schema.memorySlackPublicationReceipts.$inferSelect,
|
|
665
|
+
): MemorySlackPublicationReceipt {
|
|
666
|
+
return {
|
|
667
|
+
id: row.id,
|
|
668
|
+
publicationId: row.publicationId,
|
|
669
|
+
sequence: row.sequence,
|
|
670
|
+
kind: row.kind,
|
|
671
|
+
state: row.state,
|
|
672
|
+
attemptNumber: row.attemptNumber,
|
|
673
|
+
actorKind: row.actorKind,
|
|
674
|
+
actorSubjectId: row.actorSubjectId,
|
|
675
|
+
operationId: row.operationId,
|
|
676
|
+
errorCode: row.errorCode,
|
|
677
|
+
retryAt: row.retryAt?.toISOString() ?? null,
|
|
678
|
+
slackChannelId: row.slackChannelId,
|
|
679
|
+
slackMessageTimestamp: row.slackMessageTimestamp,
|
|
680
|
+
createdAt: row.createdAt.toISOString(),
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
function normalizeImportances(values: readonly string[]) {
|
|
685
|
+
const allowed = new Set(["major", "normal", "minor"] as const);
|
|
686
|
+
return [...new Set(values)]
|
|
687
|
+
.filter((value): value is "major" | "normal" | "minor" =>
|
|
688
|
+
allowed.has(value as "major" | "normal" | "minor"),
|
|
689
|
+
)
|
|
690
|
+
.sort();
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function normalizeActor(actor: MemorySlackPublicationActor) {
|
|
694
|
+
return {
|
|
695
|
+
kind: actor.kind,
|
|
696
|
+
subjectId: boundedSubject(actor.subjectId),
|
|
697
|
+
initiatingHumanSubjectId: actor.initiatingHumanSubjectId
|
|
698
|
+
? boundedSubject(actor.initiatingHumanSubjectId)
|
|
699
|
+
: null,
|
|
700
|
+
sessionId: actor.sessionId ?? null,
|
|
701
|
+
turnId: actor.turnId ?? null,
|
|
702
|
+
attemptId: actor.attemptId ?? null,
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
function boundedSubject(value: string) {
|
|
707
|
+
return bounded(value, 1_024, "publication actor subject id");
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
function bounded(value: string, maxBytes: number, label: string) {
|
|
711
|
+
const trimmed = value.trim();
|
|
712
|
+
if (!trimmed || Buffer.byteLength(trimmed, "utf8") > maxBytes) {
|
|
713
|
+
throw new Error(`${label} is invalid`);
|
|
714
|
+
}
|
|
715
|
+
return trimmed;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
function deterministicUuid(value: string) {
|
|
719
|
+
const bytes = createHash("sha256").update(value).digest().subarray(0, 16);
|
|
720
|
+
bytes[6] = (bytes[6]! & 0x0f) | 0x50;
|
|
721
|
+
bytes[8] = (bytes[8]! & 0x3f) | 0x80;
|
|
722
|
+
const hex = bytes.toString("hex");
|
|
723
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
function record(value: unknown): Record<string, unknown> | null {
|
|
727
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
728
|
+
? (value as Record<string, unknown>)
|
|
729
|
+
: null;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
function rawRows(value: unknown): Array<Record<string, unknown>> {
|
|
733
|
+
if (Array.isArray(value)) return value as Array<Record<string, unknown>>;
|
|
734
|
+
const rows = record(value)?.["rows"];
|
|
735
|
+
return Array.isArray(rows) ? (rows as Array<Record<string, unknown>>) : [];
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
function raw(row: Record<string, unknown>, camel: string, snake: string) {
|
|
739
|
+
return row[camel] ?? row[snake];
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
function mapClaimedPublication(row: Record<string, unknown>): ClaimedMemorySlackPublication {
|
|
743
|
+
const date = (camel: string, snake: string) => {
|
|
744
|
+
const value = raw(row, camel, snake);
|
|
745
|
+
return value instanceof Date ? value : new Date(String(value));
|
|
746
|
+
};
|
|
747
|
+
const nullableDate = (camel: string, snake: string) => {
|
|
748
|
+
const value = raw(row, camel, snake);
|
|
749
|
+
return value == null ? null : value instanceof Date ? value : new Date(String(value));
|
|
750
|
+
};
|
|
751
|
+
const string = (camel: string, snake: string) => String(raw(row, camel, snake));
|
|
752
|
+
const nullableString = (camel: string, snake: string) => {
|
|
753
|
+
const value = raw(row, camel, snake);
|
|
754
|
+
return value == null ? null : String(value);
|
|
755
|
+
};
|
|
756
|
+
return {
|
|
757
|
+
id: string("id", "id"),
|
|
758
|
+
accountId: string("accountId", "account_id"),
|
|
759
|
+
workspaceId: string("workspaceId", "workspace_id"),
|
|
760
|
+
configurationId: string("configurationId", "configuration_id"),
|
|
761
|
+
configurationRevision: Number(raw(row, "configurationRevision", "configuration_revision")),
|
|
762
|
+
connectionId: string("connectionId", "connection_id"),
|
|
763
|
+
slackTeamId: string("slackTeamId", "slack_team_id"),
|
|
764
|
+
slackChannelId: string("slackChannelId", "slack_channel_id"),
|
|
765
|
+
sourceType: string("sourceType", "source_type") as ClaimedMemorySlackPublication["sourceType"],
|
|
766
|
+
sourceId: string("sourceId", "source_id"),
|
|
767
|
+
sourceVersion: nullableString("sourceVersion", "source_version"),
|
|
768
|
+
sourceIdempotencyKey: string("sourceIdempotencyKey", "source_idempotency_key"),
|
|
769
|
+
projection: record(raw(row, "projection", "projection")) ?? {},
|
|
770
|
+
projectionSha256: string("projectionSha256", "projection_sha256"),
|
|
771
|
+
importance: string("importance", "importance") as ClaimedMemorySlackPublication["importance"],
|
|
772
|
+
deliveryMode: string(
|
|
773
|
+
"deliveryMode",
|
|
774
|
+
"delivery_mode",
|
|
775
|
+
) as ClaimedMemorySlackPublication["deliveryMode"],
|
|
776
|
+
state: string("state", "state") as ClaimedMemorySlackPublication["state"],
|
|
777
|
+
operationId: string("operationId", "operation_id"),
|
|
778
|
+
initiatorKind: string(
|
|
779
|
+
"initiatorKind",
|
|
780
|
+
"initiator_kind",
|
|
781
|
+
) as ClaimedMemorySlackPublication["initiatorKind"],
|
|
782
|
+
initiatorSubjectId: string("initiatorSubjectId", "initiator_subject_id"),
|
|
783
|
+
initiatingHumanSubjectId: nullableString(
|
|
784
|
+
"initiatingHumanSubjectId",
|
|
785
|
+
"initiating_human_subject_id",
|
|
786
|
+
),
|
|
787
|
+
sessionId: nullableString("sessionId", "session_id"),
|
|
788
|
+
turnId: nullableString("turnId", "turn_id"),
|
|
789
|
+
attemptId: nullableString("attemptId", "attempt_id"),
|
|
790
|
+
claimHolderId: nullableString("claimHolderId", "claim_holder_id"),
|
|
791
|
+
claimExpiresAt: nullableDate("claimExpiresAt", "claim_expires_at"),
|
|
792
|
+
attemptCount: Number(raw(row, "attemptCount", "attempt_count")),
|
|
793
|
+
retryAt: nullableDate("retryAt", "retry_at"),
|
|
794
|
+
lastErrorCode: nullableString("lastErrorCode", "last_error_code"),
|
|
795
|
+
slackMessageTimestamp: nullableString("slackMessageTimestamp", "slack_message_timestamp"),
|
|
796
|
+
deliveredAt: nullableDate("deliveredAt", "delivered_at"),
|
|
797
|
+
terminalAt: nullableDate("terminalAt", "terminal_at"),
|
|
798
|
+
createdAt: date("createdAt", "created_at"),
|
|
799
|
+
updatedAt: date("updatedAt", "updated_at"),
|
|
800
|
+
};
|
|
801
|
+
}
|