@sentry/junior 0.221.0 → 0.222.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-CFrfUdN5.js → agent-7kKtDe-6.js} +47 -27
- package/dist/{agent-hooks-kjLDY-Aa.js → agent-hooks-DkriF84n.js} +9 -8
- package/dist/{agent-runner-DMMa36Zl.js → agent-runner-D0ImXS7U.js} +6 -5
- package/dist/api/schema.d.ts +51 -2
- package/dist/api/schema.js +4 -4
- package/dist/api.js +18 -515
- package/dist/{app-BYD2Nyji.d.ts → app-C0zbsnr7.d.ts} +1 -1
- package/dist/app.d.ts +1 -1
- package/dist/app.js +153 -32
- package/dist/{auth-pause-state-TdhAyYM4.js → auth-pause-state-3OT67TA8.js} +52 -7
- package/dist/{briefs-CIN2DMSo.js → briefs-CNH1ztn5.js} +4 -4
- package/dist/{catalog-runtime-D1v0KsgY.js → catalog-runtime-XH_LFA_7.js} +1 -1
- package/dist/{checkpoint-B2-uc-1h.js → checkpoint-CRod8es5.js} +8 -7
- package/dist/cli/chat.js +13 -13
- package/dist/cli/check.js +3 -3
- package/dist/cli/main.js +1 -1
- package/dist/cli/plugins.js +3 -3
- package/dist/cli/snapshot-create.js +3 -3
- package/dist/cli/upgrade.js +3 -3
- package/dist/{client-DIe9JhwF.js → client-CcSboWx_.js} +2 -2
- package/dist/{config-0_JixokJ.js → config-CGYv-YPE.js} +2 -2
- package/dist/{conversation-BSRGzJmY.js → conversation-BBDt88di.js} +46 -3
- package/dist/{conversation-privacy-6NLCEkzx.js → conversation-privacy-BGjdDVWD.js} +1 -1
- package/dist/{credential-sync-Dqg2o90r.js → credential-sync-BfzGNruT.js} +125 -30
- package/dist/{execution-stats-vnXVMVeu.js → execution-stats-D97ePWuM.js} +3 -3
- package/dist/{executor-CmZKtNpH.js → executor-BLBZ-UB8.js} +1 -1
- package/dist/{history-ClE4toxe.d.ts → history-CaV8Ww7M.d.ts} +16 -1
- package/dist/instrumentation.js +2 -2
- package/dist/{job-queue-B1k3woMW.js → job-queue-_8gxwDzt.js} +1 -1
- package/dist/{logging-D2YJv_PA.js → logging-qmsMdU0y.js} +1 -1
- package/dist/nitro.d.ts +1 -1
- package/dist/nitro.js +3 -3
- package/dist/{oauth-callback-server-BQtO5l5M.js → oauth-callback-server-Mjfm46A5.js} +2 -2
- package/dist/{pending-auth-DuSNoTpn.js → pending-auth-_1Ry-_Bw.js} +2 -2
- package/dist/{profile-C3TEFAVI.js → profile-DbG8AlNd.js} +4 -4
- package/dist/{prompt-BkaHwBW0.js → prompt-B8rCQI7K.js} +1 -1
- package/dist/{registry-Bp0kd2xe.js → registry-DGyQCsiy.js} +1 -1
- package/dist/reporting-window-DbUqng0P.js +229 -0
- package/dist/{resolve-BABNNBLL.js → resolve-D5SqDbCA.js} +2 -2
- package/dist/{runner-Bvn90ijl.js → runner-BRAFjJhe.js} +21 -10
- package/dist/{sandbox-egress-signals-DLfYAOiO.js → sandbox-egress-signals-CdI1aVHT.js} +1 -1
- package/dist/{schema-YPJXVcOW.js → schema-CTOODa3L.js} +5 -128
- package/dist/{short-title-C2fM-ERl.js → short-title-CB2fxEyx.js} +2 -2
- package/dist/{skills-D1otDVYp.js → skills-BoS3b8cg.js} +2 -2
- package/dist/{spawn-C-fAS-Rk.js → spawn-BXCU1udr.js} +2 -2
- package/dist/{stats-BfUFTUrM.js → stats-qUXQZq-w.js} +52 -13
- package/dist/{task-queue-VdYCCwO0.js → task-queue-Di_q8922.js} +3 -3
- package/dist/{task-runner-6tzKHNbM.js → task-runner-4rCppQMo.js} +8 -8
- package/dist/{thread-state-CD_fsyrM.js → thread-state-2Paza8DV.js} +5 -5
- package/dist/{turn-execution-eVhqozjK.js → turn-execution--F7a9kEU.js} +3 -3
- package/dist/{turn-lifecycle-CxAtVANu.js → turn-lifecycle-f-y0Irpi.js} +1 -1
- package/dist/{validation-CMERqrCD.js → validation-Be9lSORU.js} +1 -1
- package/dist/{version-C8XFxpkQ.js → version-BydrOZMQ.js} +1 -1
- package/dist/version.js +1 -1
- package/dist/{web-authorization-Dq2NDhY0.js → web-authorization-BdfOkTuG.js} +1 -1
- package/dist/web-input-DegYUyKU.js +899 -0
- package/dist/{work-DCwaUeWu.js → work-Citqpnfc.js} +9 -9
- package/dist/{worker-grswNVoc.js → worker-Bn_HFyvM.js} +5 -5
- package/migrations/0044_event_automation_channel_destinations.sql +36 -0
- package/migrations/meta/0044_snapshot.json +3473 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +5 -5
- package/dist/reporting-schema-DosTN__Q.js +0 -105
- package/dist/web-input-Bdh_rCZ7.js +0 -392
|
@@ -309,6 +309,13 @@
|
|
|
309
309
|
"when": 1789999722695,
|
|
310
310
|
"tag": "0043_scheduled_automation_channel_destinations",
|
|
311
311
|
"breakpoints": true
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
"idx": 44,
|
|
315
|
+
"version": "7",
|
|
316
|
+
"when": 1790115429065,
|
|
317
|
+
"tag": "0044_event_automation_channel_destinations",
|
|
318
|
+
"breakpoints": true
|
|
312
319
|
}
|
|
313
320
|
]
|
|
314
321
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.222.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"pg": "^8.16.3",
|
|
86
86
|
"yaml": "^2.9.0",
|
|
87
87
|
"zod": "^4.5.4",
|
|
88
|
-
"@sentry/junior-plugin-api": "0.
|
|
88
|
+
"@sentry/junior-plugin-api": "0.222.0"
|
|
89
89
|
},
|
|
90
90
|
"devDependencies": {
|
|
91
91
|
"@emnapi/core": "^1.10.0",
|
|
@@ -104,9 +104,9 @@
|
|
|
104
104
|
"typescript": "^6.0.3",
|
|
105
105
|
"vercel": "^54.4.0",
|
|
106
106
|
"vitest": "^4.1.7",
|
|
107
|
-
"@sentry/junior-github": "0.
|
|
108
|
-
"@sentry/junior-
|
|
109
|
-
"@sentry/junior-
|
|
107
|
+
"@sentry/junior-github": "0.222.0",
|
|
108
|
+
"@sentry/junior-testing": "0.0.0",
|
|
109
|
+
"@sentry/junior-memory": "0.222.0"
|
|
110
110
|
},
|
|
111
111
|
"scripts": {
|
|
112
112
|
"build": "tsdown",
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
//#region src/reporting-schema.ts
|
|
3
|
-
const healthReportSchema = z.object({
|
|
4
|
-
status: z.literal("ok"),
|
|
5
|
-
service: z.string(),
|
|
6
|
-
timestamp: z.string()
|
|
7
|
-
}).strict();
|
|
8
|
-
const pluginSchema = z.object({
|
|
9
|
-
configKeys: z.array(z.string()),
|
|
10
|
-
description: z.string(),
|
|
11
|
-
displayName: z.string(),
|
|
12
|
-
name: z.string()
|
|
13
|
-
}).strict();
|
|
14
|
-
const pluginsSchema = z.array(pluginSchema);
|
|
15
|
-
const skillReportSchema = z.object({
|
|
16
|
-
name: z.string(),
|
|
17
|
-
pluginProvider: z.string().optional()
|
|
18
|
-
}).strict();
|
|
19
|
-
const skillReportsSchema = z.array(skillReportSchema);
|
|
20
|
-
const pluginPackageContentItemReportSchema = z.object({
|
|
21
|
-
dir: z.string(),
|
|
22
|
-
hasMigrationsDir: z.boolean(),
|
|
23
|
-
hasSkillsDir: z.boolean(),
|
|
24
|
-
packageName: z.string()
|
|
25
|
-
}).strict();
|
|
26
|
-
const pluginPackageContentReportSchema = z.object({
|
|
27
|
-
packageNames: z.array(z.string()),
|
|
28
|
-
packages: z.array(pluginPackageContentItemReportSchema),
|
|
29
|
-
manifestRoots: z.array(z.string()),
|
|
30
|
-
skillRoots: z.array(z.string()),
|
|
31
|
-
tracingIncludes: z.array(z.string())
|
|
32
|
-
}).strict();
|
|
33
|
-
const runtimeInfoReportSchema = z.object({
|
|
34
|
-
cwd: z.string(),
|
|
35
|
-
homeDir: z.string(),
|
|
36
|
-
descriptionText: z.string().optional(),
|
|
37
|
-
providers: z.array(z.string()),
|
|
38
|
-
skills: z.array(skillReportSchema),
|
|
39
|
-
packagedContent: pluginPackageContentReportSchema
|
|
40
|
-
}).strict();
|
|
41
|
-
const pluginOperationalToneSchema = z.enum([
|
|
42
|
-
"danger",
|
|
43
|
-
"good",
|
|
44
|
-
"neutral",
|
|
45
|
-
"warning"
|
|
46
|
-
]);
|
|
47
|
-
const pluginOperationalMetricSchema = z.object({
|
|
48
|
-
label: z.string(),
|
|
49
|
-
tone: pluginOperationalToneSchema.optional(),
|
|
50
|
-
value: z.string()
|
|
51
|
-
}).strict();
|
|
52
|
-
const pluginOperationalFieldSchema = z.object({
|
|
53
|
-
key: z.string(),
|
|
54
|
-
label: z.string()
|
|
55
|
-
}).strict();
|
|
56
|
-
const pluginOperationalRecordSchema = z.object({
|
|
57
|
-
id: z.string(),
|
|
58
|
-
tone: pluginOperationalToneSchema.optional(),
|
|
59
|
-
values: z.record(z.string(), z.string())
|
|
60
|
-
}).strict();
|
|
61
|
-
const pluginOperationalRecordSetSchema = z.object({
|
|
62
|
-
fields: z.array(pluginOperationalFieldSchema).optional(),
|
|
63
|
-
emptyText: z.string().optional(),
|
|
64
|
-
records: z.array(pluginOperationalRecordSchema).optional(),
|
|
65
|
-
title: z.string()
|
|
66
|
-
}).strict();
|
|
67
|
-
const pluginOperationalBarChartWidgetSchema = z.object({
|
|
68
|
-
categories: z.array(z.object({
|
|
69
|
-
id: z.string().min(1),
|
|
70
|
-
label: z.string().min(1),
|
|
71
|
-
values: z.record(z.string(), z.number().finite())
|
|
72
|
-
}).strict()).max(100),
|
|
73
|
-
description: z.string().optional(),
|
|
74
|
-
emptyText: z.string().optional(),
|
|
75
|
-
id: z.string().min(1),
|
|
76
|
-
series: z.array(z.object({
|
|
77
|
-
format: z.literal("usd").optional(),
|
|
78
|
-
key: z.string().min(1),
|
|
79
|
-
label: z.string().min(1),
|
|
80
|
-
tone: pluginOperationalToneSchema.optional()
|
|
81
|
-
}).strict()).min(1).max(8),
|
|
82
|
-
timeRangeDays: z.array(z.union([
|
|
83
|
-
z.literal(1),
|
|
84
|
-
z.literal(7),
|
|
85
|
-
z.literal(30),
|
|
86
|
-
z.literal(90)
|
|
87
|
-
])).min(1).max(4).optional(),
|
|
88
|
-
title: z.string().min(1),
|
|
89
|
-
type: z.literal("bar_chart")
|
|
90
|
-
}).strict();
|
|
91
|
-
const pluginOperationalReportSchema = z.object({
|
|
92
|
-
generatedAt: z.string().optional(),
|
|
93
|
-
metrics: z.array(pluginOperationalMetricSchema).optional(),
|
|
94
|
-
recordSets: z.array(pluginOperationalRecordSetSchema).optional(),
|
|
95
|
-
title: z.string().optional(),
|
|
96
|
-
pluginName: z.string(),
|
|
97
|
-
widgets: z.array(pluginOperationalBarChartWidgetSchema).max(12).optional()
|
|
98
|
-
}).strict();
|
|
99
|
-
const pluginOperationalReportFeedSchema = z.object({
|
|
100
|
-
generatedAt: z.string(),
|
|
101
|
-
reports: z.array(pluginOperationalReportSchema),
|
|
102
|
-
source: z.literal("plugins")
|
|
103
|
-
}).strict();
|
|
104
|
-
//#endregion
|
|
105
|
-
export { pluginPackageContentReportSchema as a, runtimeInfoReportSchema as c, pluginPackageContentItemReportSchema as i, skillReportSchema as l, pluginOperationalReportFeedSchema as n, pluginSchema as o, pluginOperationalReportSchema as r, pluginsSchema as s, healthReportSchema as t, skillReportsSchema as u };
|
|
@@ -1,392 +0,0 @@
|
|
|
1
|
-
import { f as logWarn } from "./logging-D2YJv_PA.js";
|
|
2
|
-
import { s as getSlackBotToken } from "./config-0_JixokJ.js";
|
|
3
|
-
import "./context-Bplcgypl.js";
|
|
4
|
-
import { K as normalizeSlackConversationId, Wt as getConversationStore } from "./agent-hooks-kjLDY-Aa.js";
|
|
5
|
-
import { c as parseSlackThreadId, ft as appendAndEnqueueExclusiveInboundMessage, pt as appendAndEnqueueInboundMessage, qt as buildDeterministicTurnId } from "./stats-BfUFTUrM.js";
|
|
6
|
-
import { t as healthReportSchema } from "./reporting-schema-DosTN__Q.js";
|
|
7
|
-
import { z } from "zod";
|
|
8
|
-
import { createHash } from "node:crypto";
|
|
9
|
-
import { createWebSource, localDestinationSchema } from "@sentry/junior-plugin-api";
|
|
10
|
-
import { del, get, put } from "@vercel/blob";
|
|
11
|
-
//#region src/handlers/health.ts
|
|
12
|
-
/** Build Junior's process health value for HTTP and reporting consumers. */
|
|
13
|
-
function readHealthReport() {
|
|
14
|
-
return healthReportSchema.parse({
|
|
15
|
-
status: "ok",
|
|
16
|
-
service: "junior",
|
|
17
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Returns a minimal JSON health response for runtime health checks.
|
|
22
|
-
*/
|
|
23
|
-
function GET() {
|
|
24
|
-
return Response.json(readHealthReport());
|
|
25
|
-
}
|
|
26
|
-
//#endregion
|
|
27
|
-
//#region src/chat/attachments/vercel.ts
|
|
28
|
-
/** Store private conversation attachments in Vercel Blob. */
|
|
29
|
-
function createVercelAttachmentStorage() {
|
|
30
|
-
return {
|
|
31
|
-
provider: "vercel-blob",
|
|
32
|
-
async put(input) {
|
|
33
|
-
await put(input.key, input.body, {
|
|
34
|
-
access: "private",
|
|
35
|
-
addRandomSuffix: false,
|
|
36
|
-
allowOverwrite: true,
|
|
37
|
-
contentType: input.contentType
|
|
38
|
-
});
|
|
39
|
-
},
|
|
40
|
-
async get(key) {
|
|
41
|
-
const result = await get(key, {
|
|
42
|
-
access: "private",
|
|
43
|
-
useCache: false
|
|
44
|
-
});
|
|
45
|
-
return result?.statusCode === 200 ? result.stream : null;
|
|
46
|
-
},
|
|
47
|
-
async delete(keys) {
|
|
48
|
-
if (keys.length === 0) return;
|
|
49
|
-
await del(keys);
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
//#endregion
|
|
54
|
-
//#region src/chat/conversations/web-mailbox.ts
|
|
55
|
-
/** Parse web mailbox metadata written before Source and Actor were complete. */
|
|
56
|
-
const legacyWebMailboxMetadataSchema = z.object({
|
|
57
|
-
authorEmail: z.string().email(),
|
|
58
|
-
authorFullName: z.string().min(1).optional(),
|
|
59
|
-
authorUserId: z.string().min(1),
|
|
60
|
-
authorUserName: z.string().min(1).optional(),
|
|
61
|
-
kind: z.literal("api_turn"),
|
|
62
|
-
messageId: z.string().min(1)
|
|
63
|
-
}).strict();
|
|
64
|
-
//#endregion
|
|
65
|
-
//#region src/chat/slack/user.ts
|
|
66
|
-
const slackUserInfoSchema = z.object({
|
|
67
|
-
ok: z.literal(true),
|
|
68
|
-
user: z.object({
|
|
69
|
-
name: z.string().nullish(),
|
|
70
|
-
profile: z.object({
|
|
71
|
-
email: z.string().nullish(),
|
|
72
|
-
real_name: z.string().nullish()
|
|
73
|
-
}).optional()
|
|
74
|
-
})
|
|
75
|
-
});
|
|
76
|
-
const USER_CACHE_TTL_MS = 3e5;
|
|
77
|
-
const userCache = /* @__PURE__ */ new Map();
|
|
78
|
-
function userCacheKey(teamId, userId) {
|
|
79
|
-
return `${teamId}:${userId}`;
|
|
80
|
-
}
|
|
81
|
-
function readFromCache(teamId, userId) {
|
|
82
|
-
const hit = userCache.get(userCacheKey(teamId, userId));
|
|
83
|
-
if (!hit) return null;
|
|
84
|
-
if (hit.expiresAt < Date.now()) {
|
|
85
|
-
userCache.delete(userCacheKey(teamId, userId));
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
return hit.value;
|
|
89
|
-
}
|
|
90
|
-
function writeToCache(teamId, userId, value) {
|
|
91
|
-
userCache.set(userCacheKey(teamId, userId), {
|
|
92
|
-
value,
|
|
93
|
-
expiresAt: Date.now() + USER_CACHE_TTL_MS
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
/** Fetch Slack user profile info with in-memory TTL cache to avoid repeated API calls. */
|
|
97
|
-
async function lookupSlackUser(teamId, userId) {
|
|
98
|
-
if (!teamId || !userId) return null;
|
|
99
|
-
const cached = readFromCache(teamId, userId);
|
|
100
|
-
if (cached) return cached;
|
|
101
|
-
const token = getSlackBotToken();
|
|
102
|
-
if (!token) return null;
|
|
103
|
-
try {
|
|
104
|
-
const response = await fetch(`https://slack.com/api/users.info?user=${encodeURIComponent(userId)}`, { headers: { authorization: `Bearer ${token}` } });
|
|
105
|
-
if (!response.ok) {
|
|
106
|
-
logWarn("slack.user.lookup.failed", {
|
|
107
|
-
"enduser.id": userId,
|
|
108
|
-
"app.slack.team_id": teamId,
|
|
109
|
-
"http.response.status_code": response.status
|
|
110
|
-
});
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
const parsed = slackUserInfoSchema.safeParse(await response.json());
|
|
114
|
-
if (!parsed.success) return null;
|
|
115
|
-
const payload = parsed.data;
|
|
116
|
-
const result = {
|
|
117
|
-
userName: payload.user.name?.trim() || void 0,
|
|
118
|
-
fullName: payload.user.profile?.real_name?.trim() || void 0,
|
|
119
|
-
email: payload.user.profile?.email?.trim() || void 0
|
|
120
|
-
};
|
|
121
|
-
writeToCache(teamId, userId, result);
|
|
122
|
-
return result;
|
|
123
|
-
} catch (error) {
|
|
124
|
-
logWarn("slack.user.lookup.failed", {
|
|
125
|
-
"enduser.id": userId,
|
|
126
|
-
"app.slack.team_id": teamId,
|
|
127
|
-
"exception.message": error instanceof Error ? error.message : String(error)
|
|
128
|
-
});
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
//#endregion
|
|
133
|
-
//#region src/chat/slack/conversation-context.ts
|
|
134
|
-
function normalizeConversationName(type, channelName) {
|
|
135
|
-
const trimmed = channelName?.trim();
|
|
136
|
-
if (!trimmed) return void 0;
|
|
137
|
-
if (type === "public_channel" || type === "private_channel" || type === "private_channel_or_group_dm") return trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
|
|
138
|
-
return trimmed;
|
|
139
|
-
}
|
|
140
|
-
function typeFromSlackChannelType(channelType) {
|
|
141
|
-
if (channelType === "channel") return "public_channel";
|
|
142
|
-
if (channelType === "group") return "private_channel";
|
|
143
|
-
if (channelType === "mpim") return "group_dm";
|
|
144
|
-
if (channelType === "im") return "direct_message";
|
|
145
|
-
}
|
|
146
|
-
function typeFromChannelId(channelId, channelName) {
|
|
147
|
-
const normalized = normalizeSlackConversationId(channelId);
|
|
148
|
-
if (!normalized) return void 0;
|
|
149
|
-
if (normalized.startsWith("C")) return "public_channel";
|
|
150
|
-
if (normalized.startsWith("D")) return "direct_message";
|
|
151
|
-
if (normalized.startsWith("G")) return channelName?.trim().startsWith("mpdm-") ? "group_dm" : "private_channel_or_group_dm";
|
|
152
|
-
}
|
|
153
|
-
function toSlackEventChannelType(channelType) {
|
|
154
|
-
if (channelType === "channel" || channelType === "group" || channelType === "mpim" || channelType === "im") return channelType;
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Map Slack's Events API channel_type to a source-confirmed visibility.
|
|
158
|
-
*
|
|
159
|
-
* Slack's event metadata is the only visibility authority; channel IDs never
|
|
160
|
-
* classify visibility.
|
|
161
|
-
*/
|
|
162
|
-
function conversationVisibilityFromSlackChannelType(channelType) {
|
|
163
|
-
if (!channelType) return void 0;
|
|
164
|
-
return channelType === "channel" ? "public" : "private";
|
|
165
|
-
}
|
|
166
|
-
/** Resolve Slack's raw event channel type from a Chat SDK message-like object. */
|
|
167
|
-
function resolveSlackChannelTypeFromMessage(message) {
|
|
168
|
-
const raw = message.raw;
|
|
169
|
-
if (!raw || typeof raw !== "object") return;
|
|
170
|
-
const channelType = raw.channel_type;
|
|
171
|
-
return typeof channelType === "string" ? toSlackEventChannelType(channelType.trim()) : void 0;
|
|
172
|
-
}
|
|
173
|
-
/** Build Slack conversation facts available to runtime consumers. */
|
|
174
|
-
function resolveSlackConversationContext(input) {
|
|
175
|
-
const type = typeFromSlackChannelType(input.channelType) ?? typeFromChannelId(input.channelId, input.channelName);
|
|
176
|
-
if (!type) return void 0;
|
|
177
|
-
const name = normalizeConversationName(type, input.channelName);
|
|
178
|
-
const visibility = conversationVisibilityFromSlackChannelType(input.channelType);
|
|
179
|
-
return {
|
|
180
|
-
type,
|
|
181
|
-
...name ? { name } : void 0,
|
|
182
|
-
...visibility ? { visibility } : void 0
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
/** Build Slack conversation facts from Junior's persisted Slack thread id. */
|
|
186
|
-
function resolveSlackConversationContextFromThreadId(input) {
|
|
187
|
-
const context = resolveSlackConversationContext({
|
|
188
|
-
channelId: parseSlackThreadId(input.threadId)?.channelId,
|
|
189
|
-
channelName: input.channelName
|
|
190
|
-
});
|
|
191
|
-
if (!context || !input.visibility) return context;
|
|
192
|
-
return {
|
|
193
|
-
...context,
|
|
194
|
-
type: input.visibility === "private" && context.type === "public_channel" ? "private_channel" : context.type,
|
|
195
|
-
visibility: input.visibility
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
/** Render a human label for a privacy-preserving Slack conversation type. */
|
|
199
|
-
function formatSlackConversationTypeLabel(type) {
|
|
200
|
-
if (type === "public_channel") return "Public Channel";
|
|
201
|
-
if (type === "private_channel") return "Private Channel";
|
|
202
|
-
if (type === "group_dm") return "Group DM";
|
|
203
|
-
if (type === "direct_message") return "Direct Message";
|
|
204
|
-
return "Private Channel or Group DM";
|
|
205
|
-
}
|
|
206
|
-
/** Render a Slack conversation label without exposing conversation names. */
|
|
207
|
-
function formatSlackConversationRedactedLabel(context) {
|
|
208
|
-
if (!context) return void 0;
|
|
209
|
-
return formatSlackConversationTypeLabel(context.type);
|
|
210
|
-
}
|
|
211
|
-
//#endregion
|
|
212
|
-
//#region src/chat/conversations/destination.ts
|
|
213
|
-
function requireLocalDestination(conversationId) {
|
|
214
|
-
const parsed = localDestinationSchema.safeParse({
|
|
215
|
-
platform: "local",
|
|
216
|
-
conversationId
|
|
217
|
-
});
|
|
218
|
-
if (!parsed.success) throw new Error(`Invalid local conversation id: ${conversationId}`);
|
|
219
|
-
return parsed.data;
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Keep a stored Destination, or create the temporary local Destination.
|
|
223
|
-
*
|
|
224
|
-
* TODO(dcramer): Delete this module after Conversation and AgentRun use
|
|
225
|
-
* Location, and web and event Turns no longer need a local
|
|
226
|
-
* Destination.
|
|
227
|
-
*/
|
|
228
|
-
function resolveConversationDestination(args) {
|
|
229
|
-
return args.existing ?? requireLocalDestination(args.conversationId);
|
|
230
|
-
}
|
|
231
|
-
//#endregion
|
|
232
|
-
//#region src/chat/conversations/web-input.ts
|
|
233
|
-
/** Store web input in a Conversation mailbox. */
|
|
234
|
-
function normalizeEmail(email) {
|
|
235
|
-
return email.trim().toLowerCase();
|
|
236
|
-
}
|
|
237
|
-
function stableHex(...parts) {
|
|
238
|
-
return createHash("sha256").update(parts.join("\0")).digest("hex").slice(0, 24);
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Build a durable Conversation id for one web Actor and create key.
|
|
242
|
-
*
|
|
243
|
-
* Retries with the same key must address the same Conversation before the
|
|
244
|
-
* mailbox Message id is derived.
|
|
245
|
-
*/
|
|
246
|
-
function createConversationId(args) {
|
|
247
|
-
return `local:web:${stableHex(normalizeEmail(args.actorEmail), args.idempotencyKey)}`;
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Build the retry-stable id for one web Message.
|
|
251
|
-
*
|
|
252
|
-
* TODO(dcramer): Replace the `api-msg` prefix after deployed request retries
|
|
253
|
-
* no longer need to derive ids written by the old web input code.
|
|
254
|
-
*/
|
|
255
|
-
function webMessageId(args) {
|
|
256
|
-
return `api-msg:${stableHex(args.conversationId, args.idempotencyKey)}`;
|
|
257
|
-
}
|
|
258
|
-
/** Return the stable Turn id for one mailbox Message. */
|
|
259
|
-
function conversationTurnIdForMessage(messageId) {
|
|
260
|
-
return buildDeterministicTurnId(messageId);
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Store the dashboard participant in the current Conversation actor field.
|
|
264
|
-
*
|
|
265
|
-
* TODO(dcramer): Remove this StoredSlackActor conversion after Conversation
|
|
266
|
-
* actor storage accepts web Actors directly.
|
|
267
|
-
*/
|
|
268
|
-
function storedActorFromWeb(actor) {
|
|
269
|
-
return {
|
|
270
|
-
...actor.email ? { email: normalizeEmail(actor.email) } : void 0,
|
|
271
|
-
...actor.fullName ? { fullName: actor.fullName } : void 0
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
/** Rebuild the web Actor from durable Conversation identity. */
|
|
275
|
-
function webActorFromEmail(email, profile) {
|
|
276
|
-
const normalized = normalizeEmail(email);
|
|
277
|
-
return {
|
|
278
|
-
platform: "web",
|
|
279
|
-
userId: `dashboard:${stableHex(normalized)}`,
|
|
280
|
-
email: normalized,
|
|
281
|
-
...profile?.fullName ? { fullName: profile.fullName } : void 0,
|
|
282
|
-
...profile?.userName ? { userName: profile.userName } : void 0
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
/** Build one web mailbox entry. */
|
|
286
|
-
function buildWebInboundMessage(args) {
|
|
287
|
-
const text = args.message.trim();
|
|
288
|
-
if (!text) throw new Error("Web Message must not be empty");
|
|
289
|
-
if (!args.actor.email) throw new Error("Web Actor requires a verified email");
|
|
290
|
-
const destination = resolveConversationDestination({
|
|
291
|
-
conversationId: args.conversationId,
|
|
292
|
-
existing: args.destination
|
|
293
|
-
});
|
|
294
|
-
const nowMs = args.nowMs ?? Date.now();
|
|
295
|
-
return {
|
|
296
|
-
conversationId: args.conversationId,
|
|
297
|
-
createdAtMs: args.createdAtMs ?? nowMs,
|
|
298
|
-
delivery: "defer",
|
|
299
|
-
destination,
|
|
300
|
-
inboundMessageId: args.messageId,
|
|
301
|
-
input: {
|
|
302
|
-
authorId: args.actor.userId,
|
|
303
|
-
text,
|
|
304
|
-
metadata: {
|
|
305
|
-
authorEmail: normalizeEmail(args.actor.email),
|
|
306
|
-
...args.actor.fullName && { authorFullName: args.actor.fullName },
|
|
307
|
-
authorUserId: args.actor.userId,
|
|
308
|
-
...args.actor.userName && { authorUserName: args.actor.userName },
|
|
309
|
-
kind: "api_turn",
|
|
310
|
-
messageId: args.messageId
|
|
311
|
-
}
|
|
312
|
-
},
|
|
313
|
-
receivedAtMs: nowMs,
|
|
314
|
-
source: "web"
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
/** Record web activity and create a Conversation root when needed. */
|
|
318
|
-
async function recordWebConversationActivity(args) {
|
|
319
|
-
const store = args.conversationStore ?? getConversationStore();
|
|
320
|
-
const existing = await store.get({ conversationId: args.conversationId });
|
|
321
|
-
const destination = resolveConversationDestination({
|
|
322
|
-
conversationId: args.conversationId,
|
|
323
|
-
existing: existing?.destination
|
|
324
|
-
});
|
|
325
|
-
const isNewRoot = !existing;
|
|
326
|
-
const visibility = args.rootVisibility === "private" ? "private" : "public";
|
|
327
|
-
const source = isNewRoot ? createWebSource(args.conversationId, visibility) : void 0;
|
|
328
|
-
await store.recordActivity({
|
|
329
|
-
conversationId: args.conversationId,
|
|
330
|
-
destination,
|
|
331
|
-
nowMs: args.nowMs,
|
|
332
|
-
actor: storedActorFromWeb(args.actor),
|
|
333
|
-
...isNewRoot ? { source: "web" } : void 0,
|
|
334
|
-
...source ? { sessionSource: source } : void 0,
|
|
335
|
-
...isNewRoot ? { visibility } : void 0
|
|
336
|
-
});
|
|
337
|
-
return destination;
|
|
338
|
-
}
|
|
339
|
-
/** Create a Conversation from web input and enqueue its first Message. */
|
|
340
|
-
async function createAndEnqueueConversation(input, options) {
|
|
341
|
-
if (!input.actor.email) throw new Error("Web Actor requires a verified email");
|
|
342
|
-
const conversationId = createConversationId({
|
|
343
|
-
actorEmail: input.actor.email,
|
|
344
|
-
idempotencyKey: input.idempotencyKey
|
|
345
|
-
});
|
|
346
|
-
return await appendAndEnqueueWebMessage({
|
|
347
|
-
actor: input.actor,
|
|
348
|
-
conversationId,
|
|
349
|
-
idempotencyKey: input.idempotencyKey,
|
|
350
|
-
message: input.message,
|
|
351
|
-
rootVisibility: input.visibility === "private" ? "private" : "public"
|
|
352
|
-
}, options);
|
|
353
|
-
}
|
|
354
|
-
async function appendAndEnqueueWebMessage(input, options) {
|
|
355
|
-
const text = input.message.trim();
|
|
356
|
-
if (!text) throw new Error("Web Message must not be empty");
|
|
357
|
-
if (!input.actor.email) throw new Error("Web Actor requires a verified email");
|
|
358
|
-
const nowMs = options.nowMs ?? Date.now();
|
|
359
|
-
const messageId = webMessageId({
|
|
360
|
-
conversationId: input.conversationId,
|
|
361
|
-
idempotencyKey: input.idempotencyKey
|
|
362
|
-
});
|
|
363
|
-
const destination = await recordWebConversationActivity({
|
|
364
|
-
actor: input.actor,
|
|
365
|
-
conversationId: input.conversationId,
|
|
366
|
-
conversationStore: options.conversationStore,
|
|
367
|
-
nowMs,
|
|
368
|
-
...input.rootVisibility && { rootVisibility: input.rootVisibility }
|
|
369
|
-
});
|
|
370
|
-
const result = await (options.exclusive ? appendAndEnqueueExclusiveInboundMessage : appendAndEnqueueInboundMessage)({
|
|
371
|
-
message: buildWebInboundMessage({
|
|
372
|
-
actor: input.actor,
|
|
373
|
-
conversationId: input.conversationId,
|
|
374
|
-
destination,
|
|
375
|
-
message: text,
|
|
376
|
-
messageId,
|
|
377
|
-
nowMs
|
|
378
|
-
}),
|
|
379
|
-
conversationStore: options.conversationStore,
|
|
380
|
-
nowMs,
|
|
381
|
-
queue: options.queue,
|
|
382
|
-
state: options.state
|
|
383
|
-
});
|
|
384
|
-
const status = result.status === "appended" ? "accepted" : result.status;
|
|
385
|
-
return {
|
|
386
|
-
conversationId: input.conversationId,
|
|
387
|
-
messageId,
|
|
388
|
-
status
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
//#endregion
|
|
392
|
-
export { webActorFromEmail as a, formatSlackConversationRedactedLabel as c, resolveSlackConversationContextFromThreadId as d, lookupSlackUser as f, readHealthReport as g, GET as h, recordWebConversationActivity as i, resolveSlackChannelTypeFromMessage as l, createVercelAttachmentStorage as m, conversationTurnIdForMessage as n, resolveConversationDestination as o, legacyWebMailboxMetadataSchema as p, createAndEnqueueConversation as r, conversationVisibilityFromSlackChannelType as s, appendAndEnqueueWebMessage as t, resolveSlackConversationContext as u };
|