@sentry/junior 0.267.0 → 0.269.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -9
- package/dist/{actor-DZSokpwj.js → actor-CP9K2Idu.js} +2 -1
- package/dist/{adapter-BybMEezj.js → adapter-2-CG25be.js} +3 -51
- package/dist/{admin-DOs1_EiW.js → admin-DSs8S5Uy.js} +1 -1
- package/dist/{agent-invocations-CxKldAeB.js → agent-invocations-C1RNOCs8.js} +2 -0
- package/dist/{agent-runner-DgNATN4D.js → agent-runner-CrDrPUtY.js} +3411 -1640
- package/dist/api/schema.d.ts +2 -5630
- package/dist/api/schema.js +7 -7
- package/dist/api.d.ts +3 -1
- package/dist/api.js +268 -287
- package/dist/{app-DAB3fzyp.d.ts → app-BrMMzjYm.d.ts} +10 -3
- package/dist/app.d.ts +1 -1
- package/dist/app.js +371 -223
- package/dist/{automation-CmeFBSEh.js → automation-p87hg_AV.js} +74 -74
- package/dist/{briefs-CWKjka7v.js → briefs-PtODdLNu.js} +4 -4
- package/dist/{catalog-runtime-cMlAI5LY.js → catalog-runtime-BsoSmTM0.js} +1 -1
- package/dist/cli/chat.js +16 -26
- package/dist/cli/check.js +3 -3
- package/dist/cli/main.js +5 -3
- package/dist/cli/plugins.js +4 -4
- package/dist/cli/snapshot-create.js +4 -4
- package/dist/cli/upgrade.d.ts +1 -1
- package/dist/cli/upgrade.js +3 -3
- package/dist/{client-BjgbMawj.js → client-Cqf4TboL.js} +20 -15
- package/dist/{config-CdB6yJj5.js → config-CPsE4T5L.js} +2 -2
- package/dist/{connections-RE_65jzK.js → connections-upFXLmhG.js} +10 -11
- package/dist/{conversation-DSo310Ps.js → conversation-CTpD_PGk.js} +7 -84
- package/dist/{conversation-privacy-BzgJC5k0.js → conversation-privacy-D6zJc8ff.js} +1 -1
- package/dist/{credential-sync-DhEGEpgq.js → credential-sync-CS_6IC-f.js} +30 -34
- package/dist/{credentialed-CIGwoLU2.js → credentialed-BU-OW7HP.js} +577 -74
- package/dist/{store-B8CABbmR.d.ts → db-BzOCzVb1.d.ts} +17 -7
- package/dist/{executor-VYrx_K4w.js → executor-CNS7ONt-.js} +78 -4
- package/dist/instrumentation.js +2 -2
- package/dist/{assistant-reply-CT7VcXQe.js → interruption-marker-g6Xup3Fk.js} +8 -2
- package/dist/{job-queue-C0BZSa7r.js → job-queue-DI-k-Bbk.js} +2 -2
- package/dist/{list-1XQP-o6K.js → list-CGoQdXTp.js} +429 -1401
- package/dist/locks-D-0h111u.js +48 -0
- package/dist/{logging-DuZ1A4ab.js → logging-n7knse-O.js} +1 -1
- package/dist/{history-C84mlAwO.js → media-85PDJxO_.js} +83 -1
- package/dist/{message-NKSbbnA6.js → message-BpaWd8TT.js} +4 -4
- package/dist/nitro.d.ts +1 -1
- package/dist/nitro.js +3 -3
- package/dist/{oauth-callback-server-qAHLPtml.js → oauth-callback-server-CpGmEw_i.js} +2 -2
- package/dist/{prompt-BW9jFsqZ.js → prompt-BlQB0QQt.js} +1 -1
- package/dist/{registry-FQutO926.js → registry-By36_xsm.js} +2 -2
- package/dist/reporting-schema-CZREhYc-.js +104 -0
- package/dist/reporting-window-CVarfId9.js +255 -0
- package/dist/{resolve-Bk4yeuHy.js → resolve-CUIBcdPd.js} +3 -3
- package/dist/{result-B9e9ATOU.js → result-BemsOMOO.js} +31 -12
- package/dist/{runner-C3S2ObH-.js → runner-BVok7PAA.js} +12 -11
- package/dist/{sandbox-egress-signals-DqpGzzMB.js → sandbox-egress-signals-BSUWQI95.js} +1 -1
- package/dist/{schema-X7Fj4fv1.js → schema-ChnFo5f2.js} +98 -6
- package/dist/schema-jK4-1u_H.d.ts +5927 -0
- package/dist/{service-account-Do30HUNv.js → service-account-DHvlLnZw.js} +4 -3
- package/dist/{service-account-BQasl-qq.js → service-account-hMmESDcr.js} +3 -3
- package/dist/{session-stop-DkOjt46_.js → session-stop-BQW_iFcd.js} +499 -23
- package/dist/{skills-CQHrwfOa.js → skills-DmJJIuLF.js} +2 -2
- package/dist/{spawn-BHuqN14f.js → spawn-ChCm46i-.js} +2 -2
- package/dist/{store-6EetVP-m.js → store-1DVhoKOL.js} +4 -4
- package/dist/{store-DvNZIsCr.js → store-DCo2n5iJ.js} +3 -3
- package/dist/{task-queue-B0DLgd2P.js → task-queue-HMC4QjFm.js} +3 -3
- package/dist/task-runner-cA6yjn_E.js +269 -0
- package/dist/{thread-state-DRmdJ2QN.js → thread-state-CHy8LS0C.js} +8 -18
- package/dist/{turn-failure-response-G11cs2gE.js → turn-failure-response-Dr7erLWs.js} +11 -16
- package/dist/{turn-lifecycle-OzLIwvi1.js → turn-lifecycle-bf2fIYi_.js} +1 -1
- package/dist/{tasks-0ilmuCux.js → turn-session-routing-BqO0qXLk.js} +959 -44
- package/dist/{validation-CxEmt1id.js → validation-DHR1ivfh.js} +1 -1
- package/dist/{version-Bapedn4A.js → version-CXCHLgWm.js} +1 -1
- package/dist/version.js +1 -1
- package/dist/{viewer-DrN3oC2X.js → viewer-DSbD6DF6.js} +503 -241
- package/dist/{work-DUQoNcqP.js → work-BE_qTYGi.js} +17 -36
- package/dist/{worker-D1l4Ugk0.js → worker-Cm9_sWXg.js} +15 -7
- package/migrations/0065_user_customization.sql +1 -0
- package/migrations/0066_review_items.sql +53 -0
- package/migrations/0067_review_item_dedupe_tenant.sql +2 -0
- package/migrations/0068_review_item_slack_cards.sql +10 -0
- package/migrations/0069_system_identity.sql +25 -0
- package/migrations/0070_review_item_foreign_keys.sql +5 -0
- package/migrations/AGENTS.md +17 -0
- package/migrations/meta/0065_snapshot.json +3511 -0
- package/migrations/meta/0066_snapshot.json +4099 -0
- package/migrations/meta/0067_snapshot.json +3921 -0
- package/migrations/meta/0068_snapshot.json +4176 -0
- package/migrations/meta/0069_snapshot.json +3979 -0
- package/migrations/meta/0070_snapshot.json +3997 -0
- package/migrations/meta/_journal.json +42 -0
- package/package.json +5 -5
- package/dist/db-Bg-zC3r5.d.ts +0 -5
- package/dist/history-BgI33Kie.d.ts +0 -78
- package/dist/images-CecjXAAz.js +0 -64
- package/dist/task-runner-B-PD_JOj.js +0 -1074
- package/dist/turn-session-routing-Dyut9l7Y.js +0 -76
- package/dist/user-input-request-BDFBdF8r.js +0 -116
- package/dist/web-message-id-CJSUiksE.js +0 -236
- package/migrations/README.md +0 -30
package/dist/api.js
CHANGED
|
@@ -1,29 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { p as escapeXml } from "./client-
|
|
3
|
-
import { m as logInfo, p as logException } from "./logging-
|
|
4
|
-
import {
|
|
5
|
-
import { c as juniorIdentities, d as juniorDestinations, l as juniorUsers, s as juniorConversations } from "./agent-invocations-
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { A as createConversationMessageBodySchema,
|
|
13
|
-
import { h as getChildAgentLabel } from "./store-
|
|
14
|
-
import { d as getEventCatalog } from "./session-stop-
|
|
15
|
-
import { s as
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import { d as legacyWebMailboxMetadataSchema } from "./result-
|
|
24
|
-
import { a as startConnection, l as getWebAuthorization, n as isConnectableProvider, r as listConnections, t as disconnectConnection, u as htmlCallbackResponse } from "./connections-
|
|
25
|
-
import {
|
|
26
|
-
import { A as activityHours$1, C as personParamsSchema, D as locationDirectoryReportSchema, E as locationDetailReportSchema, H as codePersonReportSchema, I as disconnectConnectionResponseSchema, L as startConnectionResponseSchema, N as connectionListSchema, O as locationParamsSchema, P as connectionParamsSchema, Q as apiErrorSchema, S as actorProfileReportSchema, U as codeRepositoryParamsSchema, V as codeOverviewReportSchema, W as codeRepositoryReportSchema, X as stopConversationTurnResponseSchema, Y as conversationChildAgentsReportSchema, Z as conversationStatsReportSchema, _ as personalTokenListSchema, a as workspaceBodySchema, b as revokePersonalTokenResponseSchema, d as serviceAccountListSchema, f as serviceAccountParamsSchema, g as createdPersonalTokenSchema, h as createPersonalTokenBodySchema, i as deleteWorkspaceResponseSchema, j as activitySixHours$1, k as activityDays$1, l as workspaceSchema, m as startServiceAccountSignInResponseSchema, n as statsReportSchema, o as workspaceListSchema, q as conversationWatchesReportSchema, s as workspaceParamsSchema, u as disconnectServiceAccountResponseSchema, w as personalSpendReportSchema, x as actorDirectoryReportSchema, y as personalTokenParamsSchema } from "./schema-X7Fj4fv1.js";
|
|
1
|
+
import { F as conversationRangeColumns, N as conversationActiveDaysColumn, P as conversationAggregateColumns, Ur as isExperimentalFeatureEnabled, ct as AutomationConflictError, ft as automationEventCatalog, j as readAutomationTitlesByConversationId } from "./turn-session-routing-BqO0qXLk.js";
|
|
2
|
+
import { p as escapeXml } from "./client-Cqf4TboL.js";
|
|
3
|
+
import { m as logInfo, p as logException } from "./logging-n7knse-O.js";
|
|
4
|
+
import { C as getSqlExecutor, J as resolveRootConversationId, K as agentTurnUsageSchema, S as getDb, c as readViewerAvatarUrl, d as updateViewerDisplayName, l as resolveViewerUser, m as readAvatarImage, q as hasAgentTurnUsage, t as findUserByEmail, x as getConversationStore } from "./viewer-DSbD6DF6.js";
|
|
5
|
+
import { c as juniorIdentities, d as juniorDestinations, l as juniorUsers, s as juniorConversations } from "./agent-invocations-C1RNOCs8.js";
|
|
6
|
+
import { C as juniorCodeRepositories, S as juniorCodeChanges, b as juniorApiTokens, m as juniorConversationParticipants, v as juniorConversationMetrics, y as juniorConversationEvents } from "./executor-CNS7ONt-.js";
|
|
7
|
+
import { t as INPUT_IMAGE_TYPES } from "./media-85PDJxO_.js";
|
|
8
|
+
import { n as pluginCatalogRuntime } from "./catalog-runtime-BsoSmTM0.js";
|
|
9
|
+
import { $ as listOwnedWatches, B as changeViewerAutomationLifecycle, Dt as readUserCustomization, G as readConversationSourceTask, H as readViewerAutomationEdit, J as readViewerAutomationSummary, K as readViewerAutomationExecutions, Ot as saveUserCustomization, St as conversationSubtreeColumns, U as updateViewerAutomation, V as deleteViewerTask, W as ViewerTaskNotFoundError, X as readViewerAutomations, Y as readViewerAutomationVersions, Z as AutomationEditError, at as createAndEnqueueConversation, ct as webActorFromEmail, ft as readLiveAttachment, m as readStats, nt as acceptsWebMessages, q as readViewerAutomationRuns, rt as appendAndEnqueueWebMessage, ut as decodeInputImages, x as createSlackReviewItemShow, xt as conversationSubtree, yt as listChildAgents, z as activateViewerAutomationVersion } from "./agent-runner-CrDrPUtY.js";
|
|
10
|
+
import { $t as workspaceRepoCheckoutPath, Ar as lookupSlackUser, D as listWaitingConversationIds, Er as listConversationAnnotations, F as isOnlyAssignee, G as deleteWorkspace, J as listWorkspaces, K as getWorkspace, M as canActOnReviewItem, O as searchReviewItems, W as createWorkspace, X as updateWorkspace, Y as listWorkspacesByRepository, Z as WorkspaceValidationError, Zt as SANDBOX_RUNTIME, _r as readConversationEventPage, ar as createPluginLogger, gr as readLatestConversationBrief, jr as createPluginCache, n as createUserTokenStore, nt as create, or as readConversationAccessFromSql, p as getPlugins, rt as hash, vr as buildSentryConversationUrl, x as countReviewItemsNeedingUser, z as reviewItemReaderForUser } from "./credentialed-BU-OW7HP.js";
|
|
11
|
+
import { r as getStateAdapter } from "./adapter-2-CG25be.js";
|
|
12
|
+
import { A as createConversationMessageBodySchema, M as forkConversationResponseSchema, N as steerConversationPendingMessagesBodySchema, P as steerConversationPendingMessagesResponseSchema, W as readMessageAttachments, _ as conversationParamsSchema, a as cancelConversationPendingMessagesBodySchema, c as conversationAttachmentParamsSchema, d as conversationDetailReportSchema, g as conversationFeedSchema, h as conversationFeedQuerySchema, i as archiveConversationResponseSchema, j as forkConversationBodySchema, k as createConversationBodySchema, m as conversationEventsQuerySchema, o as cancelConversationPendingMessagesResponseSchema, p as conversationEventPageSchema, r as archiveConversationBodySchema, t as acceptedConversationMessageSchema, u as conversationDetailQuerySchema, x as conversationPendingMessagesReportSchema } from "./conversation-CTpD_PGk.js";
|
|
13
|
+
import { h as getChildAgentLabel } from "./store-1DVhoKOL.js";
|
|
14
|
+
import { d as getEventCatalog } from "./session-stop-BQW_iFcd.js";
|
|
15
|
+
import { a as fillUtcSixHours, c as trailingUtcDayWindow, d as utcDayKey, f as utcHourKey, i as fillUtcHours, l as trailingUtcHourWindow, n as HOUR_MS$1, o as startOfUtcHour, r as fillUtcDays, s as sumUtcHoursIntoSixHours, t as DAY_MS$1, u as trailingUtcSixHourWindow, x as userCustomizationSchema, y as webMessageId } from "./reporting-window-CVarfId9.js";
|
|
16
|
+
import { s as getSandboxResources, t as getCachedSnapshot } from "./resolve-CUIBcdPd.js";
|
|
17
|
+
import { a as getVercelConversationWorkQueue } from "./job-queue-DI-k-Bbk.js";
|
|
18
|
+
import { E as steerWebPendingMessages, a as cancelHumanFacingPendingMessages, g as getConversationWorkState, h as getConversation } from "./store-DCo2n5iJ.js";
|
|
19
|
+
import { n as finishServiceAccountSignIn, o as serviceAccountPlugins, r as getServiceAccountStatus, s as startServiceAccountSignIn, t as disconnectServiceAccount } from "./service-account-DHvlLnZw.js";
|
|
20
|
+
import { N as ScheduleIntentError, P as compileScheduleIntent, _ as automationSummarySchema, b as automationVersionListSchema, c as automationLifecycleSchema, d as automationParamsSchema, f as automationRunListSchema, g as automationSchedulePreviewSchema, h as automationScheduleIntentSchema, i as automationExecutionListSchema, l as automationListQuerySchema, n as automationEditSchema, r as automationEventCatalogSchema, t as automationEditErrorSchema, u as automationListSchema, v as automationUpdateSchema, x as automationVersionParamsSchema, y as automationVersionActivateSchema } from "./automation-p87hg_AV.js";
|
|
21
|
+
import { c as runtimeInfoReportSchema, n as pluginOperationalReportFeedSchema, s as pluginsSchema, t as healthReportSchema, u as skillReportsSchema } from "./reporting-schema-CZREhYc-.js";
|
|
22
|
+
import { B as actOnReviewItem, C as isAllowedSignInEmail, H as readReviewItemForUser, J as readHealthReport, N as stopConversationTurn, O as clearSlackPendingReactions, V as closeReviewItemsAnsweredByReply, W as getAttachmentStorage, a as resolveMentions, c as readConversationAuxiliaryCostsFromSql, d as notCopiedByFork, f as childAgentTotalsReport, g as resolveSlackTeamDomains, h as conversationSummaryFromStoredConversation, i as attachConversationMentions, l as ConversationForkError, m as conversationEventHistory, n as readConversationRecordFromSql, o as readFirstHumanMessageTexts, p as readConversationTreeMetricsFromSql, r as readConversationParticipants, s as listConversationSidebarAnnotations, t as readConversationFeed, u as forkConversation, y as getProductionSlackAdapter, z as ReviewItemActionError } from "./list-CGoQdXTp.js";
|
|
23
|
+
import { d as legacyWebMailboxMetadataSchema } from "./result-BemsOMOO.js";
|
|
24
|
+
import { a as startConnection, l as getWebAuthorization, n as isConnectableProvider, r as listConnections, t as disconnectConnection, u as htmlCallbackResponse } from "./connections-upFXLmhG.js";
|
|
25
|
+
import { A as avatarParamsSchema, C as createdPersonalTokenSchema, D as revokePersonalTokenResponseSchema, E as personalTokenParamsSchema, F as locationDirectoryReportSchema, G as startConnectionResponseSchema, H as connectionParamsSchema, I as locationParamsSchema, L as activityDays$1, M as personalSpendReportSchema, O as actorDirectoryReportSchema, P as locationDetailReportSchema, Q as codeRepositoryReportSchema, R as activityHours$1, S as createPersonalTokenBodySchema, V as connectionListSchema, W as disconnectConnectionResponseSchema, X as codePersonReportSchema, Y as codeOverviewReportSchema, Z as codeRepositoryParamsSchema, _ as disconnectServiceAccountResponseSchema, a as workspaceBodySchema, at as conversationStatsReportSchema, d as reviewItemCountSchema, f as reviewItemListQuerySchema, g as reviewItemWaitingConversationsSchema, h as reviewItemSchema, i as deleteWorkspaceResponseSchema, it as stopConversationTurnResponseSchema, j as personParamsSchema, k as actorProfileReportSchema, l as workspaceSchema, m as reviewItemParamsSchema, n as statsReportSchema, o as workspaceListSchema, ot as apiErrorSchema, p as reviewItemListSchema, rt as conversationChildAgentsReportSchema, s as workspaceParamsSchema, tt as conversationWatchesReportSchema, u as reviewItemActionSchema, v as serviceAccountListSchema, w as personalTokenListSchema, x as startServiceAccountSignInResponseSchema, y as serviceAccountParamsSchema, z as activitySixHours$1 } from "./schema-ChnFo5f2.js";
|
|
27
26
|
import { Hono } from "hono";
|
|
28
27
|
import path from "node:path";
|
|
29
28
|
import { z } from "zod";
|
|
@@ -257,8 +256,7 @@ async function createConversationForViewer(viewer, body, services) {
|
|
|
257
256
|
async function appendConversationMessageForViewer(viewer, conversationId, body, services) {
|
|
258
257
|
const conversation = await getConversationStore().get({ conversationId });
|
|
259
258
|
if (!conversation) throwApiError(404, "Conversation not found.");
|
|
260
|
-
|
|
261
|
-
if (!(destinationPlatform === "local" && conversationId.startsWith("local:web:") || destinationPlatform === "slack")) throwApiError(409, "Conversation does not accept web messages.");
|
|
259
|
+
if (!acceptsWebMessages(conversationId, conversation.destination)) throwApiError(409, "Conversation does not accept web messages.");
|
|
262
260
|
if (!(await readConversationAccessFromSql(getDb(), [conversationId], viewer)).get(conversationId)?.isParticipant) throwApiError(403, "Only conversation participants can add messages.");
|
|
263
261
|
const images = parseImages(body.images);
|
|
264
262
|
const actor = actorFromViewer(viewer);
|
|
@@ -273,31 +271,34 @@ async function appendConversationMessageForViewer(viewer, conversationId, body,
|
|
|
273
271
|
attachmentStorage: services.attachmentStorage,
|
|
274
272
|
queue: services.queue
|
|
275
273
|
});
|
|
276
|
-
const
|
|
274
|
+
const closeAnswered = () => closeReviewItemsAnsweredByReply({
|
|
275
|
+
conversationId,
|
|
276
|
+
delivery: {
|
|
277
|
+
queue: services.queue,
|
|
278
|
+
show: createSlackReviewItemShow(getProductionSlackAdapter)
|
|
279
|
+
},
|
|
280
|
+
resolveUser: async () => viewer,
|
|
281
|
+
text: body.message
|
|
282
|
+
});
|
|
277
283
|
try {
|
|
278
|
-
|
|
279
|
-
|
|
284
|
+
let accepted;
|
|
285
|
+
try {
|
|
286
|
+
accepted = await append();
|
|
287
|
+
} catch (error) {
|
|
280
288
|
const state = getStateAdapter();
|
|
281
289
|
await state.connect();
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
},
|
|
295
|
-
note: () => webUserInputAnswerNote(viewer.displayName ?? viewer.email),
|
|
296
|
-
persist: append,
|
|
297
|
-
state,
|
|
298
|
-
threadTs: conversation.location?.threadTs ?? parseSlackThreadId(conversationId)?.threadTs
|
|
299
|
-
});
|
|
300
|
-
});
|
|
290
|
+
const [messageId, work] = await Promise.all([webMessageId({
|
|
291
|
+
conversationId,
|
|
292
|
+
idempotencyKey: body.idempotencyKey
|
|
293
|
+
}), getConversationWorkState({
|
|
294
|
+
conversationId,
|
|
295
|
+
state
|
|
296
|
+
})]);
|
|
297
|
+
if (work?.execution.inboundMessageIds.includes(messageId)) await closeAnswered();
|
|
298
|
+
throw error;
|
|
299
|
+
}
|
|
300
|
+
await closeAnswered();
|
|
301
|
+
return accepted;
|
|
301
302
|
} catch (error) {
|
|
302
303
|
throwApiError(500, "Unable to append message.", error);
|
|
303
304
|
}
|
|
@@ -2695,7 +2696,7 @@ async function readPeoplePluginReports(args) {
|
|
|
2695
2696
|
reports: [],
|
|
2696
2697
|
source: "plugins"
|
|
2697
2698
|
});
|
|
2698
|
-
const { getPluginProfileReports } = await import("./credentialed-
|
|
2699
|
+
const { getPluginProfileReports } = await import("./credentialed-BU-OW7HP.js").then((n) => n.a);
|
|
2699
2700
|
return pluginOperationalReportFeedSchema.parse({
|
|
2700
2701
|
generatedAt: new Date(nowMs).toISOString(),
|
|
2701
2702
|
reports: await getPluginProfileReports({
|
|
@@ -2904,6 +2905,23 @@ function spendWindow(nowMs) {
|
|
|
2904
2905
|
thirtyDayStart
|
|
2905
2906
|
};
|
|
2906
2907
|
}
|
|
2908
|
+
/**
|
|
2909
|
+
* Read a cheap change marker for one viewer's spend window.
|
|
2910
|
+
*
|
|
2911
|
+
* Writes that record Conversation metrics also move `updated_at` forward, so
|
|
2912
|
+
* the marker changes when spend can change. The spend cache TTL bounds changes
|
|
2913
|
+
* that the marker misses, such as a day that leaves the window.
|
|
2914
|
+
*/
|
|
2915
|
+
async function readPersonalSpendVersion(email, nowMs = Date.now()) {
|
|
2916
|
+
const normalizedEmail = normalizeEmail$1(email);
|
|
2917
|
+
if (!normalizedEmail) return "";
|
|
2918
|
+
const { end, thirtyDayStart } = spendWindow(nowMs);
|
|
2919
|
+
const [row] = await getDb().select({
|
|
2920
|
+
conversations: sql`count(*)::int`,
|
|
2921
|
+
updatedAt: sql`max(${juniorConversations.updatedAt})::text`
|
|
2922
|
+
}).from(juniorConversations).innerJoin(juniorIdentities, eq(juniorIdentities.id, juniorConversations.actorIdentityId)).innerJoin(juniorUsers, eq(juniorUsers.id, juniorIdentities.userId)).where(and(verifiedActorWhere(normalizedEmail), gte(juniorConversations.lastActivityAt, thirtyDayStart), lte(juniorConversations.lastActivityAt, end)));
|
|
2923
|
+
return `${row?.conversations ?? 0}:${row?.updatedAt ?? ""}`;
|
|
2924
|
+
}
|
|
2907
2925
|
/** Read one viewer's rolling model spend with one bounded aggregate query. */
|
|
2908
2926
|
async function readPersonalSpendFromSql(email, nowMs = Date.now()) {
|
|
2909
2927
|
const normalizedEmail = normalizeEmail$1(email);
|
|
@@ -2936,19 +2954,27 @@ async function readPersonalSpendFromSql(email, nowMs = Date.now()) {
|
|
|
2936
2954
|
}
|
|
2937
2955
|
// src/api/people/spend.ts
|
|
2938
2956
|
const PERSONAL_SPEND_CACHE_TTL_MS = 3e5;
|
|
2939
|
-
/**
|
|
2957
|
+
/**
|
|
2958
|
+
* Create an app-scoped personal-spend reader.
|
|
2959
|
+
*
|
|
2960
|
+
* A cached report stays valid for five minutes, or until the viewer's spend
|
|
2961
|
+
* version changes. The dashboard can poll often and still see a finished Turn
|
|
2962
|
+
* without a full aggregate query on every poll.
|
|
2963
|
+
*/
|
|
2940
2964
|
function createPersonalSpendReader() {
|
|
2941
2965
|
const cache = /* @__PURE__ */ new Map();
|
|
2942
2966
|
return async function readPersonalSpend(email) {
|
|
2943
2967
|
const normalizedEmail = email.trim().toLowerCase();
|
|
2944
2968
|
const nowMs = Date.now();
|
|
2969
|
+
const version = await readPersonalSpendVersion(normalizedEmail, nowMs);
|
|
2945
2970
|
const cached = cache.get(normalizedEmail);
|
|
2946
|
-
if (cached && cached.expiresAtMs > nowMs) return cached.report;
|
|
2971
|
+
if (cached && cached.expiresAtMs > nowMs && cached.version === version) return cached.report;
|
|
2947
2972
|
cache.delete(normalizedEmail);
|
|
2948
2973
|
const report = readPersonalSpendFromSql(normalizedEmail, nowMs);
|
|
2949
2974
|
const entry = {
|
|
2950
2975
|
expiresAtMs: nowMs + PERSONAL_SPEND_CACHE_TTL_MS,
|
|
2951
|
-
report
|
|
2976
|
+
report,
|
|
2977
|
+
version
|
|
2952
2978
|
};
|
|
2953
2979
|
cache.set(normalizedEmail, entry);
|
|
2954
2980
|
try {
|
|
@@ -2964,6 +2990,17 @@ function createPersonalSpendReader() {
|
|
|
2964
2990
|
function createPeopleRoutes() {
|
|
2965
2991
|
const app = new Hono();
|
|
2966
2992
|
const readPersonalSpend = createPersonalSpendReader();
|
|
2993
|
+
app.get("/avatars/:identityId", requireViewer, validateRequest("param", avatarParamsSchema, "Invalid route parameters."), async (context) => {
|
|
2994
|
+
const { identityId } = context.req.valid("param");
|
|
2995
|
+
const image = await readAvatarImage(getDb(), getStateAdapter(), identityId);
|
|
2996
|
+
if (!image) throwApiError(404, "Avatar not found.");
|
|
2997
|
+
return new Response(Buffer.from(image.data, "base64"), { headers: {
|
|
2998
|
+
"cache-control": "private, max-age=86400",
|
|
2999
|
+
"content-security-policy": "default-src 'none'; sandbox",
|
|
3000
|
+
"content-type": image.contentType,
|
|
3001
|
+
"x-content-type-options": "nosniff"
|
|
3002
|
+
} });
|
|
3003
|
+
});
|
|
2967
3004
|
app.get("/me/spend", requireViewer, async (context) => {
|
|
2968
3005
|
const viewer = context.get("viewer");
|
|
2969
3006
|
return jsonResponse(personalSpendReportSchema, await readPersonalSpend(viewer.email));
|
|
@@ -3207,233 +3244,6 @@ function createUserPageRoutes() {
|
|
|
3207
3244
|
});
|
|
3208
3245
|
return app;
|
|
3209
3246
|
}
|
|
3210
|
-
// src/chat/automations/edit.ts
|
|
3211
|
-
/**
|
|
3212
|
-
* Web edits. Owners and public Destination readers can edit. Creator-only
|
|
3213
|
-
* rules still apply, and lifecycle actions stay creator-only.
|
|
3214
|
-
*/
|
|
3215
|
-
/** Owners and public Destination readers can edit. Others get not found. */
|
|
3216
|
-
async function requireEditableAutomation(user, id) {
|
|
3217
|
-
const candidate = await resolveViewerTaskCandidate(user, id);
|
|
3218
|
-
if (!candidate) throw new ViewerTaskNotFoundError();
|
|
3219
|
-
return candidate;
|
|
3220
|
-
}
|
|
3221
|
-
/** Lifecycle actions are creator-only. Others get not found. */
|
|
3222
|
-
async function requireOwnedAutomation(user, id) {
|
|
3223
|
-
const candidate = await requireEditableAutomation(user, id);
|
|
3224
|
-
if (!candidate.ownedByViewer) throw new ViewerTaskNotFoundError();
|
|
3225
|
-
return candidate;
|
|
3226
|
-
}
|
|
3227
|
-
/** Record the viewer's Slack identity in the Automation workspace as the editor. */
|
|
3228
|
-
function viewerEditor(user, automation) {
|
|
3229
|
-
if (automation.ownedByViewer) return automation.task.createdBy;
|
|
3230
|
-
const identity = user.identities.find((candidate) => candidate.provider === "slack" && candidate.providerTenantId === automation.task.destination.teamId);
|
|
3231
|
-
if (!identity) return void 0;
|
|
3232
|
-
const editor = { slackUserId: identity.providerSubjectId };
|
|
3233
|
-
if (identity.displayName) editor.fullName = identity.displayName;
|
|
3234
|
-
if (identity.handle) editor.userName = identity.handle;
|
|
3235
|
-
return editor;
|
|
3236
|
-
}
|
|
3237
|
-
function editView(automation, ownedByViewer) {
|
|
3238
|
-
const { task } = automation;
|
|
3239
|
-
const common = {
|
|
3240
|
-
ownedByViewer,
|
|
3241
|
-
id: task.id,
|
|
3242
|
-
revision: automationRevision(task),
|
|
3243
|
-
title: task.title ?? null,
|
|
3244
|
-
instruction: task.task.text,
|
|
3245
|
-
credentialMode: task.credentialMode,
|
|
3246
|
-
outcomes: task.outcomes,
|
|
3247
|
-
destination: task.destination,
|
|
3248
|
-
createdBy: task.createdBy
|
|
3249
|
-
};
|
|
3250
|
-
if (automation.kind === "scheduled") {
|
|
3251
|
-
const status = automation.task.status;
|
|
3252
|
-
if (status === "deleted") throw new ViewerTaskNotFoundError();
|
|
3253
|
-
return {
|
|
3254
|
-
...common,
|
|
3255
|
-
kind: "scheduled",
|
|
3256
|
-
status,
|
|
3257
|
-
schedule: automation.task.schedule,
|
|
3258
|
-
nextRunAtMs: automation.task.nextRunAtMs
|
|
3259
|
-
};
|
|
3260
|
-
}
|
|
3261
|
-
return {
|
|
3262
|
-
...common,
|
|
3263
|
-
kind: "event",
|
|
3264
|
-
status: automation.task.status === "deleted" ? "active" : automation.task.status,
|
|
3265
|
-
trigger: automation.task.trigger
|
|
3266
|
-
};
|
|
3267
|
-
}
|
|
3268
|
-
/** Load exact editable values by ID, independent of list filters. */
|
|
3269
|
-
async function readViewerAutomationEdit(user, id) {
|
|
3270
|
-
const automation = await requireEditableAutomation(user, id);
|
|
3271
|
-
return editView(automation, automation.ownedByViewer);
|
|
3272
|
-
}
|
|
3273
|
-
/** Save a partial edit against the read revision, without dispatching work. */
|
|
3274
|
-
async function updateViewerAutomation(user, id, input) {
|
|
3275
|
-
const current = await requireEditableAutomation(user, id);
|
|
3276
|
-
if (input.kind !== current.kind) throw new AutomationEditError("The trigger type cannot change.", "kind");
|
|
3277
|
-
requireAutomationRevision(current.task, input.revision);
|
|
3278
|
-
return saveViewerEdit(user, current, input);
|
|
3279
|
-
}
|
|
3280
|
-
/** `restored` carries version values that edit input cannot express, such as a cleared title. */
|
|
3281
|
-
async function saveViewerEdit(user, current, input, restored) {
|
|
3282
|
-
const isCreator = current.ownedByViewer;
|
|
3283
|
-
const editedBy = viewerEditor(user, current);
|
|
3284
|
-
if (input.kind === "scheduled" && current.kind === "scheduled") {
|
|
3285
|
-
const next = await editScheduledAutomation(current.task, input, isCreator, Date.now());
|
|
3286
|
-
return editView({
|
|
3287
|
-
kind: "scheduled",
|
|
3288
|
-
task: await saveScheduledAutomation(getDb(), restored ? {
|
|
3289
|
-
...next,
|
|
3290
|
-
...restored
|
|
3291
|
-
} : next, input.revision, editedBy)
|
|
3292
|
-
}, isCreator);
|
|
3293
|
-
}
|
|
3294
|
-
if (input.kind === "event" && current.kind === "event") {
|
|
3295
|
-
const next = await editEventAutomation(current.task, input, isCreator, getEventCatalog());
|
|
3296
|
-
const task = await saveEventAutomation(getDb(), restored ? {
|
|
3297
|
-
...next,
|
|
3298
|
-
title: restored.title
|
|
3299
|
-
} : next, input.revision, editedBy);
|
|
3300
|
-
if (!task) throw new ViewerTaskNotFoundError();
|
|
3301
|
-
return editView({
|
|
3302
|
-
kind: "event",
|
|
3303
|
-
task
|
|
3304
|
-
}, isCreator);
|
|
3305
|
-
}
|
|
3306
|
-
throw new ViewerTaskNotFoundError();
|
|
3307
|
-
}
|
|
3308
|
-
function scheduleIntent(schedule) {
|
|
3309
|
-
const recurrence = schedule.recurrence;
|
|
3310
|
-
if (schedule.kind !== "recurring" || !recurrence) throw new AutomationEditError("A one-time Schedule cannot be restored. Set a new time in the editor.", "schedule");
|
|
3311
|
-
return {
|
|
3312
|
-
kind: "recurring",
|
|
3313
|
-
frequency: recurrence.frequency,
|
|
3314
|
-
interval: recurrence.interval,
|
|
3315
|
-
time: `${String(recurrence.time.hour).padStart(2, "0")}:${String(recurrence.time.minute).padStart(2, "0")}`,
|
|
3316
|
-
weekdays: recurrence.weekdays?.map((day) => weekdaySchema.options[day]),
|
|
3317
|
-
dayOfMonth: recurrence.dayOfMonth,
|
|
3318
|
-
month: recurrence.month,
|
|
3319
|
-
startDate: recurrence.startDate,
|
|
3320
|
-
timezone: schedule.timezone
|
|
3321
|
-
};
|
|
3322
|
-
}
|
|
3323
|
-
/**
|
|
3324
|
-
* Turn saved outcomes back into edit input. Messages to the Automation
|
|
3325
|
-
* Destination or the creator DM are always allowed again. Other stored
|
|
3326
|
-
* Destinations must still be on the Automation.
|
|
3327
|
-
*/
|
|
3328
|
-
async function restorableOutcomes(current, outcomes) {
|
|
3329
|
-
const { task } = current;
|
|
3330
|
-
let creatorDm;
|
|
3331
|
-
const restored = [];
|
|
3332
|
-
for (const outcome of outcomes) {
|
|
3333
|
-
if (sameDefinitionValue(outcome.destination, task.destination)) {
|
|
3334
|
-
restored.push({
|
|
3335
|
-
action: outcome.action,
|
|
3336
|
-
destination: "current_conversation"
|
|
3337
|
-
});
|
|
3338
|
-
continue;
|
|
3339
|
-
}
|
|
3340
|
-
if (current.ownedByViewer && !task.outcomes.some((stored) => sameDefinitionValue(stored, outcome))) {
|
|
3341
|
-
creatorDm ??= (await resolveTaskOutcomes([{
|
|
3342
|
-
action: "send_message",
|
|
3343
|
-
destination: "task_creator"
|
|
3344
|
-
}], task.destination, task.createdBy.slackUserId))[0].destination;
|
|
3345
|
-
if (sameDefinitionValue(outcome.destination, creatorDm)) {
|
|
3346
|
-
restored.push({
|
|
3347
|
-
action: outcome.action,
|
|
3348
|
-
destination: "task_creator"
|
|
3349
|
-
});
|
|
3350
|
-
continue;
|
|
3351
|
-
}
|
|
3352
|
-
}
|
|
3353
|
-
restored.push(outcome);
|
|
3354
|
-
}
|
|
3355
|
-
return restored;
|
|
3356
|
-
}
|
|
3357
|
-
/**
|
|
3358
|
-
* Make a saved version active. This saves its definition as a new version
|
|
3359
|
-
* through the same edit rules, so history is never rewritten.
|
|
3360
|
-
*/
|
|
3361
|
-
async function activateViewerAutomationVersion(user, id, input) {
|
|
3362
|
-
const current = await requireEditableAutomation(user, id);
|
|
3363
|
-
requireAutomationRevision(current.task, input.revision);
|
|
3364
|
-
const saved = await readAutomationVersion(getDb(), id, input.version);
|
|
3365
|
-
if (!saved) throw new AutomationEditError("This version does not exist.", "version");
|
|
3366
|
-
const { definition } = saved;
|
|
3367
|
-
if (sameDefinitionValue(definition, automationDefinition(current.task))) return editView(current, current.ownedByViewer);
|
|
3368
|
-
if (!sameDefinitionValue(definition.destination, current.task.destination)) throw new AutomationEditError("This version delivers to another Destination. Move the automation from Slack instead.", "destination");
|
|
3369
|
-
const fields = {};
|
|
3370
|
-
if (definition.instruction !== current.task.task.text) fields.instruction = definition.instruction;
|
|
3371
|
-
if (definition.credentialMode !== current.task.credentialMode) fields.credentialMode = definition.credentialMode;
|
|
3372
|
-
if (!sameDefinitionValue(definition.outcomes, current.task.outcomes)) fields.outcomes = await restorableOutcomes(current, definition.outcomes);
|
|
3373
|
-
const title = definition.title ?? void 0;
|
|
3374
|
-
if (saved.kind === "scheduled" && current.kind === "scheduled") {
|
|
3375
|
-
const update = {
|
|
3376
|
-
kind: "scheduled",
|
|
3377
|
-
revision: input.revision,
|
|
3378
|
-
...fields
|
|
3379
|
-
};
|
|
3380
|
-
const restored = { title };
|
|
3381
|
-
const { schedule } = saved.definition;
|
|
3382
|
-
if (!sameDefinitionValue(schedule, current.task.schedule)) {
|
|
3383
|
-
update.schedule = scheduleIntent(schedule);
|
|
3384
|
-
restored.schedule = schedule;
|
|
3385
|
-
}
|
|
3386
|
-
return saveViewerEdit(user, current, update, restored);
|
|
3387
|
-
}
|
|
3388
|
-
if (saved.kind === "event" && current.kind === "event") {
|
|
3389
|
-
const update = {
|
|
3390
|
-
kind: "event",
|
|
3391
|
-
revision: input.revision,
|
|
3392
|
-
...fields
|
|
3393
|
-
};
|
|
3394
|
-
if (!sameDefinitionValue(saved.definition.trigger, current.task.trigger)) update.trigger = saved.definition.trigger;
|
|
3395
|
-
return saveViewerEdit(user, current, update, { title });
|
|
3396
|
-
}
|
|
3397
|
-
throw new ViewerTaskNotFoundError();
|
|
3398
|
-
}
|
|
3399
|
-
/** Pause or resume future triggers. Already-claimed work can finish. */
|
|
3400
|
-
async function changeViewerAutomationLifecycle(user, id, input) {
|
|
3401
|
-
const current = await requireOwnedAutomation(user, id);
|
|
3402
|
-
requireAutomationRevision(current.task, input.revision);
|
|
3403
|
-
const status = current.task.status;
|
|
3404
|
-
if (status === "completed") throw new AutomationEditError("Completed Automations cannot be restarted.");
|
|
3405
|
-
if (input.action === "pause" && status === "paused") return editView(current, true);
|
|
3406
|
-
if (input.action === "resume" && status !== "paused" && status !== "blocked") throw new AutomationEditError("Only paused or blocked Automations can be resumed.");
|
|
3407
|
-
if (current.kind === "event") return editView({
|
|
3408
|
-
kind: "event",
|
|
3409
|
-
task: await setEventAutomationStatus(getDb(), id, input.action === "pause" ? "paused" : "active", input.revision)
|
|
3410
|
-
}, true);
|
|
3411
|
-
const nowMs = Date.now();
|
|
3412
|
-
const next = {
|
|
3413
|
-
...current.task,
|
|
3414
|
-
updatedAtMs: nowMs,
|
|
3415
|
-
runNowAtMs: void 0
|
|
3416
|
-
};
|
|
3417
|
-
if (input.action === "pause") {
|
|
3418
|
-
next.status = "paused";
|
|
3419
|
-
if (status === "blocked") next.statusReason ??= "A requirement blocked this Automation. Inspect its executions before resuming.";
|
|
3420
|
-
} else {
|
|
3421
|
-
next.status = status === "paused" && next.statusReason ? "blocked" : "active";
|
|
3422
|
-
if (next.status === "active") {
|
|
3423
|
-
next.nextRunAtMs = next.schedule.recurrence ? getFirstRunAtMs({
|
|
3424
|
-
afterMs: nowMs,
|
|
3425
|
-
recurrence: next.schedule.recurrence,
|
|
3426
|
-
timezone: next.schedule.timezone
|
|
3427
|
-
}) : next.nextRunAtMs;
|
|
3428
|
-
if (!next.nextRunAtMs || next.nextRunAtMs <= nowMs) throw new AutomationEditError("This Schedule has no future occurrence. Set a future Schedule before resuming.", "schedule");
|
|
3429
|
-
next.statusReason = void 0;
|
|
3430
|
-
}
|
|
3431
|
-
}
|
|
3432
|
-
return editView({
|
|
3433
|
-
kind: "scheduled",
|
|
3434
|
-
task: await saveScheduledAutomation(getDb(), next, input.revision)
|
|
3435
|
-
}, true);
|
|
3436
|
-
}
|
|
3437
3247
|
// src/chat/automations/editor.ts
|
|
3438
3248
|
/** Read-only editor support. Core owns schedules; plugins own Event choices. */
|
|
3439
3249
|
/**
|
|
@@ -3611,6 +3421,171 @@ function createAutomationRoutes() {
|
|
|
3611
3421
|
});
|
|
3612
3422
|
return app;
|
|
3613
3423
|
}
|
|
3424
|
+
// src/api/user-customization/routes.ts
|
|
3425
|
+
/**
|
|
3426
|
+
* Create routes that read and replace the viewer's customization. They return
|
|
3427
|
+
* 404 unless the app enables the `user-customization` experiment.
|
|
3428
|
+
*/
|
|
3429
|
+
function createUserCustomizationRoutes() {
|
|
3430
|
+
const app = new Hono();
|
|
3431
|
+
app.use("*", async (_context, next) => {
|
|
3432
|
+
if (!isExperimentalFeatureEnabled("user-customization")) throwApiError(404, "Resource not found.");
|
|
3433
|
+
await next();
|
|
3434
|
+
});
|
|
3435
|
+
app.get("/", requireViewer, async (context) => {
|
|
3436
|
+
const viewer = context.get("viewer");
|
|
3437
|
+
return jsonResponse(userCustomizationSchema, { customization: await readUserCustomization(viewer.id) ?? "" });
|
|
3438
|
+
});
|
|
3439
|
+
app.put("/", requireViewer, validateRequest("json", userCustomizationSchema, "Invalid request body."), async (context) => {
|
|
3440
|
+
const viewer = context.get("viewer");
|
|
3441
|
+
const { customization } = context.req.valid("json");
|
|
3442
|
+
if (!await saveUserCustomization(viewer.id, customization)) throwApiError(404, "User not found.");
|
|
3443
|
+
return jsonResponse(userCustomizationSchema, { customization });
|
|
3444
|
+
});
|
|
3445
|
+
return app;
|
|
3446
|
+
}
|
|
3447
|
+
// src/api/review-items/routes.ts
|
|
3448
|
+
const ARCHIVED_LIMIT = 50;
|
|
3449
|
+
const cursorSchema = z.object({
|
|
3450
|
+
conversationId: z.string().min(1).optional(),
|
|
3451
|
+
id: z.string().min(1),
|
|
3452
|
+
query: z.string().min(1).optional(),
|
|
3453
|
+
updatedAtMs: z.number().int(),
|
|
3454
|
+
version: z.literal(1)
|
|
3455
|
+
}).strict();
|
|
3456
|
+
/**
|
|
3457
|
+
* Encode the last open item of a page as the cursor of the next page. The
|
|
3458
|
+
* cursor names its list, so it cannot page through a different list.
|
|
3459
|
+
*/
|
|
3460
|
+
function encodeCursor(item, filters) {
|
|
3461
|
+
return Buffer.from(JSON.stringify({
|
|
3462
|
+
...filters,
|
|
3463
|
+
id: item.id,
|
|
3464
|
+
updatedAtMs: item.updatedAt.getTime(),
|
|
3465
|
+
version: 1
|
|
3466
|
+
}), "utf8").toString("base64url");
|
|
3467
|
+
}
|
|
3468
|
+
/** Decode a cursor of the same list, or return undefined. */
|
|
3469
|
+
function decodeCursor(cursor, filters) {
|
|
3470
|
+
try {
|
|
3471
|
+
const parsed = cursorSchema.parse(JSON.parse(Buffer.from(cursor, "base64url").toString("utf8")));
|
|
3472
|
+
if (parsed.conversationId !== filters.conversationId || parsed.query !== filters.query) return;
|
|
3473
|
+
return {
|
|
3474
|
+
id: parsed.id,
|
|
3475
|
+
updatedAt: new Date(parsed.updatedAtMs)
|
|
3476
|
+
};
|
|
3477
|
+
} catch {
|
|
3478
|
+
return;
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3481
|
+
const ERROR_STATUS = {
|
|
3482
|
+
conflict: 409,
|
|
3483
|
+
forbidden: 403,
|
|
3484
|
+
invalid: 400,
|
|
3485
|
+
not_found: 404
|
|
3486
|
+
};
|
|
3487
|
+
/** Build the API payload of one Review item for its viewer. */
|
|
3488
|
+
function reviewItemPayload(item, viewer) {
|
|
3489
|
+
return {
|
|
3490
|
+
addedByConversationId: item.addedByConversationId,
|
|
3491
|
+
assignedToEveryone: item.assignedToEveryone,
|
|
3492
|
+
assignees: item.assignees,
|
|
3493
|
+
body: item.body,
|
|
3494
|
+
canAct: canActOnReviewItem(item, viewer.id),
|
|
3495
|
+
canTake: canActOnReviewItem(item, viewer.id) && !isOnlyAssignee(item, viewer.id),
|
|
3496
|
+
conversationIds: item.conversationIds,
|
|
3497
|
+
createdAt: item.createdAt.toISOString(),
|
|
3498
|
+
id: item.id,
|
|
3499
|
+
kind: item.kind,
|
|
3500
|
+
status: item.status,
|
|
3501
|
+
title: item.title,
|
|
3502
|
+
updatedAt: item.updatedAt.toISOString(),
|
|
3503
|
+
visibility: item.visibility,
|
|
3504
|
+
...item.answer ? { answer: item.answer } : void 0,
|
|
3505
|
+
...item.automationId ? { automationId: item.automationId } : void 0,
|
|
3506
|
+
...item.closeReason ? { closeReason: item.closeReason } : void 0,
|
|
3507
|
+
...item.closedAt ? { closedAt: item.closedAt.toISOString() } : void 0,
|
|
3508
|
+
...item.closedBy ? { closedBy: item.closedBy } : void 0,
|
|
3509
|
+
...item.request ? { request: item.request } : void 0,
|
|
3510
|
+
...item.subject ? { subject: item.subject } : void 0
|
|
3511
|
+
};
|
|
3512
|
+
}
|
|
3513
|
+
function actionErrorResponse(error) {
|
|
3514
|
+
if (error instanceof ReviewItemActionError) return jsonResponse(apiErrorSchema, { error: error.message }, { status: ERROR_STATUS[error.code] });
|
|
3515
|
+
throw error;
|
|
3516
|
+
}
|
|
3517
|
+
/** Create the authenticated review desk routes. */
|
|
3518
|
+
function createReviewItemRoutes(services) {
|
|
3519
|
+
const app = new Hono();
|
|
3520
|
+
app.get("/", requireViewer, validateRequest("query", reviewItemListQuerySchema, "Invalid review item query."), async (context) => {
|
|
3521
|
+
const viewer = context.get("viewer");
|
|
3522
|
+
const query = context.req.valid("query");
|
|
3523
|
+
const filters = {
|
|
3524
|
+
...query.conversationId ? { conversationId: query.conversationId } : void 0,
|
|
3525
|
+
...query.q ? { query: query.q } : void 0
|
|
3526
|
+
};
|
|
3527
|
+
const after = query.cursor ? decodeCursor(query.cursor, filters) : void 0;
|
|
3528
|
+
if (query.cursor && !after) return jsonResponse(apiErrorSchema, { error: "Invalid review item cursor." }, { status: 400 });
|
|
3529
|
+
const db = getDb();
|
|
3530
|
+
const reader = reviewItemReaderForUser(viewer);
|
|
3531
|
+
const open = await searchReviewItems(db, reader, {
|
|
3532
|
+
...filters,
|
|
3533
|
+
limit: query.limit + 1,
|
|
3534
|
+
statuses: ["open"],
|
|
3535
|
+
...after ? { after } : void 0
|
|
3536
|
+
});
|
|
3537
|
+
const page = open.slice(0, query.limit);
|
|
3538
|
+
const last = page.at(-1);
|
|
3539
|
+
const archived = filters.query && !after ? await searchReviewItems(db, reader, {
|
|
3540
|
+
...filters,
|
|
3541
|
+
limit: 51,
|
|
3542
|
+
statuses: ["done", "dismissed"]
|
|
3543
|
+
}) : [];
|
|
3544
|
+
return jsonResponse(reviewItemListSchema, {
|
|
3545
|
+
items: [...page, ...archived.slice(0, ARCHIVED_LIMIT)].map((item) => reviewItemPayload(item, viewer)),
|
|
3546
|
+
...archived.length > ARCHIVED_LIMIT ? { moreArchived: true } : void 0,
|
|
3547
|
+
...open.length > query.limit && last ? { nextCursor: encodeCursor(last, filters) } : void 0
|
|
3548
|
+
});
|
|
3549
|
+
});
|
|
3550
|
+
app.get("/count", requireViewer, async (context) => {
|
|
3551
|
+
const viewer = context.get("viewer");
|
|
3552
|
+
return jsonResponse(reviewItemCountSchema, { needsViewer: await countReviewItemsNeedingUser(getDb(), viewer.id) });
|
|
3553
|
+
});
|
|
3554
|
+
app.get("/waiting-conversations", requireViewer, async (context) => {
|
|
3555
|
+
const viewer = context.get("viewer");
|
|
3556
|
+
return jsonResponse(reviewItemWaitingConversationsSchema, { conversationIds: await listWaitingConversationIds(getDb(), viewer.id) });
|
|
3557
|
+
});
|
|
3558
|
+
app.get("/:id", requireViewer, validateRequest("param", reviewItemParamsSchema, "Invalid review item."), async (context) => {
|
|
3559
|
+
const viewer = context.get("viewer");
|
|
3560
|
+
try {
|
|
3561
|
+
const item = await readReviewItemForUser(viewer, context.req.valid("param").id);
|
|
3562
|
+
return jsonResponse(reviewItemSchema, reviewItemPayload(item, viewer));
|
|
3563
|
+
} catch (error) {
|
|
3564
|
+
return actionErrorResponse(error);
|
|
3565
|
+
}
|
|
3566
|
+
});
|
|
3567
|
+
app.post("/:id/actions", requireViewer, validateRequest("param", reviewItemParamsSchema, "Invalid review item."), validateRequest("json", reviewItemActionSchema, "Invalid action."), async (context) => {
|
|
3568
|
+
const viewer = context.get("viewer");
|
|
3569
|
+
const body = context.req.valid("json");
|
|
3570
|
+
const { id } = context.req.valid("param");
|
|
3571
|
+
try {
|
|
3572
|
+
const item = await actOnReviewItem({
|
|
3573
|
+
action: body.action,
|
|
3574
|
+
delivery: services.delivery,
|
|
3575
|
+
id,
|
|
3576
|
+
user: viewer,
|
|
3577
|
+
...body.action === "submit" ? { answer: {
|
|
3578
|
+
...body.answer,
|
|
3579
|
+
requestId: id
|
|
3580
|
+
} } : void 0
|
|
3581
|
+
});
|
|
3582
|
+
return jsonResponse(reviewItemSchema, reviewItemPayload(item, viewer));
|
|
3583
|
+
} catch (error) {
|
|
3584
|
+
return actionErrorResponse(error);
|
|
3585
|
+
}
|
|
3586
|
+
});
|
|
3587
|
+
return app;
|
|
3588
|
+
}
|
|
3614
3589
|
// src/api/workspaces/routes.ts
|
|
3615
3590
|
function snapshotView(workspace) {
|
|
3616
3591
|
const recorded = workspace.snapshot;
|
|
@@ -3735,7 +3710,7 @@ function readDescriptionText(home) {
|
|
|
3735
3710
|
}
|
|
3736
3711
|
/** Read discovered skill names for authenticated runtime diagnostics. */
|
|
3737
3712
|
async function readSkillReports() {
|
|
3738
|
-
const { discoverSkills } = await import("./skills-
|
|
3713
|
+
const { discoverSkills } = await import("./skills-DmJJIuLF.js").then((n) => n.o);
|
|
3739
3714
|
const skills = await discoverSkills();
|
|
3740
3715
|
return skillReportsSchema.parse(skills.map((skill) => ({
|
|
3741
3716
|
name: skill.name,
|
|
@@ -3744,7 +3719,7 @@ async function readSkillReports() {
|
|
|
3744
3719
|
}
|
|
3745
3720
|
/** Read safe configured plugin metadata for authenticated runtime diagnostics. */
|
|
3746
3721
|
async function readPlugins() {
|
|
3747
|
-
const { pluginCatalogRuntime } = await import("./catalog-runtime-
|
|
3722
|
+
const { pluginCatalogRuntime } = await import("./catalog-runtime-BsoSmTM0.js").then((n) => n.t);
|
|
3748
3723
|
return pluginsSchema.parse(pluginCatalogRuntime.getProviders().map((plugin) => ({
|
|
3749
3724
|
configKeys: plugin.manifest.configKeys ?? [],
|
|
3750
3725
|
description: plugin.manifest.description,
|
|
@@ -3756,7 +3731,7 @@ async function readPlugins() {
|
|
|
3756
3731
|
async function readRuntimeInfoReport() {
|
|
3757
3732
|
const [{ homeDir }, { pluginCatalogRuntime }, plugins, skills] = await Promise.all([
|
|
3758
3733
|
import("./package-discovery-DJBompdd.js").then((n) => n.r),
|
|
3759
|
-
import("./catalog-runtime-
|
|
3734
|
+
import("./catalog-runtime-BsoSmTM0.js").then((n) => n.t),
|
|
3760
3735
|
readPlugins(),
|
|
3761
3736
|
readSkillReports()
|
|
3762
3737
|
]);
|
|
@@ -3773,7 +3748,7 @@ async function readRuntimeInfoReport() {
|
|
|
3773
3748
|
/** Read sanitized operational summaries contributed by plugins. */
|
|
3774
3749
|
async function readPluginOperationalReportFeed() {
|
|
3775
3750
|
const nowMs = Date.now();
|
|
3776
|
-
const { getPluginOperationalReports } = await import("./credentialed-
|
|
3751
|
+
const { getPluginOperationalReports } = await import("./credentialed-BU-OW7HP.js").then((n) => n.a);
|
|
3777
3752
|
return pluginOperationalReportFeedSchema.parse({
|
|
3778
3753
|
source: "plugins",
|
|
3779
3754
|
generatedAt: new Date(nowMs).toISOString(),
|
|
@@ -3790,10 +3765,15 @@ function createJuniorApi(options = {}) {
|
|
|
3790
3765
|
app.get("/api/skills", async () => jsonResponse(skillReportsSchema, await readSkillReports()));
|
|
3791
3766
|
app.get("/api/plugin-reports", async () => jsonResponse(pluginOperationalReportFeedSchema, await readPluginOperationalReportFeed()));
|
|
3792
3767
|
app.get("/api/stats", async () => jsonResponse(statsReportSchema, await readStatsReport()));
|
|
3768
|
+
const queue = options.conversationWorkQueue ?? getVercelConversationWorkQueue();
|
|
3793
3769
|
app.route("/api/conversations", createConversationRoutes({
|
|
3794
3770
|
attachmentStorage: getAttachmentStorage(),
|
|
3795
|
-
queue
|
|
3771
|
+
queue
|
|
3796
3772
|
}));
|
|
3773
|
+
app.route("/api/review-items", createReviewItemRoutes({ delivery: {
|
|
3774
|
+
queue,
|
|
3775
|
+
show: createSlackReviewItemShow(getProductionSlackAdapter)
|
|
3776
|
+
} }));
|
|
3797
3777
|
app.route("/api/code", createCodeRoutes());
|
|
3798
3778
|
app.route("/api/connections", createConnectionRoutes());
|
|
3799
3779
|
app.route("/api/personal-tokens", createPersonalTokenRoutes());
|
|
@@ -3801,6 +3781,7 @@ function createJuniorApi(options = {}) {
|
|
|
3801
3781
|
app.route("/api/people", createPeopleRoutes());
|
|
3802
3782
|
app.route("/api/locations", createLocationRoutes());
|
|
3803
3783
|
app.route("/api/user-pages", createUserPageRoutes());
|
|
3784
|
+
app.route("/api/user-customization", createUserCustomizationRoutes());
|
|
3804
3785
|
app.route("/api/automations", createAutomationRoutes());
|
|
3805
3786
|
app.route("/api/workspaces", createWorkspaceRoutes());
|
|
3806
3787
|
app.notFound(() => jsonResponse(apiErrorSchema, { error: "Resource not found." }, { status: 404 }));
|
|
@@ -3814,4 +3795,4 @@ function createJuniorApi(options = {}) {
|
|
|
3814
3795
|
});
|
|
3815
3796
|
return app;
|
|
3816
3797
|
}
|
|
3817
|
-
export { authenticatePersonalToken, createJuniorApi, isAllowedSignInEmail, jsonResponse, resolveViewerUser, updateViewerDisplayName };
|
|
3798
|
+
export { authenticatePersonalToken, createJuniorApi, isAllowedSignInEmail, jsonResponse, readViewerAvatarUrl, resolveViewerUser, updateViewerDisplayName };
|