@sentry/junior 0.147.0 → 0.149.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-hooks-HS6VGUPM.js → agent-hooks-CWMCXAMT.js} +10 -10
- package/dist/api/conversations/access.d.ts +2 -1
- package/dist/api/conversations/aggregate.d.ts +2 -0
- package/dist/api/conversations/archive.d.ts +3 -5
- package/dist/api/conversations/create.d.ts +6 -0
- package/dist/api/conversations/detail.d.ts +2 -296
- package/dist/api/conversations/event-list.d.ts +2 -193
- package/dist/api/conversations/list.d.ts +4 -66
- package/dist/api/conversations/pending-messages.d.ts +15 -0
- package/dist/api/conversations/routes.d.ts +1 -1
- package/dist/api/conversations/stats.d.ts +0 -54
- package/dist/api/http.d.ts +2 -6
- package/dist/api/locations/detail.d.ts +3 -111
- package/dist/api/locations/list.d.ts +0 -45
- package/dist/api/locations/query.d.ts +2 -1
- package/dist/api/locations/routes.d.ts +1 -1
- package/dist/api/people/list.d.ts +0 -29
- package/dist/api/people/profile.d.ts +2 -106
- package/dist/api/people/profile.query.d.ts +2 -1
- package/dist/api/people/routes.d.ts +1 -1
- package/dist/api/people/shared.d.ts +3 -1
- package/dist/api/people/spend.d.ts +3 -4
- package/dist/api/personal-tokens/routes.d.ts +2 -2
- package/dist/api/route.d.ts +8 -14
- package/dist/api/schema/conversation.d.ts +130 -25
- package/dist/api/schema/location.d.ts +4 -0
- package/dist/api/schema/person.d.ts +7 -3
- package/dist/api/schema.d.ts +2 -2
- package/dist/api/schema.js +13 -1
- package/dist/api/stats.d.ts +0 -13
- package/dist/api/validation.d.ts +7 -0
- package/dist/api/viewer.d.ts +3 -0
- package/dist/api.d.ts +2 -1
- package/dist/api.js +772 -561
- package/dist/app.js +241 -204
- package/dist/{catalog-runtime-M6ZTPYRV.js → catalog-runtime-GAW5APVI.js} +5 -5
- package/dist/chat/actor.d.ts +4 -1
- package/dist/chat/agent/request.d.ts +2 -0
- package/dist/chat/agent/resume.d.ts +1 -0
- package/dist/chat/agent-dispatch/context.d.ts +2 -2
- package/dist/chat/agent-invocations/types.d.ts +52 -0
- package/dist/chat/api-turns/work.d.ts +108 -0
- package/dist/chat/app/services.d.ts +1 -1
- package/dist/chat/conversations/history.d.ts +60 -0
- package/dist/chat/conversations/provenance.d.ts +6 -0
- package/dist/chat/conversations/sql/actor-identity.d.ts +19 -0
- package/dist/chat/conversations/store.d.ts +1 -1
- package/dist/chat/resource-events/store.d.ts +1 -1
- package/dist/chat/runtime/reply-executor.d.ts +1 -3
- package/dist/chat/runtime/slack-runtime.d.ts +2 -1
- package/dist/chat/runtime/turn-preparation.d.ts +9 -0
- package/dist/chat/scheduled-tasks/tool-support.d.ts +5 -5
- package/dist/chat/scheduled-tasks/tools/create.d.ts +2 -2
- package/dist/chat/scheduled-tasks/tools/delete.d.ts +2 -2
- package/dist/chat/scheduled-tasks/tools/list.d.ts +2 -2
- package/dist/chat/scheduled-tasks/tools/run-now.d.ts +2 -2
- package/dist/chat/scheduled-tasks/tools/update.d.ts +2 -2
- package/dist/chat/services/conversation-memory.d.ts +2 -2
- package/dist/chat/services/conversation-title.d.ts +20 -0
- package/dist/chat/slack/assistant-thread/title.d.ts +10 -27
- package/dist/chat/slack/channel.d.ts +26 -0
- package/dist/chat/slack/dispatch-turn.d.ts +1 -0
- package/dist/chat/slack/timestamp-param.d.ts +0 -2
- package/dist/chat/slack/{tools → tool-support}/canvas/api.d.ts +1 -1
- package/dist/chat/slack/{tools → tool-support}/canvas/context.d.ts +3 -0
- package/dist/chat/slack/tool-support/channel-access.d.ts +42 -0
- package/dist/chat/slack/tool-support/channel-target.d.ts +27 -0
- package/dist/chat/slack/tool-support/list/params.d.ts +5 -0
- package/dist/chat/slack/tools/canvas/create.d.ts +1 -1
- package/dist/chat/slack/tools/channel-join.d.ts +28 -0
- package/dist/chat/slack/tools/channel-list-messages.d.ts +3 -2
- package/dist/chat/slack/tools/conversation-message-search.d.ts +2 -8
- package/dist/chat/slack/tools/message-add-reaction.d.ts +1 -1
- package/dist/chat/slack/tools/public-search.d.ts +63 -2
- package/dist/chat/slack/tools/send-files.d.ts +1 -1
- package/dist/chat/slack/tools/thread-read.d.ts +2 -5
- package/dist/chat/source.d.ts +2 -0
- package/dist/chat/state/conversation.d.ts +1 -0
- package/dist/chat/task-execution/checkpoint.d.ts +1 -0
- package/dist/chat/task-execution/state.d.ts +9 -4
- package/dist/chat/task-execution/store.d.ts +3 -2
- package/dist/chat/task-execution/turn-cursor.d.ts +2 -0
- package/dist/chat/task-execution/worker.d.ts +2 -0
- package/dist/chat/tasks/read.d.ts +1 -1
- package/dist/chat/tools/search-conversation-events.d.ts +1 -1
- package/dist/chat/tools/types.d.ts +11 -4
- package/dist/chat/tools/watch-resource-events.d.ts +2 -2
- package/dist/{chunk-ORO3PWKQ.js → chunk-25SC7D6Z.js} +1 -1
- package/dist/{chunk-GGB4WL5A.js → chunk-2JHKISQA.js} +1 -1
- package/dist/{chunk-AVZDSUFB.js → chunk-2P6BQQER.js} +4 -4
- package/dist/{chunk-7NVJVJID.js → chunk-32CXPIZE.js} +2 -2
- package/dist/{chunk-YSB2N563.js → chunk-3GYZ5YEH.js} +121 -87
- package/dist/{chunk-DU74WPIC.js → chunk-3I2IVWSZ.js} +1 -1
- package/dist/{chunk-SVHUEHUL.js → chunk-3JVUWLR7.js} +65 -4
- package/dist/{chunk-RXBVB2JM.js → chunk-3KGQOKIG.js} +1 -1
- package/dist/{chunk-YBVXBNSV.js → chunk-4JGM7FXO.js} +2 -2
- package/dist/{chunk-AZ7EAOBE.js → chunk-4YKPOYRV.js} +5 -5
- package/dist/chunk-5CWUQXXT.js +806 -0
- package/dist/{chunk-DLDRQHG5.js → chunk-6G5RCDKP.js} +1 -1
- package/dist/{chunk-AYU72WO7.js → chunk-75DSYFFB.js} +47 -70
- package/dist/{chunk-YHDERDRO.js → chunk-7BH46NB4.js} +91 -75
- package/dist/{chunk-745QJ3ZA.js → chunk-ALJG73MQ.js} +2 -2
- package/dist/chunk-ART7P442.js +48 -0
- package/dist/{chunk-7A4M7PCV.js → chunk-DK5UT6PQ.js} +1180 -556
- package/dist/{chunk-AIOK6LAG.js → chunk-EW2LNCRL.js} +813 -44
- package/dist/chunk-EXW76GW4.js +36 -0
- package/dist/chunk-GDFF3BQG.js +136 -0
- package/dist/{chunk-7UQIVT6G.js → chunk-JMH3DBM5.js} +6 -6
- package/dist/{chunk-CWP5RHVX.js → chunk-JMSMXXED.js} +12 -5
- package/dist/{chunk-CSZINQJ2.js → chunk-MEMURSBG.js} +1 -1
- package/dist/{chunk-YNTTROJT.js → chunk-QIW7OQEO.js} +1 -1
- package/dist/{chunk-Q4R73I43.js → chunk-RB26T3LU.js} +58 -28
- package/dist/{chunk-4LJO56OJ.js → chunk-RLVZIWNQ.js} +1 -1
- package/dist/{chunk-SGZO7UWH.js → chunk-SWHPVZBV.js} +2 -2
- package/dist/{chunk-PVNG6UMU.js → chunk-TFBNBXPU.js} +22 -10
- package/dist/{chunk-EFLJ2WJH.js → chunk-URDCZAL4.js} +5 -5
- package/dist/{chunk-6H5SZR24.js → chunk-UZGASGOD.js} +300 -383
- package/dist/{chunk-YEO3VX72.js → chunk-W7UONUIZ.js} +8 -6
- package/dist/{chunk-JUUKWRBE.js → chunk-X22XXSZA.js} +1 -1
- package/dist/{chunk-X5UY34KT.js → chunk-XPYWWMHR.js} +8 -8
- package/dist/{chunk-IIOOO4FQ.js → chunk-ZYMYLLWG.js} +2 -1
- package/dist/cli/chat.js +32 -35
- package/dist/cli/check.js +6 -6
- package/dist/cli/plugins.js +13 -13
- package/dist/cli/snapshot-create.js +9 -9
- package/dist/cli/upgrade.js +7 -7
- package/dist/db/schema/agent-invocations.d.ts +20 -0
- package/dist/db/schema/conversation-annotations.d.ts +2 -2
- package/dist/db/schema.d.ts +22 -2
- package/dist/{db-CSEW4RII.js → db-JBZLRIVY.js} +7 -7
- package/dist/instrumentation.js +4 -4
- package/dist/nitro.js +2 -2
- package/dist/{oauth-callback-server-FUEV5HNG.js → oauth-callback-server-MNTVYY2I.js} +24 -26
- package/dist/{runner-U3XHSKSH.js → runner-K24BWZSZ.js} +23 -25
- package/dist/{skills-AKE6W3WI.js → skills-AU5ST2CG.js} +6 -6
- package/dist/spawn-6PO7CVJ6.js +38 -0
- package/dist/{validation-WDTAULUF.js → validation-IXGZCFRO.js} +6 -6
- package/dist/version.js +1 -1
- package/dist/{work-C3HAO2BG.js → work-WCZIYILX.js} +18 -19
- package/dist/{worker-LNP4YKBM.js → worker-U2AACBEY.js} +11 -10
- package/package.json +6 -5
- package/dist/api/health.d.ts +0 -7
- package/dist/api/personal-tokens/create.d.ts +0 -11
- package/dist/api/personal-tokens/list.d.ts +0 -12
- package/dist/api/personal-tokens/revoke.d.ts +0 -5
- package/dist/api/plugin-reports.d.ts +0 -63
- package/dist/api/plugins.d.ts +0 -8
- package/dist/api/runtime.d.ts +0 -24
- package/dist/api/skills.d.ts +0 -6
- package/dist/chat/slack/tools/channel-access.d.ts +0 -30
- package/dist/chunk-34GS7B6L.js +0 -115
- package/dist/chunk-AVOCH2Z5.js +0 -285
- package/dist/chunk-HLXXQI5Q.js +0 -238
- package/dist/chunk-K4SZAP3K.js +0 -67
- package/dist/chunk-R776HKPK.js +0 -81
- package/dist/chunk-Z4YU7Z4L.js +0 -76
- package/dist/spawn-B6GDTWJ4.js +0 -39
- /package/dist/chat/slack/{tools → tool-support}/canvas/markdown.d.ts +0 -0
- /package/dist/chat/slack/{tools → tool-support}/channel-capabilities.d.ts +0 -0
- /package/dist/chat/slack/{tools → tool-support}/context.d.ts +0 -0
- /package/dist/chat/slack/{tools → tool-support}/list/api.d.ts +0 -0
- /package/dist/chat/slack/{tools → tool-support}/slack-message-url.d.ts +0 -0
|
@@ -0,0 +1,806 @@
|
|
|
1
|
+
import {
|
|
2
|
+
appendAndEnqueueInboundMessage
|
|
3
|
+
} from "./chunk-JMSMXXED.js";
|
|
4
|
+
import {
|
|
5
|
+
buildDeliveredTurnStatePatch,
|
|
6
|
+
finalizeFailedTurnReplyWithEvent,
|
|
7
|
+
getTurnUserMessage,
|
|
8
|
+
processPluginTask,
|
|
9
|
+
scheduleSessionCompletedPluginTasks
|
|
10
|
+
} from "./chunk-UZGASGOD.js";
|
|
11
|
+
import {
|
|
12
|
+
ConversationTurnLifecycleService,
|
|
13
|
+
coerceThreadConversationState,
|
|
14
|
+
getPersistedSandboxState,
|
|
15
|
+
getPersistedThreadState,
|
|
16
|
+
persistThreadStateById
|
|
17
|
+
} from "./chunk-4YKPOYRV.js";
|
|
18
|
+
import {
|
|
19
|
+
healthReportSchema
|
|
20
|
+
} from "./chunk-UTL3MHPC.js";
|
|
21
|
+
import {
|
|
22
|
+
parseSlackThreadId
|
|
23
|
+
} from "./chunk-MEMURSBG.js";
|
|
24
|
+
import {
|
|
25
|
+
TurnInputCommitLostError,
|
|
26
|
+
buildConversationContext,
|
|
27
|
+
buildDeterministicTurnId,
|
|
28
|
+
getAssistantReplyText,
|
|
29
|
+
getConversationTurnBoundaryError,
|
|
30
|
+
getTurnRecord,
|
|
31
|
+
hydrateConversationMessages,
|
|
32
|
+
isTurnInputCommitLostError,
|
|
33
|
+
listTurnSummaries,
|
|
34
|
+
loadProjection,
|
|
35
|
+
markConversationMessage,
|
|
36
|
+
markTurnFailed,
|
|
37
|
+
normalizeConversationText,
|
|
38
|
+
persistConversationMessages,
|
|
39
|
+
persistWithRetry,
|
|
40
|
+
recordDeliveredAssistantMessage,
|
|
41
|
+
saveTurnCheckpoint,
|
|
42
|
+
startActiveTurn,
|
|
43
|
+
upsertConversationMessage
|
|
44
|
+
} from "./chunk-EW2LNCRL.js";
|
|
45
|
+
import {
|
|
46
|
+
normalizeSlackConversationId
|
|
47
|
+
} from "./chunk-SWHPVZBV.js";
|
|
48
|
+
import {
|
|
49
|
+
getConversationEventStore,
|
|
50
|
+
getConversationStore
|
|
51
|
+
} from "./chunk-3GYZ5YEH.js";
|
|
52
|
+
import {
|
|
53
|
+
credentialContextForActor
|
|
54
|
+
} from "./chunk-RUIXF3YQ.js";
|
|
55
|
+
import {
|
|
56
|
+
logException,
|
|
57
|
+
setTags,
|
|
58
|
+
withLogContext
|
|
59
|
+
} from "./chunk-RLVZIWNQ.js";
|
|
60
|
+
|
|
61
|
+
// src/handlers/health.ts
|
|
62
|
+
function readHealthReport() {
|
|
63
|
+
return healthReportSchema.parse({
|
|
64
|
+
status: "ok",
|
|
65
|
+
service: "junior",
|
|
66
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function GET() {
|
|
70
|
+
return Response.json(readHealthReport());
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/chat/api-turns/work.ts
|
|
74
|
+
import { createHash } from "crypto";
|
|
75
|
+
import { z } from "zod";
|
|
76
|
+
import {
|
|
77
|
+
createWebSource,
|
|
78
|
+
localDestinationSchema
|
|
79
|
+
} from "@sentry/junior-plugin-api";
|
|
80
|
+
var apiTurnMailboxMetadataSchema = z.object({
|
|
81
|
+
authorEmail: z.string().email(),
|
|
82
|
+
authorFullName: z.string().min(1).optional(),
|
|
83
|
+
authorUserId: z.string().min(1),
|
|
84
|
+
authorUserName: z.string().min(1).optional(),
|
|
85
|
+
kind: z.literal("api_turn"),
|
|
86
|
+
messageId: z.string().min(1)
|
|
87
|
+
}).strict();
|
|
88
|
+
function normalizeEmail(email) {
|
|
89
|
+
return email.trim().toLowerCase();
|
|
90
|
+
}
|
|
91
|
+
function stableHex(...parts) {
|
|
92
|
+
return createHash("sha256").update(parts.join("\0")).digest("hex").slice(0, 24);
|
|
93
|
+
}
|
|
94
|
+
function createApiConversationId(args) {
|
|
95
|
+
return `local:web:${stableHex(
|
|
96
|
+
normalizeEmail(args.actorEmail),
|
|
97
|
+
args.idempotencyKey
|
|
98
|
+
)}`;
|
|
99
|
+
}
|
|
100
|
+
function apiMessageId(args) {
|
|
101
|
+
return `api-msg:${stableHex(args.conversationId, args.idempotencyKey)}`;
|
|
102
|
+
}
|
|
103
|
+
function apiTurnIdForMessage(messageId) {
|
|
104
|
+
return buildDeterministicTurnId(messageId);
|
|
105
|
+
}
|
|
106
|
+
function requireLocalDestination(conversationId) {
|
|
107
|
+
const parsed = localDestinationSchema.safeParse({
|
|
108
|
+
platform: "local",
|
|
109
|
+
conversationId
|
|
110
|
+
});
|
|
111
|
+
if (!parsed.success) {
|
|
112
|
+
throw new Error(`Invalid local conversation id: ${conversationId}`);
|
|
113
|
+
}
|
|
114
|
+
return parsed.data;
|
|
115
|
+
}
|
|
116
|
+
function resolveApiTurnDestination(args) {
|
|
117
|
+
if (args.existing) {
|
|
118
|
+
return args.existing;
|
|
119
|
+
}
|
|
120
|
+
return requireLocalDestination(args.conversationId);
|
|
121
|
+
}
|
|
122
|
+
function webSourceForConversation(args) {
|
|
123
|
+
return createWebSource(
|
|
124
|
+
args.conversationId,
|
|
125
|
+
args.visibility === "private" ? "private" : "public"
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
function actorFromMetadata(metadata) {
|
|
129
|
+
return {
|
|
130
|
+
platform: "web",
|
|
131
|
+
userId: metadata.authorUserId,
|
|
132
|
+
email: normalizeEmail(metadata.authorEmail),
|
|
133
|
+
...metadata.authorFullName ? { fullName: metadata.authorFullName } : {},
|
|
134
|
+
...metadata.authorUserName ? { userName: metadata.authorUserName } : {}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function storedActorFromApi(actor) {
|
|
138
|
+
return {
|
|
139
|
+
...actor.email ? { email: normalizeEmail(actor.email) } : {},
|
|
140
|
+
...actor.fullName ? { fullName: actor.fullName } : {}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function webActorFromEmail(email, profile) {
|
|
144
|
+
const normalized = normalizeEmail(email);
|
|
145
|
+
return {
|
|
146
|
+
platform: "web",
|
|
147
|
+
userId: `dashboard:${stableHex(normalized)}`,
|
|
148
|
+
email: normalized,
|
|
149
|
+
...profile?.fullName ? { fullName: profile.fullName } : {},
|
|
150
|
+
...profile?.userName ? { userName: profile.userName } : {}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function actorFromStoredConversation(stored) {
|
|
154
|
+
const email = stored?.email?.trim().toLowerCase();
|
|
155
|
+
if (!email) {
|
|
156
|
+
return void 0;
|
|
157
|
+
}
|
|
158
|
+
return webActorFromEmail(
|
|
159
|
+
email,
|
|
160
|
+
stored?.fullName ? { fullName: stored.fullName } : void 0
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
function buildApiTurnInboundMessage(args) {
|
|
164
|
+
const text = args.message.trim();
|
|
165
|
+
if (!text) {
|
|
166
|
+
throw new Error("API conversation message must not be empty");
|
|
167
|
+
}
|
|
168
|
+
if (!args.actor.email) {
|
|
169
|
+
throw new Error("API conversation actor requires a verified email");
|
|
170
|
+
}
|
|
171
|
+
const destination = resolveApiTurnDestination({
|
|
172
|
+
conversationId: args.conversationId,
|
|
173
|
+
existing: args.destination
|
|
174
|
+
});
|
|
175
|
+
const nowMs = args.nowMs ?? Date.now();
|
|
176
|
+
return {
|
|
177
|
+
conversationId: args.conversationId,
|
|
178
|
+
createdAtMs: args.createdAtMs ?? nowMs,
|
|
179
|
+
delivery: "defer",
|
|
180
|
+
destination,
|
|
181
|
+
inboundMessageId: args.messageId,
|
|
182
|
+
input: {
|
|
183
|
+
authorId: args.actor.userId,
|
|
184
|
+
text,
|
|
185
|
+
metadata: {
|
|
186
|
+
authorEmail: normalizeEmail(args.actor.email),
|
|
187
|
+
...args.actor.fullName ? { authorFullName: args.actor.fullName } : {},
|
|
188
|
+
authorUserId: args.actor.userId,
|
|
189
|
+
...args.actor.userName ? { authorUserName: args.actor.userName } : {},
|
|
190
|
+
kind: "api_turn",
|
|
191
|
+
messageId: args.messageId
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
receivedAtMs: nowMs,
|
|
195
|
+
publishExternally: false,
|
|
196
|
+
source: "web"
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
async function recordApiConversationActivity(args) {
|
|
200
|
+
const store = args.conversationStore ?? getConversationStore();
|
|
201
|
+
const existing = await store.get({ conversationId: args.conversationId });
|
|
202
|
+
const destination = resolveApiTurnDestination({
|
|
203
|
+
conversationId: args.conversationId,
|
|
204
|
+
existing: existing?.destination
|
|
205
|
+
});
|
|
206
|
+
const isNewRoot = !existing;
|
|
207
|
+
const visibility = args.rootVisibility === "private" ? "private" : "public";
|
|
208
|
+
const source = isNewRoot ? webSourceForConversation({
|
|
209
|
+
conversationId: args.conversationId,
|
|
210
|
+
visibility
|
|
211
|
+
}) : void 0;
|
|
212
|
+
await store.recordActivity({
|
|
213
|
+
conversationId: args.conversationId,
|
|
214
|
+
destination,
|
|
215
|
+
nowMs: args.nowMs,
|
|
216
|
+
actor: storedActorFromApi(args.actor),
|
|
217
|
+
// Do not rewrite a Slack root's origin source when a dashboard participant
|
|
218
|
+
// continues it. Mailbox entries still carry source "web" per turn.
|
|
219
|
+
...isNewRoot ? { source: "web" } : {},
|
|
220
|
+
...source ? { sessionSource: source } : {},
|
|
221
|
+
...isNewRoot ? { visibility } : {}
|
|
222
|
+
});
|
|
223
|
+
return destination;
|
|
224
|
+
}
|
|
225
|
+
async function createAndEnqueueApiConversation(input, options) {
|
|
226
|
+
if (!input.actor.email) {
|
|
227
|
+
throw new Error("API conversation actor requires a verified email");
|
|
228
|
+
}
|
|
229
|
+
const conversationId = createApiConversationId({
|
|
230
|
+
actorEmail: input.actor.email,
|
|
231
|
+
idempotencyKey: input.idempotencyKey
|
|
232
|
+
});
|
|
233
|
+
return await appendAndEnqueueApiConversationMessage(
|
|
234
|
+
{
|
|
235
|
+
actor: input.actor,
|
|
236
|
+
conversationId,
|
|
237
|
+
idempotencyKey: input.idempotencyKey,
|
|
238
|
+
message: input.message,
|
|
239
|
+
rootVisibility: input.visibility === "private" ? "private" : "public"
|
|
240
|
+
},
|
|
241
|
+
options
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
async function appendAndEnqueueApiConversationMessage(input, options) {
|
|
245
|
+
const text = input.message.trim();
|
|
246
|
+
if (!text) {
|
|
247
|
+
throw new Error("API conversation message must not be empty");
|
|
248
|
+
}
|
|
249
|
+
if (!input.actor.email) {
|
|
250
|
+
throw new Error("API conversation actor requires a verified email");
|
|
251
|
+
}
|
|
252
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
253
|
+
const messageId = apiMessageId({
|
|
254
|
+
conversationId: input.conversationId,
|
|
255
|
+
idempotencyKey: input.idempotencyKey
|
|
256
|
+
});
|
|
257
|
+
const destination = await recordApiConversationActivity({
|
|
258
|
+
actor: input.actor,
|
|
259
|
+
conversationId: input.conversationId,
|
|
260
|
+
conversationStore: options.conversationStore,
|
|
261
|
+
nowMs,
|
|
262
|
+
...input.rootVisibility ? { rootVisibility: input.rootVisibility } : {}
|
|
263
|
+
});
|
|
264
|
+
const result = await appendAndEnqueueInboundMessage({
|
|
265
|
+
message: buildApiTurnInboundMessage({
|
|
266
|
+
actor: input.actor,
|
|
267
|
+
conversationId: input.conversationId,
|
|
268
|
+
destination,
|
|
269
|
+
message: text,
|
|
270
|
+
messageId,
|
|
271
|
+
nowMs
|
|
272
|
+
}),
|
|
273
|
+
conversationStore: options.conversationStore,
|
|
274
|
+
nowMs,
|
|
275
|
+
queue: options.queue,
|
|
276
|
+
state: options.state
|
|
277
|
+
});
|
|
278
|
+
return {
|
|
279
|
+
conversationId: input.conversationId,
|
|
280
|
+
messageId,
|
|
281
|
+
status: result.status === "duplicate" ? "duplicate" : "accepted"
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function parseApiTurnMessages(messages) {
|
|
285
|
+
if (messages.length === 0) {
|
|
286
|
+
return [];
|
|
287
|
+
}
|
|
288
|
+
const parsed = messages.map((message) => ({
|
|
289
|
+
message,
|
|
290
|
+
metadata: apiTurnMailboxMetadataSchema.safeParse(message.input.metadata)
|
|
291
|
+
}));
|
|
292
|
+
if (parsed.every((entry) => !entry.metadata.success)) {
|
|
293
|
+
return [];
|
|
294
|
+
}
|
|
295
|
+
if (parsed.some((entry) => !entry.metadata.success)) {
|
|
296
|
+
throw new Error("Conversation mailbox mixes web turns and other input");
|
|
297
|
+
}
|
|
298
|
+
return parsed.map((entry) => {
|
|
299
|
+
if (!entry.metadata.success) {
|
|
300
|
+
throw new Error("API turn mailbox metadata failed validation");
|
|
301
|
+
}
|
|
302
|
+
return { message: entry.message, metadata: entry.metadata.data };
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
async function resolveApiTurnWork(context) {
|
|
306
|
+
const batch = parseApiTurnMessages(context.attempt.messages);
|
|
307
|
+
if (batch.length > 0) {
|
|
308
|
+
return { kind: "mailbox", batch };
|
|
309
|
+
}
|
|
310
|
+
if (context.attempt.messages.length > 0) {
|
|
311
|
+
return void 0;
|
|
312
|
+
}
|
|
313
|
+
const summaries = await listTurnSummaries(context.conversationId);
|
|
314
|
+
const active = summaries.filter(
|
|
315
|
+
(summary) => summary.surface === "api" && !summary.dispatchId && (summary.state === "paused" || summary.state === "running")
|
|
316
|
+
);
|
|
317
|
+
if (active.length > 1) {
|
|
318
|
+
throw new Error(
|
|
319
|
+
`Conversation ${context.conversationId} has multiple active web turns`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
const turnId = active[0]?.turnId;
|
|
323
|
+
if (!turnId) {
|
|
324
|
+
return void 0;
|
|
325
|
+
}
|
|
326
|
+
const record = await getTurnRecord(context.conversationId, turnId);
|
|
327
|
+
if (!record || record.surface !== "api" || Boolean(record.dispatchId) || record.state !== "paused" && record.state !== "running") {
|
|
328
|
+
return void 0;
|
|
329
|
+
}
|
|
330
|
+
return { kind: "resume", turnId };
|
|
331
|
+
}
|
|
332
|
+
async function isApiTurnWork(context) {
|
|
333
|
+
return await resolveApiTurnWork(context) !== void 0;
|
|
334
|
+
}
|
|
335
|
+
function captureApiBoundaryFailure(args) {
|
|
336
|
+
const eventId = logException(args.error, `api.turn.${args.failureCode}`, {
|
|
337
|
+
conversationId: args.conversationId,
|
|
338
|
+
...args.runId ? { runId: args.runId } : {},
|
|
339
|
+
turnId: args.turnId
|
|
340
|
+
});
|
|
341
|
+
return typeof eventId === "string" ? eventId : void 0;
|
|
342
|
+
}
|
|
343
|
+
function createApiTurnWorker(options) {
|
|
344
|
+
return async (context) => {
|
|
345
|
+
const resolved = await resolveApiTurnWork(context);
|
|
346
|
+
if (!resolved) {
|
|
347
|
+
throw new Error(
|
|
348
|
+
`API turn worker received non-API work for ${context.conversationId}`
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
if (context.publishExternally) {
|
|
352
|
+
throw new Error(
|
|
353
|
+
`API turn work must not publish externally for ${context.conversationId}`
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
const lifecycle = options.turnLifecycle ?? new ConversationTurnLifecycleService(getConversationEventStore());
|
|
357
|
+
const isResume = resolved.kind === "resume";
|
|
358
|
+
let actor;
|
|
359
|
+
let text;
|
|
360
|
+
let turnId;
|
|
361
|
+
let userMessageId;
|
|
362
|
+
let startedAtMs;
|
|
363
|
+
let inputMessageIds;
|
|
364
|
+
const storedConversation = await getConversationStore().get({
|
|
365
|
+
conversationId: context.conversationId
|
|
366
|
+
});
|
|
367
|
+
if (resolved.kind === "mailbox") {
|
|
368
|
+
const first = resolved.batch[0];
|
|
369
|
+
text = resolved.batch.map((entry) => entry.message.input.text.trim()).filter(Boolean).join("\n\n");
|
|
370
|
+
actor = actorFromMetadata(first.metadata);
|
|
371
|
+
turnId = apiTurnIdForMessage(first.metadata.messageId);
|
|
372
|
+
userMessageId = first.metadata.messageId;
|
|
373
|
+
startedAtMs = first.message.createdAtMs;
|
|
374
|
+
inputMessageIds = resolved.batch.map((entry) => entry.metadata.messageId);
|
|
375
|
+
} else {
|
|
376
|
+
turnId = resolved.turnId;
|
|
377
|
+
const resumedActor = actorFromStoredConversation(
|
|
378
|
+
storedConversation?.actor
|
|
379
|
+
);
|
|
380
|
+
if (!resumedActor) {
|
|
381
|
+
throw new Error(
|
|
382
|
+
`API turn resume missing actor for ${context.conversationId}`
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
actor = resumedActor;
|
|
386
|
+
text = "";
|
|
387
|
+
userMessageId = "";
|
|
388
|
+
startedAtMs = Date.now();
|
|
389
|
+
inputMessageIds = [];
|
|
390
|
+
}
|
|
391
|
+
const destination = resolveApiTurnDestination({
|
|
392
|
+
conversationId: context.conversationId,
|
|
393
|
+
existing: context.destination ?? (resolved.kind === "mailbox" ? resolved.batch[0]?.message.destination : void 0) ?? storedConversation?.destination
|
|
394
|
+
});
|
|
395
|
+
const source = webSourceForConversation({
|
|
396
|
+
conversationId: context.conversationId,
|
|
397
|
+
visibility: storedConversation?.visibility
|
|
398
|
+
});
|
|
399
|
+
return await withLogContext(
|
|
400
|
+
{
|
|
401
|
+
conversationId: context.conversationId,
|
|
402
|
+
platform: "web",
|
|
403
|
+
userId: actor.userId,
|
|
404
|
+
...actor.userName ? { userName: actor.userName } : {}
|
|
405
|
+
},
|
|
406
|
+
async () => {
|
|
407
|
+
let acknowledged = isResume || context.attempt.messages.length === 0;
|
|
408
|
+
const acknowledge = async () => {
|
|
409
|
+
if (acknowledged) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
try {
|
|
413
|
+
await context.attempt.ack();
|
|
414
|
+
} catch {
|
|
415
|
+
throw new TurnInputCommitLostError(
|
|
416
|
+
`Conversation work lease lost before API turn inbox ack for ${context.conversationId}`
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
acknowledged = true;
|
|
420
|
+
};
|
|
421
|
+
const persisted = await getPersistedThreadState(context.conversationId);
|
|
422
|
+
const conversation = coerceThreadConversationState(persisted);
|
|
423
|
+
await hydrateConversationMessages({
|
|
424
|
+
conversation,
|
|
425
|
+
conversationId: context.conversationId
|
|
426
|
+
});
|
|
427
|
+
let sandboxRef = getPersistedSandboxState(persisted);
|
|
428
|
+
const initialSandboxRef = sandboxRef;
|
|
429
|
+
if (isResume) {
|
|
430
|
+
const userMessage = getTurnUserMessage(conversation, turnId);
|
|
431
|
+
if (!userMessage) {
|
|
432
|
+
throw new Error(
|
|
433
|
+
`Unable to locate the persisted user message for API turn "${turnId}"`
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
userMessageId = userMessage.id;
|
|
437
|
+
text = userMessage.text;
|
|
438
|
+
startedAtMs = userMessage.createdAtMs;
|
|
439
|
+
inputMessageIds = [userMessageId];
|
|
440
|
+
} else {
|
|
441
|
+
upsertConversationMessage(conversation, {
|
|
442
|
+
id: userMessageId,
|
|
443
|
+
role: "user",
|
|
444
|
+
text: normalizeConversationText(text),
|
|
445
|
+
createdAtMs: startedAtMs,
|
|
446
|
+
author: {
|
|
447
|
+
...actor.fullName ? { fullName: actor.fullName } : {},
|
|
448
|
+
userId: actor.userId,
|
|
449
|
+
...actor.userName ? { userName: actor.userName } : {}
|
|
450
|
+
},
|
|
451
|
+
meta: {
|
|
452
|
+
explicitMention: true,
|
|
453
|
+
replied: false,
|
|
454
|
+
source: "web"
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
await persistConversationMessages({
|
|
458
|
+
conversation,
|
|
459
|
+
conversationId: context.conversationId
|
|
460
|
+
});
|
|
461
|
+
await lifecycle.start({
|
|
462
|
+
conversationId: context.conversationId,
|
|
463
|
+
createdAtMs: Date.now(),
|
|
464
|
+
inputMessageIds,
|
|
465
|
+
surface: "api",
|
|
466
|
+
turnId
|
|
467
|
+
});
|
|
468
|
+
startActiveTurn({
|
|
469
|
+
conversation,
|
|
470
|
+
nextTurnId: turnId
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
let currentRunId;
|
|
474
|
+
let assistantMessageDelivered = false;
|
|
475
|
+
let failureCode = "persistence_failed";
|
|
476
|
+
let modelFailureEventId;
|
|
477
|
+
let modelFailureCaptureAttempted = false;
|
|
478
|
+
let reply;
|
|
479
|
+
const deliverAssistantMessage = async (value) => {
|
|
480
|
+
const replyText = typeof value === "string" ? value : getAssistantReplyText(value);
|
|
481
|
+
if (!replyText?.trim()) {
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
failureCode = "delivery_failed";
|
|
485
|
+
assistantMessageDelivered = true;
|
|
486
|
+
recordDeliveredAssistantMessage({
|
|
487
|
+
conversation,
|
|
488
|
+
sessionId: turnId,
|
|
489
|
+
source: "web",
|
|
490
|
+
text: replyText,
|
|
491
|
+
userMessageId
|
|
492
|
+
});
|
|
493
|
+
try {
|
|
494
|
+
await persistWithRetry(
|
|
495
|
+
() => persistConversationMessages({
|
|
496
|
+
conversation,
|
|
497
|
+
conversationId: context.conversationId
|
|
498
|
+
})
|
|
499
|
+
);
|
|
500
|
+
} catch (error) {
|
|
501
|
+
logException(
|
|
502
|
+
new Error("Accepted assistant message persistence failed"),
|
|
503
|
+
"api.assistant.message_post_delivery_persist.failed",
|
|
504
|
+
{
|
|
505
|
+
"error.type": error instanceof Error ? error.name : typeof error
|
|
506
|
+
}
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
failureCode = "agent_run_failed";
|
|
510
|
+
};
|
|
511
|
+
try {
|
|
512
|
+
await persistThreadStateById(context.conversationId, {
|
|
513
|
+
conversation
|
|
514
|
+
});
|
|
515
|
+
const piMessages = await loadProjection({
|
|
516
|
+
conversationId: context.conversationId
|
|
517
|
+
});
|
|
518
|
+
failureCode = "agent_run_failed";
|
|
519
|
+
currentRunId = `api-run:${stableHex(turnId, String(startedAtMs))}`;
|
|
520
|
+
setTags({ runId: currentRunId });
|
|
521
|
+
const outcome = await options.agentRunner.run({
|
|
522
|
+
conversationId: context.conversationId,
|
|
523
|
+
turnId,
|
|
524
|
+
runId: currentRunId,
|
|
525
|
+
input: {
|
|
526
|
+
messageText: text,
|
|
527
|
+
conversationContext: buildConversationContext(conversation, {
|
|
528
|
+
excludeMessageId: userMessageId
|
|
529
|
+
}),
|
|
530
|
+
piMessages
|
|
531
|
+
},
|
|
532
|
+
routing: {
|
|
533
|
+
actor,
|
|
534
|
+
credentialContext: credentialContextForActor(actor),
|
|
535
|
+
destination,
|
|
536
|
+
publishExternally: false,
|
|
537
|
+
source,
|
|
538
|
+
surface: "api"
|
|
539
|
+
},
|
|
540
|
+
policy: {
|
|
541
|
+
disabledFeatures: ["interactive-auth"]
|
|
542
|
+
},
|
|
543
|
+
state: {
|
|
544
|
+
pendingAuth: conversation.processing.pendingAuth,
|
|
545
|
+
sandboxRef
|
|
546
|
+
},
|
|
547
|
+
delivery: deliverAssistantMessage,
|
|
548
|
+
durability: {
|
|
549
|
+
onInputCommitted: acknowledge,
|
|
550
|
+
shouldYield: context.shouldYield,
|
|
551
|
+
onSandboxRefChanged: async (nextSandboxRef) => {
|
|
552
|
+
sandboxRef = nextSandboxRef;
|
|
553
|
+
await persistThreadStateById(context.conversationId, {
|
|
554
|
+
conversation,
|
|
555
|
+
sandboxRef
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
if (outcome.status === "suspended") {
|
|
561
|
+
return { status: "yielded" };
|
|
562
|
+
}
|
|
563
|
+
if (outcome.status !== "completed") {
|
|
564
|
+
throw new Error(`API agent run ended with ${outcome.status}`);
|
|
565
|
+
}
|
|
566
|
+
reply = outcome.result;
|
|
567
|
+
modelFailureCaptureAttempted = reply.diagnostics.outcome !== "success";
|
|
568
|
+
const finalized = finalizeFailedTurnReplyWithEvent({
|
|
569
|
+
reply,
|
|
570
|
+
logException
|
|
571
|
+
});
|
|
572
|
+
reply = finalized.reply;
|
|
573
|
+
modelFailureEventId = finalized.eventId;
|
|
574
|
+
if (reply.diagnostics.outcome !== "success") {
|
|
575
|
+
await deliverAssistantMessage(reply.text);
|
|
576
|
+
}
|
|
577
|
+
const completedState = buildDeliveredTurnStatePatch({
|
|
578
|
+
conversation,
|
|
579
|
+
reply,
|
|
580
|
+
sessionId: turnId,
|
|
581
|
+
userMessageId
|
|
582
|
+
});
|
|
583
|
+
await persistThreadStateById(context.conversationId, {
|
|
584
|
+
conversation: completedState.conversation,
|
|
585
|
+
sandboxRef: reply.sandboxRef ?? sandboxRef
|
|
586
|
+
});
|
|
587
|
+
if (reply.piMessages?.length) {
|
|
588
|
+
const latest = await getTurnRecord(context.conversationId, turnId);
|
|
589
|
+
await saveTurnCheckpoint({
|
|
590
|
+
mode: "completed",
|
|
591
|
+
conversationId: context.conversationId,
|
|
592
|
+
turnId,
|
|
593
|
+
sliceId: latest?.sliceId ?? 1,
|
|
594
|
+
messages: reply.piMessages,
|
|
595
|
+
durationMs: reply.diagnostics.durationMs,
|
|
596
|
+
usage: reply.diagnostics.usage,
|
|
597
|
+
destination,
|
|
598
|
+
publishExternally: false,
|
|
599
|
+
source,
|
|
600
|
+
actor,
|
|
601
|
+
surface: "api"
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
if (reply.diagnostics.outcome === "success") {
|
|
605
|
+
await lifecycle.complete({
|
|
606
|
+
conversationId: context.conversationId,
|
|
607
|
+
createdAtMs: Date.now(),
|
|
608
|
+
outcome: assistantMessageDelivered ? "success" : "no_reply",
|
|
609
|
+
turnId
|
|
610
|
+
});
|
|
611
|
+
try {
|
|
612
|
+
await scheduleSessionCompletedPluginTasks(
|
|
613
|
+
{
|
|
614
|
+
conversationId: context.conversationId,
|
|
615
|
+
sessionId: turnId
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
send: async (message) => {
|
|
619
|
+
try {
|
|
620
|
+
await processPluginTask(message);
|
|
621
|
+
} catch (error) {
|
|
622
|
+
logException(
|
|
623
|
+
error,
|
|
624
|
+
"api.plugin.session_completion_task.failed",
|
|
625
|
+
{
|
|
626
|
+
conversationId: context.conversationId,
|
|
627
|
+
pluginName: message.plugin,
|
|
628
|
+
taskName: message.name,
|
|
629
|
+
turnId
|
|
630
|
+
}
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
);
|
|
636
|
+
} catch (error) {
|
|
637
|
+
logException(error, "api.plugin.session_completion_task.failed", {
|
|
638
|
+
conversationId: context.conversationId,
|
|
639
|
+
turnId
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
} else {
|
|
643
|
+
await lifecycle.fail({
|
|
644
|
+
conversationId: context.conversationId,
|
|
645
|
+
createdAtMs: Date.now(),
|
|
646
|
+
...modelFailureEventId ? { eventId: modelFailureEventId } : {},
|
|
647
|
+
failureCode: "model_execution_failed",
|
|
648
|
+
turnId
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
await acknowledge();
|
|
652
|
+
return { status: "completed" };
|
|
653
|
+
} catch (error) {
|
|
654
|
+
const cause = getConversationTurnBoundaryError(error)?.cause ?? error;
|
|
655
|
+
if (isTurnInputCommitLostError(error) || isTurnInputCommitLostError(cause)) {
|
|
656
|
+
return { status: "lost_lease" };
|
|
657
|
+
}
|
|
658
|
+
if (!context.attempt.isFinalAttempt) {
|
|
659
|
+
throw error;
|
|
660
|
+
}
|
|
661
|
+
const failureEventId = modelFailureCaptureAttempted && failureCode === "agent_run_failed" ? modelFailureEventId : captureApiBoundaryFailure({
|
|
662
|
+
conversationId: context.conversationId,
|
|
663
|
+
error,
|
|
664
|
+
failureCode,
|
|
665
|
+
runId: currentRunId,
|
|
666
|
+
turnId
|
|
667
|
+
});
|
|
668
|
+
try {
|
|
669
|
+
markTurnFailed({
|
|
670
|
+
conversation,
|
|
671
|
+
nowMs: Date.now(),
|
|
672
|
+
sessionId: turnId,
|
|
673
|
+
userMessageId,
|
|
674
|
+
markConversationMessage
|
|
675
|
+
});
|
|
676
|
+
await persistThreadStateById(context.conversationId, {
|
|
677
|
+
conversation,
|
|
678
|
+
sandboxRef: initialSandboxRef ?? null
|
|
679
|
+
});
|
|
680
|
+
} catch (persistenceError) {
|
|
681
|
+
captureApiBoundaryFailure({
|
|
682
|
+
conversationId: context.conversationId,
|
|
683
|
+
error: persistenceError,
|
|
684
|
+
failureCode: "persistence_failed",
|
|
685
|
+
runId: currentRunId,
|
|
686
|
+
turnId
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
await lifecycle.fail({
|
|
690
|
+
conversationId: context.conversationId,
|
|
691
|
+
createdAtMs: Date.now(),
|
|
692
|
+
...failureEventId ? { eventId: failureEventId } : {},
|
|
693
|
+
failureCode,
|
|
694
|
+
turnId
|
|
695
|
+
});
|
|
696
|
+
await acknowledge();
|
|
697
|
+
return { status: "completed" };
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
);
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
function routeApiTurnWork(options) {
|
|
704
|
+
return async (context) => {
|
|
705
|
+
return await isApiTurnWork(context) ? await options.apiTurnWorker(context) : await options.fallbackWorker(context);
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// src/chat/slack/conversation-context.ts
|
|
710
|
+
function normalizeConversationName(type, channelName) {
|
|
711
|
+
const trimmed = channelName?.trim();
|
|
712
|
+
if (!trimmed) return void 0;
|
|
713
|
+
if (type === "public_channel" || type === "private_channel" || type === "private_channel_or_group_dm") {
|
|
714
|
+
return trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
|
|
715
|
+
}
|
|
716
|
+
return trimmed;
|
|
717
|
+
}
|
|
718
|
+
function typeFromSlackChannelType(channelType) {
|
|
719
|
+
if (channelType === "channel") return "public_channel";
|
|
720
|
+
if (channelType === "group") return "private_channel";
|
|
721
|
+
if (channelType === "mpim") return "group_dm";
|
|
722
|
+
if (channelType === "im") return "direct_message";
|
|
723
|
+
return void 0;
|
|
724
|
+
}
|
|
725
|
+
function typeFromChannelId(channelId, channelName) {
|
|
726
|
+
const normalized = normalizeSlackConversationId(channelId);
|
|
727
|
+
if (!normalized) return void 0;
|
|
728
|
+
if (normalized.startsWith("C")) return "public_channel";
|
|
729
|
+
if (normalized.startsWith("D")) return "direct_message";
|
|
730
|
+
if (normalized.startsWith("G")) {
|
|
731
|
+
return channelName?.trim().startsWith("mpdm-") ? "group_dm" : "private_channel_or_group_dm";
|
|
732
|
+
}
|
|
733
|
+
return void 0;
|
|
734
|
+
}
|
|
735
|
+
function toSlackEventChannelType(channelType) {
|
|
736
|
+
if (channelType === "channel" || channelType === "group" || channelType === "mpim" || channelType === "im") {
|
|
737
|
+
return channelType;
|
|
738
|
+
}
|
|
739
|
+
return void 0;
|
|
740
|
+
}
|
|
741
|
+
function conversationVisibilityFromSlackChannelType(channelType) {
|
|
742
|
+
if (!channelType) return void 0;
|
|
743
|
+
return channelType === "channel" ? "public" : "private";
|
|
744
|
+
}
|
|
745
|
+
function resolveSlackChannelTypeFromMessage(message) {
|
|
746
|
+
const raw = message.raw;
|
|
747
|
+
if (!raw || typeof raw !== "object") {
|
|
748
|
+
return void 0;
|
|
749
|
+
}
|
|
750
|
+
const channelType = raw.channel_type;
|
|
751
|
+
return typeof channelType === "string" ? toSlackEventChannelType(channelType.trim()) : void 0;
|
|
752
|
+
}
|
|
753
|
+
function resolveSlackConversationContext(input) {
|
|
754
|
+
const type = typeFromSlackChannelType(input.channelType) ?? typeFromChannelId(input.channelId, input.channelName);
|
|
755
|
+
if (!type) return void 0;
|
|
756
|
+
const name = normalizeConversationName(type, input.channelName);
|
|
757
|
+
const visibility = conversationVisibilityFromSlackChannelType(
|
|
758
|
+
input.channelType
|
|
759
|
+
);
|
|
760
|
+
return {
|
|
761
|
+
type,
|
|
762
|
+
...name ? { name } : {},
|
|
763
|
+
...visibility ? { visibility } : {}
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
function resolveSlackConversationContextFromThreadId(input) {
|
|
767
|
+
const slackThread = parseSlackThreadId(input.threadId);
|
|
768
|
+
const context = resolveSlackConversationContext({
|
|
769
|
+
channelId: slackThread?.channelId,
|
|
770
|
+
channelName: input.channelName
|
|
771
|
+
});
|
|
772
|
+
if (!context || !input.visibility) {
|
|
773
|
+
return context;
|
|
774
|
+
}
|
|
775
|
+
return {
|
|
776
|
+
...context,
|
|
777
|
+
type: input.visibility === "private" && context.type === "public_channel" ? "private_channel" : context.type,
|
|
778
|
+
visibility: input.visibility
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
function formatSlackConversationTypeLabel(type) {
|
|
782
|
+
if (type === "public_channel") return "Public Channel";
|
|
783
|
+
if (type === "private_channel") return "Private Channel";
|
|
784
|
+
if (type === "group_dm") return "Group DM";
|
|
785
|
+
if (type === "direct_message") return "Direct Message";
|
|
786
|
+
return "Private Channel or Group DM";
|
|
787
|
+
}
|
|
788
|
+
function formatSlackConversationRedactedLabel(context) {
|
|
789
|
+
if (!context) return void 0;
|
|
790
|
+
return formatSlackConversationTypeLabel(context.type);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
export {
|
|
794
|
+
readHealthReport,
|
|
795
|
+
GET,
|
|
796
|
+
conversationVisibilityFromSlackChannelType,
|
|
797
|
+
resolveSlackChannelTypeFromMessage,
|
|
798
|
+
resolveSlackConversationContext,
|
|
799
|
+
resolveSlackConversationContextFromThreadId,
|
|
800
|
+
formatSlackConversationRedactedLabel,
|
|
801
|
+
webActorFromEmail,
|
|
802
|
+
createAndEnqueueApiConversation,
|
|
803
|
+
appendAndEnqueueApiConversationMessage,
|
|
804
|
+
createApiTurnWorker,
|
|
805
|
+
routeApiTurnWork
|
|
806
|
+
};
|