@opengeni/db 0.16.2 → 0.17.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/{chunk-4RUK5CON.js → chunk-T6RSJT6C.js} +20 -2
- package/dist/chunk-T6RSJT6C.js.map +1 -0
- package/dist/{chunk-HZD7KVAR.js → chunk-TLAC622R.js} +359 -17
- package/dist/chunk-TLAC622R.js.map +1 -0
- package/dist/index.d.ts +157 -0
- package/dist/index.js +3597 -2520
- package/dist/index.js.map +1 -1
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +3 -3
- package/dist/schema.d.ts +2555 -758
- package/dist/schema.js +15 -1
- package/dist/workspace-artifacts.d.ts +62 -0
- package/drizzle/0149_workspace_artifacts.sql +348 -0
- package/drizzle/0150_slack_task_interactions.sql +344 -0
- package/package.json +3 -3
- package/src/index.ts +924 -7
- package/src/runtime-posture.ts +19 -1
- package/src/schema.ts +399 -16
- package/src/workspace-artifacts.ts +751 -0
- package/dist/chunk-4RUK5CON.js.map +0 -1
- package/dist/chunk-HZD7KVAR.js.map +0 -1
package/src/index.ts
CHANGED
|
@@ -1190,6 +1190,8 @@ export const allWorkspacePermissions: Permission[] = [
|
|
|
1190
1190
|
"goals:manage",
|
|
1191
1191
|
"enrollments:read",
|
|
1192
1192
|
"enrollments:manage",
|
|
1193
|
+
"artifacts:read",
|
|
1194
|
+
"artifacts:publish",
|
|
1193
1195
|
];
|
|
1194
1196
|
|
|
1195
1197
|
export const allAccountPermissions: Permission[] = [
|
|
@@ -5297,6 +5299,881 @@ export async function revokeConnection(
|
|
|
5297
5299
|
);
|
|
5298
5300
|
}
|
|
5299
5301
|
|
|
5302
|
+
export type SlackInstallationRoute = {
|
|
5303
|
+
accountId: string;
|
|
5304
|
+
workspaceId: string;
|
|
5305
|
+
connectionId: string;
|
|
5306
|
+
botId: string;
|
|
5307
|
+
botUserId: string;
|
|
5308
|
+
};
|
|
5309
|
+
|
|
5310
|
+
export type SlackBotUserLink = {
|
|
5311
|
+
id: string;
|
|
5312
|
+
accountId: string;
|
|
5313
|
+
workspaceId: string;
|
|
5314
|
+
connectionId: string;
|
|
5315
|
+
slackTeamId: string;
|
|
5316
|
+
slackUserId: string;
|
|
5317
|
+
subjectId: string;
|
|
5318
|
+
linkedBySubjectId: string;
|
|
5319
|
+
createdAt: Date;
|
|
5320
|
+
updatedAt: Date;
|
|
5321
|
+
};
|
|
5322
|
+
|
|
5323
|
+
export type SlackInteractionTriggerKind =
|
|
5324
|
+
| "app_mention"
|
|
5325
|
+
| "dm"
|
|
5326
|
+
| "slash_command"
|
|
5327
|
+
| "message_shortcut"
|
|
5328
|
+
| "thread_reply";
|
|
5329
|
+
|
|
5330
|
+
export type SlackInteractionInboxEntry = {
|
|
5331
|
+
id: string;
|
|
5332
|
+
accountId: string;
|
|
5333
|
+
workspaceId: string;
|
|
5334
|
+
connectionId: string;
|
|
5335
|
+
providerEventId: string;
|
|
5336
|
+
providerMessageId: string;
|
|
5337
|
+
slackTeamId: string;
|
|
5338
|
+
slackUserId: string;
|
|
5339
|
+
slackChannelId: string;
|
|
5340
|
+
slackMessageTs: string;
|
|
5341
|
+
slackThreadTs: string | null;
|
|
5342
|
+
triggerKind: SlackInteractionTriggerKind;
|
|
5343
|
+
text: string;
|
|
5344
|
+
status: "pending" | "processing" | "processed" | "failed";
|
|
5345
|
+
claimHolderId: string | null;
|
|
5346
|
+
claimExpiresAt: Date | null;
|
|
5347
|
+
attemptCount: number;
|
|
5348
|
+
lastErrorCode: string | null;
|
|
5349
|
+
processedAt: Date | null;
|
|
5350
|
+
createdAt: Date;
|
|
5351
|
+
updatedAt: Date;
|
|
5352
|
+
};
|
|
5353
|
+
|
|
5354
|
+
export type SlackInteraction = {
|
|
5355
|
+
id: string;
|
|
5356
|
+
accountId: string;
|
|
5357
|
+
workspaceId: string;
|
|
5358
|
+
connectionId: string;
|
|
5359
|
+
slackTeamId: string;
|
|
5360
|
+
slackChannelId: string;
|
|
5361
|
+
slackThreadTs: string;
|
|
5362
|
+
routeKey: string;
|
|
5363
|
+
triggeringProviderEventId: string;
|
|
5364
|
+
owningSubjectId: string;
|
|
5365
|
+
visibility: "private" | "workspace";
|
|
5366
|
+
sessionReservationId: string;
|
|
5367
|
+
sessionId: string | null;
|
|
5368
|
+
lastDeliveredSessionEventSequence: number;
|
|
5369
|
+
deliveryClaimHolderId: string | null;
|
|
5370
|
+
deliveryClaimExpiresAt: Date | null;
|
|
5371
|
+
ackSlackMessageTs: string | null;
|
|
5372
|
+
progressCount: number;
|
|
5373
|
+
terminalDeliveryState: "open" | "completed" | "failed" | "cancelled" | "blocked";
|
|
5374
|
+
createdAt: Date;
|
|
5375
|
+
updatedAt: Date;
|
|
5376
|
+
};
|
|
5377
|
+
|
|
5378
|
+
export type SlackInteractionProgressDelivery = {
|
|
5379
|
+
id: string;
|
|
5380
|
+
accountId: string;
|
|
5381
|
+
workspaceId: string;
|
|
5382
|
+
interactionId: string;
|
|
5383
|
+
sessionEventSequence: number;
|
|
5384
|
+
slot: number;
|
|
5385
|
+
operationId: string;
|
|
5386
|
+
createdAt: Date;
|
|
5387
|
+
};
|
|
5388
|
+
|
|
5389
|
+
export async function resolveSlackInstallationRoute(
|
|
5390
|
+
db: Database,
|
|
5391
|
+
slackTeamId: string,
|
|
5392
|
+
): Promise<SlackInstallationRoute | null> {
|
|
5393
|
+
const rows = await db.execute<{
|
|
5394
|
+
account_id: string;
|
|
5395
|
+
workspace_id: string;
|
|
5396
|
+
connection_id: string;
|
|
5397
|
+
bot_id: string;
|
|
5398
|
+
bot_user_id: string;
|
|
5399
|
+
}>(sql`select * from opengeni_private.resolve_slack_installation(${slackTeamId})`);
|
|
5400
|
+
const row = rows[0];
|
|
5401
|
+
return row
|
|
5402
|
+
? {
|
|
5403
|
+
accountId: row.account_id,
|
|
5404
|
+
workspaceId: row.workspace_id,
|
|
5405
|
+
connectionId: row.connection_id,
|
|
5406
|
+
botId: row.bot_id,
|
|
5407
|
+
botUserId: row.bot_user_id,
|
|
5408
|
+
}
|
|
5409
|
+
: null;
|
|
5410
|
+
}
|
|
5411
|
+
|
|
5412
|
+
export async function saveSlackBotUserLink(
|
|
5413
|
+
db: Database,
|
|
5414
|
+
input: Omit<SlackBotUserLink, "id" | "createdAt" | "updatedAt">,
|
|
5415
|
+
): Promise<SlackBotUserLink> {
|
|
5416
|
+
return await withWorkspaceRls(db, input.workspaceId, async (scopedDb) => {
|
|
5417
|
+
const [row] = await scopedDb
|
|
5418
|
+
.insert(schema.slackBotUserLinks)
|
|
5419
|
+
.values(input)
|
|
5420
|
+
.onConflictDoUpdate({
|
|
5421
|
+
target: [schema.slackBotUserLinks.connectionId, schema.slackBotUserLinks.slackUserId],
|
|
5422
|
+
set: {
|
|
5423
|
+
accountId: input.accountId,
|
|
5424
|
+
workspaceId: input.workspaceId,
|
|
5425
|
+
slackTeamId: input.slackTeamId,
|
|
5426
|
+
subjectId: input.subjectId,
|
|
5427
|
+
linkedBySubjectId: input.linkedBySubjectId,
|
|
5428
|
+
updatedAt: sql`now()`,
|
|
5429
|
+
},
|
|
5430
|
+
})
|
|
5431
|
+
.returning();
|
|
5432
|
+
if (!row) throw new Error("Slack identity link write returned no row");
|
|
5433
|
+
return mapSlackBotUserLink(row);
|
|
5434
|
+
});
|
|
5435
|
+
}
|
|
5436
|
+
|
|
5437
|
+
export async function getSlackBotUserLink(
|
|
5438
|
+
db: Database,
|
|
5439
|
+
workspaceId: string,
|
|
5440
|
+
connectionId: string,
|
|
5441
|
+
slackUserId: string,
|
|
5442
|
+
): Promise<SlackBotUserLink | null> {
|
|
5443
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
5444
|
+
const [row] = await scopedDb
|
|
5445
|
+
.select()
|
|
5446
|
+
.from(schema.slackBotUserLinks)
|
|
5447
|
+
.where(
|
|
5448
|
+
and(
|
|
5449
|
+
eq(schema.slackBotUserLinks.workspaceId, workspaceId),
|
|
5450
|
+
eq(schema.slackBotUserLinks.connectionId, connectionId),
|
|
5451
|
+
eq(schema.slackBotUserLinks.slackUserId, slackUserId),
|
|
5452
|
+
),
|
|
5453
|
+
)
|
|
5454
|
+
.limit(1);
|
|
5455
|
+
return row ? mapSlackBotUserLink(row) : null;
|
|
5456
|
+
});
|
|
5457
|
+
}
|
|
5458
|
+
|
|
5459
|
+
export async function deleteSlackBotUserLink(
|
|
5460
|
+
db: Database,
|
|
5461
|
+
workspaceId: string,
|
|
5462
|
+
connectionId: string,
|
|
5463
|
+
slackUserId: string,
|
|
5464
|
+
): Promise<boolean> {
|
|
5465
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
5466
|
+
const rows = await scopedDb
|
|
5467
|
+
.delete(schema.slackBotUserLinks)
|
|
5468
|
+
.where(
|
|
5469
|
+
and(
|
|
5470
|
+
eq(schema.slackBotUserLinks.workspaceId, workspaceId),
|
|
5471
|
+
eq(schema.slackBotUserLinks.connectionId, connectionId),
|
|
5472
|
+
eq(schema.slackBotUserLinks.slackUserId, slackUserId),
|
|
5473
|
+
),
|
|
5474
|
+
)
|
|
5475
|
+
.returning({ id: schema.slackBotUserLinks.id });
|
|
5476
|
+
return rows.length > 0;
|
|
5477
|
+
});
|
|
5478
|
+
}
|
|
5479
|
+
|
|
5480
|
+
export async function enqueueSlackInteractionInbox(
|
|
5481
|
+
db: Database,
|
|
5482
|
+
input: Omit<
|
|
5483
|
+
SlackInteractionInboxEntry,
|
|
5484
|
+
| "id"
|
|
5485
|
+
| "status"
|
|
5486
|
+
| "claimHolderId"
|
|
5487
|
+
| "claimExpiresAt"
|
|
5488
|
+
| "attemptCount"
|
|
5489
|
+
| "lastErrorCode"
|
|
5490
|
+
| "processedAt"
|
|
5491
|
+
| "createdAt"
|
|
5492
|
+
| "updatedAt"
|
|
5493
|
+
>,
|
|
5494
|
+
): Promise<{ inserted: boolean; entry: SlackInteractionInboxEntry }> {
|
|
5495
|
+
return await withRlsContext(
|
|
5496
|
+
db,
|
|
5497
|
+
{ accountId: input.accountId, workspaceId: input.workspaceId },
|
|
5498
|
+
async (scopedDb) => {
|
|
5499
|
+
const [created] = await scopedDb
|
|
5500
|
+
.insert(schema.slackInteractionInbox)
|
|
5501
|
+
.values(input)
|
|
5502
|
+
.onConflictDoNothing()
|
|
5503
|
+
.returning();
|
|
5504
|
+
if (created) return { inserted: true, entry: mapSlackInteractionInbox(created) };
|
|
5505
|
+
const [existing] = await scopedDb
|
|
5506
|
+
.select()
|
|
5507
|
+
.from(schema.slackInteractionInbox)
|
|
5508
|
+
.where(
|
|
5509
|
+
and(
|
|
5510
|
+
eq(schema.slackInteractionInbox.connectionId, input.connectionId),
|
|
5511
|
+
or(
|
|
5512
|
+
eq(schema.slackInteractionInbox.providerEventId, input.providerEventId),
|
|
5513
|
+
eq(schema.slackInteractionInbox.providerMessageId, input.providerMessageId),
|
|
5514
|
+
),
|
|
5515
|
+
),
|
|
5516
|
+
)
|
|
5517
|
+
.limit(1);
|
|
5518
|
+
if (!existing) throw new Error("Slack inbox idempotency conflict could not be resolved");
|
|
5519
|
+
return { inserted: false, entry: mapSlackInteractionInbox(existing) };
|
|
5520
|
+
},
|
|
5521
|
+
);
|
|
5522
|
+
}
|
|
5523
|
+
|
|
5524
|
+
export async function claimSlackInteractionInbox(
|
|
5525
|
+
db: Database,
|
|
5526
|
+
claimHolderId: string,
|
|
5527
|
+
claimLeaseMs: number,
|
|
5528
|
+
): Promise<SlackInteractionInboxEntry | null> {
|
|
5529
|
+
const rows = await db.execute<typeof schema.slackInteractionInbox.$inferSelect>(
|
|
5530
|
+
sql`select * from opengeni_private.claim_slack_interaction_inbox(${claimHolderId}::uuid, ${claimLeaseMs})`,
|
|
5531
|
+
);
|
|
5532
|
+
return rows[0] ? mapSlackInteractionInbox(rows[0]) : null;
|
|
5533
|
+
}
|
|
5534
|
+
|
|
5535
|
+
export async function settleSlackInteractionInbox(
|
|
5536
|
+
db: Database,
|
|
5537
|
+
input: {
|
|
5538
|
+
entry: Pick<SlackInteractionInboxEntry, "id" | "accountId" | "workspaceId">;
|
|
5539
|
+
claimHolderId: string;
|
|
5540
|
+
outcome: "processed" | "failed";
|
|
5541
|
+
errorCode?: string | null;
|
|
5542
|
+
},
|
|
5543
|
+
): Promise<boolean> {
|
|
5544
|
+
return await withRlsContext(db, input.entry, async (scopedDb) => {
|
|
5545
|
+
const rows = await scopedDb
|
|
5546
|
+
.update(schema.slackInteractionInbox)
|
|
5547
|
+
.set({
|
|
5548
|
+
status: input.outcome,
|
|
5549
|
+
claimHolderId: null,
|
|
5550
|
+
claimExpiresAt: null,
|
|
5551
|
+
processedAt: sql`now()`,
|
|
5552
|
+
lastErrorCode: input.errorCode ?? null,
|
|
5553
|
+
updatedAt: sql`now()`,
|
|
5554
|
+
})
|
|
5555
|
+
.where(
|
|
5556
|
+
and(
|
|
5557
|
+
eq(schema.slackInteractionInbox.id, input.entry.id),
|
|
5558
|
+
eq(schema.slackInteractionInbox.claimHolderId, input.claimHolderId),
|
|
5559
|
+
eq(schema.slackInteractionInbox.status, "processing"),
|
|
5560
|
+
),
|
|
5561
|
+
)
|
|
5562
|
+
.returning({ id: schema.slackInteractionInbox.id });
|
|
5563
|
+
return rows.length === 1;
|
|
5564
|
+
});
|
|
5565
|
+
}
|
|
5566
|
+
|
|
5567
|
+
export async function releaseSlackInteractionInbox(
|
|
5568
|
+
db: Database,
|
|
5569
|
+
input: {
|
|
5570
|
+
entry: Pick<SlackInteractionInboxEntry, "id" | "accountId" | "workspaceId">;
|
|
5571
|
+
claimHolderId: string;
|
|
5572
|
+
errorCode: string;
|
|
5573
|
+
},
|
|
5574
|
+
): Promise<boolean> {
|
|
5575
|
+
return await withRlsContext(db, input.entry, async (scopedDb) => {
|
|
5576
|
+
const rows = await scopedDb
|
|
5577
|
+
.update(schema.slackInteractionInbox)
|
|
5578
|
+
.set({
|
|
5579
|
+
status: "pending",
|
|
5580
|
+
claimHolderId: null,
|
|
5581
|
+
claimExpiresAt: null,
|
|
5582
|
+
lastErrorCode: input.errorCode,
|
|
5583
|
+
updatedAt: sql`now()`,
|
|
5584
|
+
})
|
|
5585
|
+
.where(
|
|
5586
|
+
and(
|
|
5587
|
+
eq(schema.slackInteractionInbox.id, input.entry.id),
|
|
5588
|
+
eq(schema.slackInteractionInbox.claimHolderId, input.claimHolderId),
|
|
5589
|
+
eq(schema.slackInteractionInbox.status, "processing"),
|
|
5590
|
+
),
|
|
5591
|
+
)
|
|
5592
|
+
.returning({ id: schema.slackInteractionInbox.id });
|
|
5593
|
+
return rows.length === 1;
|
|
5594
|
+
});
|
|
5595
|
+
}
|
|
5596
|
+
|
|
5597
|
+
export async function getOrCreateSlackInteraction(
|
|
5598
|
+
db: Database,
|
|
5599
|
+
input: Omit<
|
|
5600
|
+
SlackInteraction,
|
|
5601
|
+
| "id"
|
|
5602
|
+
| "sessionReservationId"
|
|
5603
|
+
| "sessionId"
|
|
5604
|
+
| "lastDeliveredSessionEventSequence"
|
|
5605
|
+
| "deliveryClaimHolderId"
|
|
5606
|
+
| "deliveryClaimExpiresAt"
|
|
5607
|
+
| "ackSlackMessageTs"
|
|
5608
|
+
| "progressCount"
|
|
5609
|
+
| "terminalDeliveryState"
|
|
5610
|
+
| "createdAt"
|
|
5611
|
+
| "updatedAt"
|
|
5612
|
+
>,
|
|
5613
|
+
): Promise<{ created: boolean; interaction: SlackInteraction }> {
|
|
5614
|
+
return await withRlsContext(db, input, async (scopedDb) => {
|
|
5615
|
+
const [created] = await scopedDb
|
|
5616
|
+
.insert(schema.slackInteractions)
|
|
5617
|
+
.values(input)
|
|
5618
|
+
.onConflictDoNothing()
|
|
5619
|
+
.returning();
|
|
5620
|
+
if (created) return { created: true, interaction: mapSlackInteraction(created) };
|
|
5621
|
+
const [existing] = await scopedDb
|
|
5622
|
+
.select()
|
|
5623
|
+
.from(schema.slackInteractions)
|
|
5624
|
+
.where(
|
|
5625
|
+
and(
|
|
5626
|
+
eq(schema.slackInteractions.connectionId, input.connectionId),
|
|
5627
|
+
eq(schema.slackInteractions.routeKey, input.routeKey),
|
|
5628
|
+
),
|
|
5629
|
+
)
|
|
5630
|
+
.limit(1);
|
|
5631
|
+
if (!existing) throw new Error("Slack route idempotency conflict could not be resolved");
|
|
5632
|
+
return { created: false, interaction: mapSlackInteraction(existing) };
|
|
5633
|
+
});
|
|
5634
|
+
}
|
|
5635
|
+
|
|
5636
|
+
export async function getSlackInteractionByRoute(
|
|
5637
|
+
db: Database,
|
|
5638
|
+
workspaceId: string,
|
|
5639
|
+
connectionId: string,
|
|
5640
|
+
routeKey: string,
|
|
5641
|
+
): Promise<SlackInteraction | null> {
|
|
5642
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
5643
|
+
const [row] = await scopedDb
|
|
5644
|
+
.select()
|
|
5645
|
+
.from(schema.slackInteractions)
|
|
5646
|
+
.where(
|
|
5647
|
+
and(
|
|
5648
|
+
eq(schema.slackInteractions.workspaceId, workspaceId),
|
|
5649
|
+
eq(schema.slackInteractions.connectionId, connectionId),
|
|
5650
|
+
eq(schema.slackInteractions.routeKey, routeKey),
|
|
5651
|
+
),
|
|
5652
|
+
)
|
|
5653
|
+
.limit(1);
|
|
5654
|
+
return row ? mapSlackInteraction(row) : null;
|
|
5655
|
+
});
|
|
5656
|
+
}
|
|
5657
|
+
|
|
5658
|
+
export async function getSlackInteractionSessionAccess(
|
|
5659
|
+
db: Database,
|
|
5660
|
+
workspaceId: string,
|
|
5661
|
+
rootSessionId: string,
|
|
5662
|
+
): Promise<Pick<SlackInteraction, "owningSubjectId" | "visibility"> | null> {
|
|
5663
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
5664
|
+
const [row] = await scopedDb
|
|
5665
|
+
.select({
|
|
5666
|
+
owningSubjectId: schema.slackInteractions.owningSubjectId,
|
|
5667
|
+
visibility: schema.slackInteractions.visibility,
|
|
5668
|
+
})
|
|
5669
|
+
.from(schema.slackInteractions)
|
|
5670
|
+
.where(
|
|
5671
|
+
and(
|
|
5672
|
+
eq(schema.slackInteractions.workspaceId, workspaceId),
|
|
5673
|
+
eq(schema.slackInteractions.sessionReservationId, rootSessionId),
|
|
5674
|
+
),
|
|
5675
|
+
)
|
|
5676
|
+
.limit(1);
|
|
5677
|
+
return row ?? null;
|
|
5678
|
+
});
|
|
5679
|
+
}
|
|
5680
|
+
|
|
5681
|
+
export async function getSlackInteractionSessionAccessForSession(
|
|
5682
|
+
db: Database,
|
|
5683
|
+
input: {
|
|
5684
|
+
accountId: string;
|
|
5685
|
+
workspaceId: string;
|
|
5686
|
+
sessionId: string;
|
|
5687
|
+
},
|
|
5688
|
+
): Promise<
|
|
5689
|
+
(Pick<SlackInteraction, "owningSubjectId" | "visibility"> & { rootSessionId: string }) | null
|
|
5690
|
+
> {
|
|
5691
|
+
return await withRlsContext(db, input, async (scopedDb) => {
|
|
5692
|
+
const rows = await scopedDb.execute<{
|
|
5693
|
+
rootSessionId: string;
|
|
5694
|
+
parentSessionId: string | null;
|
|
5695
|
+
depth: number;
|
|
5696
|
+
cycle: boolean;
|
|
5697
|
+
owningSubjectId: string | null;
|
|
5698
|
+
visibility: SlackInteraction["visibility"] | null;
|
|
5699
|
+
}>(sql`
|
|
5700
|
+
with recursive lineage(id, parent_session_id, depth, path, cycle) as (
|
|
5701
|
+
select
|
|
5702
|
+
${schema.sessions.id},
|
|
5703
|
+
${schema.sessions.parentSessionId},
|
|
5704
|
+
0,
|
|
5705
|
+
array[${schema.sessions.id}],
|
|
5706
|
+
false
|
|
5707
|
+
from ${schema.sessions}
|
|
5708
|
+
where ${schema.sessions.accountId} = ${input.accountId}
|
|
5709
|
+
and ${schema.sessions.workspaceId} = ${input.workspaceId}
|
|
5710
|
+
and ${schema.sessions.id} = ${input.sessionId}
|
|
5711
|
+
union all
|
|
5712
|
+
select
|
|
5713
|
+
parent.id,
|
|
5714
|
+
parent.parent_session_id,
|
|
5715
|
+
lineage.depth + 1,
|
|
5716
|
+
lineage.path || parent.id,
|
|
5717
|
+
parent.id = any(lineage.path)
|
|
5718
|
+
from ${schema.sessions} parent
|
|
5719
|
+
join lineage on lineage.parent_session_id = parent.id
|
|
5720
|
+
where parent.account_id = ${input.accountId}
|
|
5721
|
+
and parent.workspace_id = ${input.workspaceId}
|
|
5722
|
+
and not lineage.cycle
|
|
5723
|
+
and lineage.depth < 64
|
|
5724
|
+
), root as (
|
|
5725
|
+
select id, parent_session_id, depth, cycle
|
|
5726
|
+
from lineage
|
|
5727
|
+
order by depth desc
|
|
5728
|
+
limit 1
|
|
5729
|
+
)
|
|
5730
|
+
select
|
|
5731
|
+
root.id as "rootSessionId",
|
|
5732
|
+
root.parent_session_id as "parentSessionId",
|
|
5733
|
+
root.depth,
|
|
5734
|
+
root.cycle,
|
|
5735
|
+
interaction.owning_subject_id as "owningSubjectId",
|
|
5736
|
+
interaction.visibility
|
|
5737
|
+
from root
|
|
5738
|
+
left join ${schema.slackInteractions} interaction
|
|
5739
|
+
on interaction.account_id = ${input.accountId}
|
|
5740
|
+
and interaction.workspace_id = ${input.workspaceId}
|
|
5741
|
+
and interaction.session_reservation_id = root.id
|
|
5742
|
+
`);
|
|
5743
|
+
const root = rows[0];
|
|
5744
|
+
if (!root) return null;
|
|
5745
|
+
if (root.cycle || root.parentSessionId !== null || Number(root.depth) >= 64) {
|
|
5746
|
+
throw new Error(`session lineage for ${input.sessionId} has no valid workspace root`);
|
|
5747
|
+
}
|
|
5748
|
+
if (!root.owningSubjectId || !root.visibility) return null;
|
|
5749
|
+
return {
|
|
5750
|
+
rootSessionId: root.rootSessionId,
|
|
5751
|
+
owningSubjectId: root.owningSubjectId,
|
|
5752
|
+
visibility: root.visibility,
|
|
5753
|
+
};
|
|
5754
|
+
});
|
|
5755
|
+
}
|
|
5756
|
+
|
|
5757
|
+
export async function bindSlackInteractionSession(
|
|
5758
|
+
db: Database,
|
|
5759
|
+
input: Pick<SlackInteraction, "id" | "accountId" | "workspaceId" | "owningSubjectId"> & {
|
|
5760
|
+
sessionId: string;
|
|
5761
|
+
},
|
|
5762
|
+
): Promise<SlackInteraction | null> {
|
|
5763
|
+
return await withRlsContext(db, input, async (scopedDb) => {
|
|
5764
|
+
const [row] = await scopedDb
|
|
5765
|
+
.update(schema.slackInteractions)
|
|
5766
|
+
.set({ sessionId: input.sessionId, updatedAt: sql`now()` })
|
|
5767
|
+
.where(
|
|
5768
|
+
and(
|
|
5769
|
+
eq(schema.slackInteractions.id, input.id),
|
|
5770
|
+
eq(schema.slackInteractions.owningSubjectId, input.owningSubjectId),
|
|
5771
|
+
eq(schema.slackInteractions.sessionReservationId, input.sessionId),
|
|
5772
|
+
isNull(schema.slackInteractions.sessionId),
|
|
5773
|
+
),
|
|
5774
|
+
)
|
|
5775
|
+
.returning();
|
|
5776
|
+
if (row) return mapSlackInteraction(row);
|
|
5777
|
+
const [existing] = await scopedDb
|
|
5778
|
+
.select()
|
|
5779
|
+
.from(schema.slackInteractions)
|
|
5780
|
+
.where(eq(schema.slackInteractions.id, input.id))
|
|
5781
|
+
.limit(1);
|
|
5782
|
+
return existing?.sessionId === input.sessionId ? mapSlackInteraction(existing) : null;
|
|
5783
|
+
});
|
|
5784
|
+
}
|
|
5785
|
+
|
|
5786
|
+
export type SlackInteractionProgressClaim =
|
|
5787
|
+
| {
|
|
5788
|
+
kind: "claimed";
|
|
5789
|
+
created: boolean;
|
|
5790
|
+
progressCount: number;
|
|
5791
|
+
delivery: SlackInteractionProgressDelivery;
|
|
5792
|
+
}
|
|
5793
|
+
| { kind: "limit_reached"; progressCount: number }
|
|
5794
|
+
| { kind: "not_owned" };
|
|
5795
|
+
|
|
5796
|
+
/**
|
|
5797
|
+
* Reserve one globally bounded progress slot before any Slack provider call.
|
|
5798
|
+
* The interaction row lock serializes replicas and expired delivery claim
|
|
5799
|
+
* successors; the event row preserves one operation UUID across every retry.
|
|
5800
|
+
*/
|
|
5801
|
+
export async function claimSlackInteractionProgressDelivery(
|
|
5802
|
+
db: Database,
|
|
5803
|
+
input: {
|
|
5804
|
+
accountId: string;
|
|
5805
|
+
workspaceId: string;
|
|
5806
|
+
interactionId: string;
|
|
5807
|
+
claimHolderId: string;
|
|
5808
|
+
sessionEventSequence: number;
|
|
5809
|
+
maxProgress: number;
|
|
5810
|
+
},
|
|
5811
|
+
): Promise<SlackInteractionProgressClaim> {
|
|
5812
|
+
if (
|
|
5813
|
+
!Number.isSafeInteger(input.sessionEventSequence) ||
|
|
5814
|
+
input.sessionEventSequence < 1 ||
|
|
5815
|
+
!Number.isSafeInteger(input.maxProgress) ||
|
|
5816
|
+
input.maxProgress < 1 ||
|
|
5817
|
+
input.maxProgress > 3
|
|
5818
|
+
) {
|
|
5819
|
+
throw new RangeError("invalid Slack progress delivery claim bounds");
|
|
5820
|
+
}
|
|
5821
|
+
return await withRlsContext(db, input, async (scopedDb) => {
|
|
5822
|
+
const [interaction] = await scopedDb
|
|
5823
|
+
.select({
|
|
5824
|
+
progressCount: schema.slackInteractions.progressCount,
|
|
5825
|
+
deliveryClaimHolderId: schema.slackInteractions.deliveryClaimHolderId,
|
|
5826
|
+
terminalDeliveryState: schema.slackInteractions.terminalDeliveryState,
|
|
5827
|
+
})
|
|
5828
|
+
.from(schema.slackInteractions)
|
|
5829
|
+
.where(
|
|
5830
|
+
and(
|
|
5831
|
+
eq(schema.slackInteractions.id, input.interactionId),
|
|
5832
|
+
eq(schema.slackInteractions.accountId, input.accountId),
|
|
5833
|
+
eq(schema.slackInteractions.workspaceId, input.workspaceId),
|
|
5834
|
+
),
|
|
5835
|
+
)
|
|
5836
|
+
.for("update")
|
|
5837
|
+
.limit(1);
|
|
5838
|
+
if (
|
|
5839
|
+
!interaction ||
|
|
5840
|
+
interaction.deliveryClaimHolderId !== input.claimHolderId ||
|
|
5841
|
+
interaction.terminalDeliveryState !== "open"
|
|
5842
|
+
) {
|
|
5843
|
+
return { kind: "not_owned" };
|
|
5844
|
+
}
|
|
5845
|
+
|
|
5846
|
+
const [existing] = await scopedDb
|
|
5847
|
+
.select()
|
|
5848
|
+
.from(schema.slackInteractionProgressDeliveries)
|
|
5849
|
+
.where(
|
|
5850
|
+
and(
|
|
5851
|
+
eq(schema.slackInteractionProgressDeliveries.interactionId, input.interactionId),
|
|
5852
|
+
eq(
|
|
5853
|
+
schema.slackInteractionProgressDeliveries.sessionEventSequence,
|
|
5854
|
+
input.sessionEventSequence,
|
|
5855
|
+
),
|
|
5856
|
+
),
|
|
5857
|
+
)
|
|
5858
|
+
.limit(1);
|
|
5859
|
+
if (existing) {
|
|
5860
|
+
return {
|
|
5861
|
+
kind: "claimed",
|
|
5862
|
+
created: false,
|
|
5863
|
+
progressCount: interaction.progressCount,
|
|
5864
|
+
delivery: mapSlackInteractionProgressDelivery(existing),
|
|
5865
|
+
};
|
|
5866
|
+
}
|
|
5867
|
+
if (interaction.progressCount >= input.maxProgress) {
|
|
5868
|
+
return { kind: "limit_reached", progressCount: interaction.progressCount };
|
|
5869
|
+
}
|
|
5870
|
+
|
|
5871
|
+
const slot = interaction.progressCount + 1;
|
|
5872
|
+
const [delivery] = await scopedDb
|
|
5873
|
+
.insert(schema.slackInteractionProgressDeliveries)
|
|
5874
|
+
.values({
|
|
5875
|
+
accountId: input.accountId,
|
|
5876
|
+
workspaceId: input.workspaceId,
|
|
5877
|
+
interactionId: input.interactionId,
|
|
5878
|
+
sessionEventSequence: input.sessionEventSequence,
|
|
5879
|
+
slot,
|
|
5880
|
+
operationId: crypto.randomUUID(),
|
|
5881
|
+
})
|
|
5882
|
+
.returning();
|
|
5883
|
+
if (!delivery) throw new Error("Slack progress delivery claim returned no row");
|
|
5884
|
+
const [updated] = await scopedDb
|
|
5885
|
+
.update(schema.slackInteractions)
|
|
5886
|
+
.set({ progressCount: slot, updatedAt: sql`now()` })
|
|
5887
|
+
.where(
|
|
5888
|
+
and(
|
|
5889
|
+
eq(schema.slackInteractions.id, input.interactionId),
|
|
5890
|
+
eq(schema.slackInteractions.deliveryClaimHolderId, input.claimHolderId),
|
|
5891
|
+
eq(schema.slackInteractions.progressCount, interaction.progressCount),
|
|
5892
|
+
),
|
|
5893
|
+
)
|
|
5894
|
+
.returning({ progressCount: schema.slackInteractions.progressCount });
|
|
5895
|
+
if (!updated) throw new Error("Slack progress delivery lost its durable interaction claim");
|
|
5896
|
+
return {
|
|
5897
|
+
kind: "claimed",
|
|
5898
|
+
created: true,
|
|
5899
|
+
progressCount: updated.progressCount,
|
|
5900
|
+
delivery: mapSlackInteractionProgressDelivery(delivery),
|
|
5901
|
+
};
|
|
5902
|
+
});
|
|
5903
|
+
}
|
|
5904
|
+
|
|
5905
|
+
export async function rekeySlackInteractionRoute(
|
|
5906
|
+
db: Database,
|
|
5907
|
+
input: Pick<SlackInteraction, "id" | "accountId" | "workspaceId"> & {
|
|
5908
|
+
routeKey: string;
|
|
5909
|
+
slackThreadTs: string;
|
|
5910
|
+
ackSlackMessageTs: string;
|
|
5911
|
+
},
|
|
5912
|
+
): Promise<SlackInteraction | null> {
|
|
5913
|
+
return await withRlsContext(db, input, async (scopedDb) => {
|
|
5914
|
+
const [row] = await scopedDb
|
|
5915
|
+
.update(schema.slackInteractions)
|
|
5916
|
+
.set({
|
|
5917
|
+
routeKey: input.routeKey,
|
|
5918
|
+
slackThreadTs: input.slackThreadTs,
|
|
5919
|
+
ackSlackMessageTs: input.ackSlackMessageTs,
|
|
5920
|
+
updatedAt: sql`now()`,
|
|
5921
|
+
})
|
|
5922
|
+
.where(eq(schema.slackInteractions.id, input.id))
|
|
5923
|
+
.returning();
|
|
5924
|
+
return row ? mapSlackInteraction(row) : null;
|
|
5925
|
+
});
|
|
5926
|
+
}
|
|
5927
|
+
|
|
5928
|
+
export async function claimSlackInteractionDelivery(
|
|
5929
|
+
db: Database,
|
|
5930
|
+
claimHolderId: string,
|
|
5931
|
+
claimLeaseMs: number,
|
|
5932
|
+
): Promise<SlackInteraction | null> {
|
|
5933
|
+
const rows = await db.execute<typeof schema.slackInteractions.$inferSelect>(
|
|
5934
|
+
sql`select * from opengeni_private.claim_slack_interaction_delivery(${claimHolderId}::uuid, ${claimLeaseMs})`,
|
|
5935
|
+
);
|
|
5936
|
+
return rows[0] ? mapSlackInteraction(rows[0]) : null;
|
|
5937
|
+
}
|
|
5938
|
+
|
|
5939
|
+
export async function reopenSlackInteractionDelivery(
|
|
5940
|
+
db: Database,
|
|
5941
|
+
input: Pick<SlackInteraction, "id" | "accountId" | "workspaceId">,
|
|
5942
|
+
): Promise<boolean> {
|
|
5943
|
+
return await withRlsContext(db, input, async (scopedDb) => {
|
|
5944
|
+
const rows = await scopedDb
|
|
5945
|
+
.update(schema.slackInteractions)
|
|
5946
|
+
.set({ terminalDeliveryState: "open", updatedAt: sql`now()` })
|
|
5947
|
+
.where(eq(schema.slackInteractions.id, input.id))
|
|
5948
|
+
.returning({ id: schema.slackInteractions.id });
|
|
5949
|
+
return rows.length === 1;
|
|
5950
|
+
});
|
|
5951
|
+
}
|
|
5952
|
+
|
|
5953
|
+
export async function advanceSlackInteractionDelivery(
|
|
5954
|
+
db: Database,
|
|
5955
|
+
input: Pick<SlackInteraction, "id" | "accountId" | "workspaceId"> & {
|
|
5956
|
+
claimHolderId: string;
|
|
5957
|
+
sequence: number;
|
|
5958
|
+
ackSlackMessageTs?: string | null;
|
|
5959
|
+
},
|
|
5960
|
+
): Promise<boolean> {
|
|
5961
|
+
return await withRlsContext(db, input, async (scopedDb) => {
|
|
5962
|
+
const rows = await scopedDb
|
|
5963
|
+
.update(schema.slackInteractions)
|
|
5964
|
+
.set({
|
|
5965
|
+
lastDeliveredSessionEventSequence: input.sequence,
|
|
5966
|
+
...(input.ackSlackMessageTs !== undefined
|
|
5967
|
+
? { ackSlackMessageTs: input.ackSlackMessageTs }
|
|
5968
|
+
: {}),
|
|
5969
|
+
updatedAt: sql`now()`,
|
|
5970
|
+
})
|
|
5971
|
+
.where(
|
|
5972
|
+
and(
|
|
5973
|
+
eq(schema.slackInteractions.id, input.id),
|
|
5974
|
+
eq(schema.slackInteractions.deliveryClaimHolderId, input.claimHolderId),
|
|
5975
|
+
lte(schema.slackInteractions.lastDeliveredSessionEventSequence, input.sequence),
|
|
5976
|
+
),
|
|
5977
|
+
)
|
|
5978
|
+
.returning({ id: schema.slackInteractions.id });
|
|
5979
|
+
return rows.length === 1;
|
|
5980
|
+
});
|
|
5981
|
+
}
|
|
5982
|
+
|
|
5983
|
+
export async function releaseSlackInteractionDelivery(
|
|
5984
|
+
db: Database,
|
|
5985
|
+
input: Pick<SlackInteraction, "id" | "accountId" | "workspaceId"> & {
|
|
5986
|
+
claimHolderId: string;
|
|
5987
|
+
},
|
|
5988
|
+
): Promise<boolean> {
|
|
5989
|
+
return await withRlsContext(db, input, async (scopedDb) => {
|
|
5990
|
+
const rows = await scopedDb
|
|
5991
|
+
.update(schema.slackInteractions)
|
|
5992
|
+
.set({
|
|
5993
|
+
deliveryClaimHolderId: null,
|
|
5994
|
+
deliveryClaimExpiresAt: null,
|
|
5995
|
+
updatedAt: sql`now()`,
|
|
5996
|
+
})
|
|
5997
|
+
.where(
|
|
5998
|
+
and(
|
|
5999
|
+
eq(schema.slackInteractions.id, input.id),
|
|
6000
|
+
eq(schema.slackInteractions.deliveryClaimHolderId, input.claimHolderId),
|
|
6001
|
+
),
|
|
6002
|
+
)
|
|
6003
|
+
.returning({ id: schema.slackInteractions.id });
|
|
6004
|
+
return rows.length === 1;
|
|
6005
|
+
});
|
|
6006
|
+
}
|
|
6007
|
+
|
|
6008
|
+
export async function closeSlackInteractionDelivery(
|
|
6009
|
+
db: Database,
|
|
6010
|
+
input: Pick<SlackInteraction, "id" | "accountId" | "workspaceId"> & {
|
|
6011
|
+
claimHolderId: string;
|
|
6012
|
+
sequence: number;
|
|
6013
|
+
state: Exclude<SlackInteraction["terminalDeliveryState"], "open">;
|
|
6014
|
+
},
|
|
6015
|
+
): Promise<boolean> {
|
|
6016
|
+
return await withRlsContext(db, input, async (scopedDb) => {
|
|
6017
|
+
const rows = await scopedDb
|
|
6018
|
+
.update(schema.slackInteractions)
|
|
6019
|
+
.set({
|
|
6020
|
+
lastDeliveredSessionEventSequence: input.sequence,
|
|
6021
|
+
terminalDeliveryState: input.state,
|
|
6022
|
+
deliveryClaimHolderId: null,
|
|
6023
|
+
deliveryClaimExpiresAt: null,
|
|
6024
|
+
updatedAt: sql`now()`,
|
|
6025
|
+
})
|
|
6026
|
+
.where(
|
|
6027
|
+
and(
|
|
6028
|
+
eq(schema.slackInteractions.id, input.id),
|
|
6029
|
+
eq(schema.slackInteractions.deliveryClaimHolderId, input.claimHolderId),
|
|
6030
|
+
),
|
|
6031
|
+
)
|
|
6032
|
+
.returning({ id: schema.slackInteractions.id });
|
|
6033
|
+
return rows.length === 1;
|
|
6034
|
+
});
|
|
6035
|
+
}
|
|
6036
|
+
|
|
6037
|
+
function mapSlackBotUserLink(row: typeof schema.slackBotUserLinks.$inferSelect): SlackBotUserLink {
|
|
6038
|
+
return row;
|
|
6039
|
+
}
|
|
6040
|
+
|
|
6041
|
+
function mapSlackInteractionInbox(
|
|
6042
|
+
row: typeof schema.slackInteractionInbox.$inferSelect | Record<string, unknown>,
|
|
6043
|
+
): SlackInteractionInboxEntry {
|
|
6044
|
+
return {
|
|
6045
|
+
id: slackRowString(row, "id", "id"),
|
|
6046
|
+
accountId: slackRowString(row, "accountId", "account_id"),
|
|
6047
|
+
workspaceId: slackRowString(row, "workspaceId", "workspace_id"),
|
|
6048
|
+
connectionId: slackRowString(row, "connectionId", "connection_id"),
|
|
6049
|
+
providerEventId: slackRowString(row, "providerEventId", "provider_event_id"),
|
|
6050
|
+
providerMessageId: slackRowString(row, "providerMessageId", "provider_message_id"),
|
|
6051
|
+
slackTeamId: slackRowString(row, "slackTeamId", "slack_team_id"),
|
|
6052
|
+
slackUserId: slackRowString(row, "slackUserId", "slack_user_id"),
|
|
6053
|
+
slackChannelId: slackRowString(row, "slackChannelId", "slack_channel_id"),
|
|
6054
|
+
slackMessageTs: slackRowString(row, "slackMessageTs", "slack_message_ts"),
|
|
6055
|
+
slackThreadTs: slackRowNullableString(row, "slackThreadTs", "slack_thread_ts"),
|
|
6056
|
+
triggerKind: slackRowString(row, "triggerKind", "trigger_kind") as SlackInteractionTriggerKind,
|
|
6057
|
+
text: slackRowString(row, "text", "text"),
|
|
6058
|
+
status: slackRowString(row, "status", "status") as SlackInteractionInboxEntry["status"],
|
|
6059
|
+
claimHolderId: slackRowNullableString(row, "claimHolderId", "claim_holder_id"),
|
|
6060
|
+
claimExpiresAt: slackRowNullableDate(row, "claimExpiresAt", "claim_expires_at"),
|
|
6061
|
+
attemptCount: slackRowNumber(row, "attemptCount", "attempt_count"),
|
|
6062
|
+
lastErrorCode: slackRowNullableString(row, "lastErrorCode", "last_error_code"),
|
|
6063
|
+
processedAt: slackRowNullableDate(row, "processedAt", "processed_at"),
|
|
6064
|
+
createdAt: slackRowDate(row, "createdAt", "created_at"),
|
|
6065
|
+
updatedAt: slackRowDate(row, "updatedAt", "updated_at"),
|
|
6066
|
+
};
|
|
6067
|
+
}
|
|
6068
|
+
|
|
6069
|
+
function mapSlackInteraction(
|
|
6070
|
+
row: typeof schema.slackInteractions.$inferSelect | Record<string, unknown>,
|
|
6071
|
+
): SlackInteraction {
|
|
6072
|
+
return {
|
|
6073
|
+
id: slackRowString(row, "id", "id"),
|
|
6074
|
+
accountId: slackRowString(row, "accountId", "account_id"),
|
|
6075
|
+
workspaceId: slackRowString(row, "workspaceId", "workspace_id"),
|
|
6076
|
+
connectionId: slackRowString(row, "connectionId", "connection_id"),
|
|
6077
|
+
slackTeamId: slackRowString(row, "slackTeamId", "slack_team_id"),
|
|
6078
|
+
slackChannelId: slackRowString(row, "slackChannelId", "slack_channel_id"),
|
|
6079
|
+
slackThreadTs: slackRowString(row, "slackThreadTs", "slack_thread_ts"),
|
|
6080
|
+
routeKey: slackRowString(row, "routeKey", "route_key"),
|
|
6081
|
+
triggeringProviderEventId: slackRowString(
|
|
6082
|
+
row,
|
|
6083
|
+
"triggeringProviderEventId",
|
|
6084
|
+
"triggering_provider_event_id",
|
|
6085
|
+
),
|
|
6086
|
+
owningSubjectId: slackRowString(row, "owningSubjectId", "owning_subject_id"),
|
|
6087
|
+
visibility: slackRowString(row, "visibility", "visibility") as SlackInteraction["visibility"],
|
|
6088
|
+
sessionReservationId: slackRowString(row, "sessionReservationId", "session_reservation_id"),
|
|
6089
|
+
sessionId: slackRowNullableString(row, "sessionId", "session_id"),
|
|
6090
|
+
lastDeliveredSessionEventSequence: slackRowNumber(
|
|
6091
|
+
row,
|
|
6092
|
+
"lastDeliveredSessionEventSequence",
|
|
6093
|
+
"last_delivered_session_event_sequence",
|
|
6094
|
+
),
|
|
6095
|
+
deliveryClaimHolderId: slackRowNullableString(
|
|
6096
|
+
row,
|
|
6097
|
+
"deliveryClaimHolderId",
|
|
6098
|
+
"delivery_claim_holder_id",
|
|
6099
|
+
),
|
|
6100
|
+
deliveryClaimExpiresAt: slackRowNullableDate(
|
|
6101
|
+
row,
|
|
6102
|
+
"deliveryClaimExpiresAt",
|
|
6103
|
+
"delivery_claim_expires_at",
|
|
6104
|
+
),
|
|
6105
|
+
ackSlackMessageTs: slackRowNullableString(row, "ackSlackMessageTs", "ack_slack_message_ts"),
|
|
6106
|
+
progressCount: slackRowNumber(row, "progressCount", "progress_count"),
|
|
6107
|
+
terminalDeliveryState: slackRowString(
|
|
6108
|
+
row,
|
|
6109
|
+
"terminalDeliveryState",
|
|
6110
|
+
"terminal_delivery_state",
|
|
6111
|
+
) as SlackInteraction["terminalDeliveryState"],
|
|
6112
|
+
createdAt: slackRowDate(row, "createdAt", "created_at"),
|
|
6113
|
+
updatedAt: slackRowDate(row, "updatedAt", "updated_at"),
|
|
6114
|
+
};
|
|
6115
|
+
}
|
|
6116
|
+
|
|
6117
|
+
function mapSlackInteractionProgressDelivery(
|
|
6118
|
+
row: typeof schema.slackInteractionProgressDeliveries.$inferSelect | Record<string, unknown>,
|
|
6119
|
+
): SlackInteractionProgressDelivery {
|
|
6120
|
+
return {
|
|
6121
|
+
id: slackRowString(row, "id", "id"),
|
|
6122
|
+
accountId: slackRowString(row, "accountId", "account_id"),
|
|
6123
|
+
workspaceId: slackRowString(row, "workspaceId", "workspace_id"),
|
|
6124
|
+
interactionId: slackRowString(row, "interactionId", "interaction_id"),
|
|
6125
|
+
sessionEventSequence: slackRowNumber(row, "sessionEventSequence", "session_event_sequence"),
|
|
6126
|
+
slot: slackRowNumber(row, "slot", "slot"),
|
|
6127
|
+
operationId: slackRowString(row, "operationId", "operation_id"),
|
|
6128
|
+
createdAt: slackRowDate(row, "createdAt", "created_at"),
|
|
6129
|
+
};
|
|
6130
|
+
}
|
|
6131
|
+
|
|
6132
|
+
function slackRowValue(row: Record<string, unknown>, camelKey: string, snakeKey: string): unknown {
|
|
6133
|
+
return row[camelKey] ?? row[snakeKey];
|
|
6134
|
+
}
|
|
6135
|
+
|
|
6136
|
+
function slackRowString(row: Record<string, unknown>, camelKey: string, snakeKey: string): string {
|
|
6137
|
+
const value = slackRowValue(row, camelKey, snakeKey);
|
|
6138
|
+
if (typeof value !== "string") throw new Error(`Slack row omitted ${snakeKey}`);
|
|
6139
|
+
return value;
|
|
6140
|
+
}
|
|
6141
|
+
|
|
6142
|
+
function slackRowNullableString(
|
|
6143
|
+
row: Record<string, unknown>,
|
|
6144
|
+
camelKey: string,
|
|
6145
|
+
snakeKey: string,
|
|
6146
|
+
): string | null {
|
|
6147
|
+
const value = slackRowValue(row, camelKey, snakeKey);
|
|
6148
|
+
if (value === null || value === undefined) return null;
|
|
6149
|
+
if (typeof value !== "string") throw new Error(`Slack row malformed ${snakeKey}`);
|
|
6150
|
+
return value;
|
|
6151
|
+
}
|
|
6152
|
+
|
|
6153
|
+
function slackRowNumber(row: Record<string, unknown>, camelKey: string, snakeKey: string): number {
|
|
6154
|
+
const value = slackRowValue(row, camelKey, snakeKey);
|
|
6155
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
6156
|
+
if (!Number.isSafeInteger(parsed)) throw new Error(`Slack row malformed ${snakeKey}`);
|
|
6157
|
+
return parsed;
|
|
6158
|
+
}
|
|
6159
|
+
|
|
6160
|
+
function slackRowDate(row: Record<string, unknown>, camelKey: string, snakeKey: string): Date {
|
|
6161
|
+
const value = slackRowValue(row, camelKey, snakeKey);
|
|
6162
|
+
const parsed = value instanceof Date ? value : new Date(String(value));
|
|
6163
|
+
if (Number.isNaN(parsed.getTime())) throw new Error(`Slack row malformed ${snakeKey}`);
|
|
6164
|
+
return parsed;
|
|
6165
|
+
}
|
|
6166
|
+
|
|
6167
|
+
function slackRowNullableDate(
|
|
6168
|
+
row: Record<string, unknown>,
|
|
6169
|
+
camelKey: string,
|
|
6170
|
+
snakeKey: string,
|
|
6171
|
+
): Date | null {
|
|
6172
|
+
const value = slackRowValue(row, camelKey, snakeKey);
|
|
6173
|
+
if (value === null || value === undefined) return null;
|
|
6174
|
+
return slackRowDate(row, camelKey, snakeKey);
|
|
6175
|
+
}
|
|
6176
|
+
|
|
5300
6177
|
export class SlackBotLifecycleSuccessAuditError extends Error {
|
|
5301
6178
|
constructor() {
|
|
5302
6179
|
super("OpenGeni Slack bot lifecycle success audit failed");
|
|
@@ -5889,7 +6766,10 @@ export async function claimSlackBotDeleteOperation(
|
|
|
5889
6766
|
})
|
|
5890
6767
|
.returning();
|
|
5891
6768
|
if (created) {
|
|
5892
|
-
return {
|
|
6769
|
+
return {
|
|
6770
|
+
kind: "claimed",
|
|
6771
|
+
operation: mapSlackBotDeleteOperation(created),
|
|
6772
|
+
} as const;
|
|
5893
6773
|
}
|
|
5894
6774
|
|
|
5895
6775
|
const [existing] = await tx
|
|
@@ -6030,7 +6910,11 @@ export async function releaseSlackBotDeleteOperationClaim(
|
|
|
6030
6910
|
}
|
|
6031
6911
|
|
|
6032
6912
|
export type CompleteSlackBotDeleteOperationResult =
|
|
6033
|
-
| {
|
|
6913
|
+
| {
|
|
6914
|
+
kind: "completed";
|
|
6915
|
+
operation: SlackBotDeleteOperation;
|
|
6916
|
+
newlyCompleted: boolean;
|
|
6917
|
+
}
|
|
6034
6918
|
| { kind: "not_found" | "not_owned" };
|
|
6035
6919
|
|
|
6036
6920
|
export async function completeSlackBotDeleteOperation(
|
|
@@ -15680,9 +16564,21 @@ export async function sessionTreeStatsForSessions(
|
|
|
15680
16564
|
}
|
|
15681
16565
|
|
|
15682
16566
|
function sessionFilters(
|
|
15683
|
-
options: Pick<
|
|
16567
|
+
options: Pick<
|
|
16568
|
+
ListSessionsForSubjectOptions,
|
|
16569
|
+
"authorizationScope" | "parentSessionId" | "search" | "subjectId"
|
|
16570
|
+
>,
|
|
15684
16571
|
): SQL[] {
|
|
15685
|
-
const filters: SQL[] = [
|
|
16572
|
+
const filters: SQL[] = [
|
|
16573
|
+
sql`not exists (
|
|
16574
|
+
select 1
|
|
16575
|
+
from ${schema.slackInteractions} private_slack_interaction
|
|
16576
|
+
where private_slack_interaction.workspace_id = ${schema.sessions.workspaceId}
|
|
16577
|
+
and private_slack_interaction.session_reservation_id = ${schema.sessions.rootSessionId}
|
|
16578
|
+
and private_slack_interaction.visibility = 'private'
|
|
16579
|
+
and private_slack_interaction.owning_subject_id <> ${options.subjectId}
|
|
16580
|
+
)`,
|
|
16581
|
+
];
|
|
15686
16582
|
if (options.authorizationScope) {
|
|
15687
16583
|
filters.push(sessionAuthorizationScopeFilter(options.authorizationScope));
|
|
15688
16584
|
}
|
|
@@ -16295,7 +17191,20 @@ export async function getSessionForSubject(
|
|
|
16295
17191
|
eq(schema.sessionPins.sessionId, schema.sessions.id),
|
|
16296
17192
|
),
|
|
16297
17193
|
)
|
|
16298
|
-
.where(
|
|
17194
|
+
.where(
|
|
17195
|
+
and(
|
|
17196
|
+
eq(schema.sessions.workspaceId, workspaceId),
|
|
17197
|
+
eq(schema.sessions.id, sessionId),
|
|
17198
|
+
sql`not exists (
|
|
17199
|
+
select 1
|
|
17200
|
+
from ${schema.slackInteractions} private_slack_interaction
|
|
17201
|
+
where private_slack_interaction.workspace_id = ${schema.sessions.workspaceId}
|
|
17202
|
+
and private_slack_interaction.session_reservation_id = ${schema.sessions.rootSessionId}
|
|
17203
|
+
and private_slack_interaction.visibility = 'private'
|
|
17204
|
+
and private_slack_interaction.owning_subject_id <> ${subjectId}
|
|
17205
|
+
)`,
|
|
17206
|
+
),
|
|
17207
|
+
)
|
|
16299
17208
|
.limit(1);
|
|
16300
17209
|
if (!row) return null;
|
|
16301
17210
|
const mcpServers = await sessionMcpServerMetadataForSessions(scopedDb, workspaceId, [
|
|
@@ -19436,7 +20345,10 @@ export async function recordSkippedContextCompaction(
|
|
|
19436
20345
|
});
|
|
19437
20346
|
if (!fence.allowed) return { recorded: false as const, reason: fence.reason };
|
|
19438
20347
|
if (requirePendingRequest && !fence.session.compactRequested) {
|
|
19439
|
-
return {
|
|
20348
|
+
return {
|
|
20349
|
+
recorded: false as const,
|
|
20350
|
+
reason: "request_not_pending" as const,
|
|
20351
|
+
};
|
|
19440
20352
|
}
|
|
19441
20353
|
const inserted = await tx
|
|
19442
20354
|
.insert(schema.sessionEvents)
|
|
@@ -27566,7 +28478,11 @@ export type WorkspaceArchiveCaptureClaim = {
|
|
|
27566
28478
|
};
|
|
27567
28479
|
|
|
27568
28480
|
export type ClaimWorkspaceArchiveCaptureResult =
|
|
27569
|
-
| {
|
|
28481
|
+
| {
|
|
28482
|
+
status: "claimed";
|
|
28483
|
+
claim: WorkspaceArchiveCaptureClaim;
|
|
28484
|
+
lease: LeaseSnapshot;
|
|
28485
|
+
}
|
|
27570
28486
|
| {
|
|
27571
28487
|
status:
|
|
27572
28488
|
| "lease_fenced"
|
|
@@ -41457,3 +42373,4 @@ function shortHash(value: string): string {
|
|
|
41457
42373
|
// evaluates under the index↔resolver module cycle.
|
|
41458
42374
|
export * from "./codex-token-resolver";
|
|
41459
42375
|
export * from "./connection-token-resolver";
|
|
42376
|
+
export * from "./workspace-artifacts";
|