@sentry/junior 0.184.0 → 0.186.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-XTZVALGQ.js → agent-hooks-4B3Z4WRK.js} +13 -11
- package/dist/api/acp/schema.d.ts +20 -2
- package/dist/api/code/overview.d.ts +39 -0
- package/dist/api/locations/detail.d.ts +1 -1
- package/dist/api/people/code.d.ts +22 -0
- package/dist/api/schema/code.d.ts +46 -0
- package/dist/api/schema/conversation.d.ts +108 -20
- package/dist/api/schema/location.d.ts +1 -1
- package/dist/api/schema/person.d.ts +1 -1
- package/dist/api/schema.d.ts +4 -4
- package/dist/api/schema.js +12 -1
- package/dist/api.js +368 -78
- package/dist/app.js +1412 -538
- package/dist/{catalog-runtime-5357XR4F.js → catalog-runtime-AFJCAOHC.js} +4 -4
- package/dist/chat/api-turns/accepted-reply.d.ts +11 -0
- package/dist/chat/api-turns/cancellation.d.ts +3 -5
- package/dist/chat/api-turns/mailbox-input.d.ts +7 -0
- package/dist/chat/api-turns/routing.d.ts +18 -3
- package/dist/chat/api-turns/work.d.ts +4 -9
- package/dist/chat/app/conversation-work.d.ts +1 -1
- package/dist/chat/app/factory.d.ts +4 -0
- package/dist/chat/app/services.d.ts +4 -3
- package/dist/chat/conversations/history.d.ts +83 -9
- package/dist/chat/conversations/turn-lifecycle.d.ts +6 -3
- package/dist/chat/providers/slack/input.d.ts +32 -0
- package/dist/chat/{runtime → providers/slack}/processing-reaction.d.ts +6 -8
- package/dist/chat/{runtime/slack-resume.d.ts → providers/slack/resume.d.ts} +16 -19
- package/dist/chat/providers/slack/runtime.d.ts +1 -12
- package/dist/chat/providers/slack/turn.d.ts +10 -25
- package/dist/chat/resource-events/actor.d.ts +7 -2
- package/dist/chat/resource-events/app-publisher.d.ts +18 -0
- package/dist/chat/resource-events/catalog.d.ts +19 -0
- package/dist/chat/resource-events/ingest.d.ts +0 -1
- package/dist/chat/resource-events/notification.d.ts +28 -3
- package/dist/chat/resource-events/runtime-catalog.d.ts +7 -2
- package/dist/chat/resource-events/store.d.ts +2 -14
- package/dist/chat/resource-events/tool-support.d.ts +0 -5
- package/dist/chat/resource-events/workspace.d.ts +7 -3
- package/dist/chat/runtime/turn-execution.d.ts +7 -2
- package/dist/chat/sandbox/egress/credentials.d.ts +5 -4
- package/dist/chat/sandbox/egress/session.d.ts +10 -11
- package/dist/chat/sandbox/errors.d.ts +1 -1
- package/dist/chat/sandbox/ref.d.ts +1 -1
- package/dist/chat/sandbox/snapshot/events.d.ts +8 -1
- package/dist/chat/sandbox/snapshot/not-ready-error.d.ts +6 -1
- 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/scheduled-tasks/types.d.ts +1 -1
- package/dist/chat/sentry-links.d.ts +2 -0
- package/dist/chat/services/provider-retry.d.ts +2 -0
- package/dist/chat/services/turn-failure-response.d.ts +4 -0
- package/dist/chat/services/turn-session-routing.d.ts +20 -5
- package/dist/chat/task-execution/paused-turn.d.ts +0 -2
- package/dist/chat/task-execution/slack-resource-event.d.ts +47 -0
- package/dist/chat/task-execution/slack-work.d.ts +0 -21
- package/dist/chat/task-execution/state.d.ts +2 -2
- package/dist/chat/task-execution/store.d.ts +1 -1
- package/dist/chat/task-execution/turn-wake.d.ts +12 -0
- package/dist/chat/tool-support/action-review.d.ts +1 -1
- package/dist/chat/tools/event-tasks.d.ts +1 -1
- package/dist/chat/tools/resource-events.d.ts +2 -2
- package/dist/chat/tools/search-conversation-events.d.ts +1 -1
- package/dist/chat/tools/types.d.ts +1 -1
- package/dist/chat/tools/watch-resource-events.d.ts +2 -2
- package/dist/{chunk-Z3QAXJR7.js → chunk-2IBDVE62.js} +12 -9
- package/dist/{chunk-4RONS3WR.js → chunk-2U7EPTRZ.js} +11 -5
- package/dist/{chunk-3S7ULUWW.js → chunk-3CVEE6LA.js} +1 -1
- package/dist/{chunk-IZDYYYGP.js → chunk-43LODHJM.js} +24 -384
- package/dist/{chunk-LHXIZLFF.js → chunk-64WF2GQ3.js} +1 -1
- package/dist/{chunk-YKX6MTLU.js → chunk-7EOWT62G.js} +1 -1
- package/dist/{chunk-UBOIHNTH.js → chunk-7EU4YSZ4.js} +362 -188
- package/dist/{chunk-UF6BUA5W.js → chunk-ACGWTJ74.js} +3 -3
- package/dist/{chunk-6UDPINOX.js → chunk-AD64YMEC.js} +19 -44
- package/dist/{chunk-MLSQX2EX.js → chunk-BRKB5R7P.js} +1 -1
- package/dist/{chunk-E57WPXWY.js → chunk-CM6RTYSO.js} +5 -5
- package/dist/{chunk-MAPJFUMV.js → chunk-CV5CPRVG.js} +3 -3
- package/dist/chunk-D4YV5TDV.js +45 -0
- package/dist/{chunk-HIPUIPJM.js → chunk-DHYZ4VKG.js} +383 -254
- package/dist/{chunk-DUKIDPF6.js → chunk-DOP3FIQC.js} +1 -1
- package/dist/{chunk-BT53RIGJ.js → chunk-EWRPUXE3.js} +12 -50
- package/dist/{chunk-BHYRYYLY.js → chunk-FFYDRLXW.js} +1 -1
- package/dist/chunk-GFBR4S3Q.js +406 -0
- package/dist/chunk-GQMQCY7H.js +58 -0
- package/dist/{chunk-JNO2GUDS.js → chunk-GSB3NTNS.js} +926 -1856
- package/dist/{chunk-2ZQPVX7C.js → chunk-H7X2G2KS.js} +25 -12
- package/dist/{chunk-WQFEWULF.js → chunk-HKFLRIBD.js} +2 -2
- package/dist/{chunk-SHJK75Z2.js → chunk-HWXP6RHG.js} +38 -4
- package/dist/{chunk-RF6J3DIO.js → chunk-INQHUE72.js} +84 -1
- package/dist/{chunk-4CNDGK25.js → chunk-JFPY6XFF.js} +911 -993
- package/dist/{chunk-6WGC3SO4.js → chunk-MCLWOBPN.js} +80 -18
- package/dist/{chunk-PJPRXO2T.js → chunk-NRMAOGRS.js} +1 -1
- package/dist/{chunk-ACPDVEJO.js → chunk-O6GQO7VP.js} +201 -218
- package/dist/{chunk-M5YWNMTE.js → chunk-OZG7E22Y.js} +24 -6
- package/dist/{chunk-WVIA7V3M.js → chunk-PFAZSRGR.js} +29 -1
- package/dist/{chunk-LCP4EDKR.js → chunk-PZZOGHVR.js} +14 -4
- package/dist/{chunk-QABCY33S.js → chunk-QBL2AVFF.js} +2 -2
- package/dist/{chunk-FKEZZMXS.js → chunk-QU3URVWG.js} +9 -94
- package/dist/{chunk-M3QYTBQ6.js → chunk-STOCCPWH.js} +11 -2
- package/dist/{chunk-K7VDC2QW.js → chunk-TXLBLGNQ.js} +6 -6
- package/dist/{chunk-JA43DV4Y.js → chunk-VOGOTEW7.js} +1 -1
- package/dist/{chunk-HUXFMDBZ.js → chunk-VQIWBJOP.js} +1 -1
- package/dist/{chunk-JM2COLGC.js → chunk-VQZYIVYN.js} +6 -6
- package/dist/{chunk-BCHYQS4B.js → chunk-VXL2LBPK.js} +1 -1
- package/dist/{chunk-6VOYP4M6.js → chunk-YXSBZRP7.js} +1 -1
- package/dist/cli/chat.js +35 -33
- package/dist/cli/check.js +5 -5
- package/dist/cli/plugins.js +14 -12
- package/dist/cli/snapshot-create.js +10 -9
- package/dist/cli/upgrade.js +7 -6
- package/dist/{db-MA52H62H.js → db-7XL4UPAE.js} +8 -6
- package/dist/instrumentation.js +3 -3
- package/dist/nitro.js +7 -6
- package/dist/{oauth-callback-server-DXGHKN6S.js → oauth-callback-server-RAOYHWDI.js} +30 -27
- package/dist/{runner-4IXUAQNK.js → runner-KMPBWRPM.js} +24 -19
- package/dist/{skills-X3DY7AXO.js → skills-IPCALGLQ.js} +5 -5
- package/dist/spawn-OSOWTMBG.js +41 -0
- package/dist/task-runner-IQNBA3RY.js +34 -0
- package/dist/{validation-Z6JXSTJS.js → validation-BSRCCX6N.js} +5 -5
- package/dist/version.js +1 -1
- package/dist/{work-OKWRFBDW.js → work-T6UFTOJY.js} +24 -19
- package/dist/worker-L35OG7VA.js +24 -0
- package/package.json +5 -5
- package/dist/spawn-43WGS6CH.js +0 -37
- package/dist/task-runner-VDRKITPV.js +0 -32
- package/dist/worker-B236AE72.js +0 -22
|
@@ -1,36 +1,45 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createWebAuthorization,
|
|
3
3
|
deleteWebAuthorization
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PZZOGHVR.js";
|
|
5
|
+
import {
|
|
6
|
+
AgentRunError,
|
|
7
|
+
executeTurn
|
|
8
|
+
} from "./chunk-GQMQCY7H.js";
|
|
5
9
|
import {
|
|
6
10
|
buildDeliveredTurnStatePatch,
|
|
7
11
|
completeAuthPauseTurn,
|
|
8
12
|
finalizeFailedTurnReplyWithEvent,
|
|
9
13
|
getTurnUserMessage
|
|
10
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-OZG7E22Y.js";
|
|
11
15
|
import {
|
|
12
16
|
processPluginTask,
|
|
13
17
|
scheduleSessionCompletedPluginTasks
|
|
14
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-MCLWOBPN.js";
|
|
15
19
|
import {
|
|
16
20
|
ConversationTurnLifecycleService
|
|
17
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-STOCCPWH.js";
|
|
18
22
|
import {
|
|
19
23
|
coerceThreadConversationState,
|
|
20
24
|
getPersistedSandboxState,
|
|
21
25
|
getPersistedThreadState,
|
|
22
26
|
persistThreadStateById
|
|
23
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-CM6RTYSO.js";
|
|
24
28
|
import {
|
|
25
29
|
healthReportSchema
|
|
26
30
|
} from "./chunk-UTL3MHPC.js";
|
|
31
|
+
import {
|
|
32
|
+
RESOURCE_EVENT_AUTHOR_ID,
|
|
33
|
+
isResourceEventConversationMessage,
|
|
34
|
+
isResourceEventMailboxMetadata
|
|
35
|
+
} from "./chunk-DHYZ4VKG.js";
|
|
27
36
|
import {
|
|
28
37
|
parseSlackThreadId
|
|
29
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-7EOWT62G.js";
|
|
30
39
|
import {
|
|
31
40
|
appendAndEnqueueExclusiveInboundMessage,
|
|
32
41
|
appendAndEnqueueInboundMessage
|
|
33
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-2IBDVE62.js";
|
|
34
43
|
import {
|
|
35
44
|
TurnInputCommitLostError,
|
|
36
45
|
buildDeterministicTurnId,
|
|
@@ -41,10 +50,11 @@ import {
|
|
|
41
50
|
markTurnClosed,
|
|
42
51
|
markTurnFailed,
|
|
43
52
|
startActiveTurn
|
|
44
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-VOGOTEW7.js";
|
|
45
54
|
import {
|
|
46
55
|
abandonTurnRecord,
|
|
47
56
|
buildConversationContext,
|
|
57
|
+
commitAcceptedReply,
|
|
48
58
|
getTurnRecord,
|
|
49
59
|
hydrateConversationMessages,
|
|
50
60
|
listTurnSummaries,
|
|
@@ -56,35 +66,39 @@ import {
|
|
|
56
66
|
recordDeliveredAssistantMessage,
|
|
57
67
|
saveTurnCheckpoint,
|
|
58
68
|
upsertConversationMessage
|
|
59
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-H7X2G2KS.js";
|
|
60
70
|
import {
|
|
61
71
|
normalizeSlackConversationId
|
|
62
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-JFPY6XFF.js";
|
|
63
73
|
import {
|
|
64
74
|
getConversationEventStore,
|
|
65
75
|
getConversationStore,
|
|
66
76
|
getDb
|
|
67
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-43LODHJM.js";
|
|
68
78
|
import {
|
|
69
79
|
juniorConversationParticipants,
|
|
70
80
|
juniorConversations,
|
|
71
81
|
juniorIdentities,
|
|
72
82
|
juniorUsers
|
|
73
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-VXL2LBPK.js";
|
|
74
84
|
import {
|
|
75
85
|
juniorDestinations
|
|
76
86
|
} from "./chunk-VFUK3X5B.js";
|
|
87
|
+
import {
|
|
88
|
+
getSlackBotToken
|
|
89
|
+
} from "./chunk-AD64YMEC.js";
|
|
77
90
|
import {
|
|
78
91
|
canExposeConversationPayload
|
|
79
|
-
} from "./chunk-
|
|
92
|
+
} from "./chunk-FFYDRLXW.js";
|
|
80
93
|
import {
|
|
81
94
|
credentialContextForActor
|
|
82
95
|
} from "./chunk-CEMMLV5L.js";
|
|
83
96
|
import {
|
|
84
97
|
logException,
|
|
98
|
+
logWarn,
|
|
85
99
|
setTags,
|
|
86
100
|
withLogContext
|
|
87
|
-
} from "./chunk-
|
|
101
|
+
} from "./chunk-3CVEE6LA.js";
|
|
88
102
|
|
|
89
103
|
// src/handlers/health.ts
|
|
90
104
|
function readHealthReport() {
|
|
@@ -174,11 +188,33 @@ function parseApiTurnMessages(messages) {
|
|
|
174
188
|
return { message: entry.message, metadata: entry.metadata.data };
|
|
175
189
|
});
|
|
176
190
|
}
|
|
191
|
+
function parseLocalResourceEventMessages(messages) {
|
|
192
|
+
if (messages.length === 0) {
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
if (!messages.every(
|
|
196
|
+
(message) => message.source === "resource_event" && isResourceEventMailboxMetadata(message.input.metadata)
|
|
197
|
+
)) {
|
|
198
|
+
return [];
|
|
199
|
+
}
|
|
200
|
+
return messages.map((message) => ({
|
|
201
|
+
message,
|
|
202
|
+
kind: "resource_event"
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
177
205
|
async function resolveApiTurnWork(context) {
|
|
178
206
|
const batch = parseApiTurnMessages(context.attempt.messages);
|
|
179
207
|
if (batch.length > 0) {
|
|
180
208
|
return { kind: "mailbox", batch };
|
|
181
209
|
}
|
|
210
|
+
if (context.destination?.platform === "local") {
|
|
211
|
+
const resourceBatch = parseLocalResourceEventMessages(
|
|
212
|
+
context.attempt.messages
|
|
213
|
+
);
|
|
214
|
+
if (resourceBatch.length > 0) {
|
|
215
|
+
return { kind: "resource_event", batch: resourceBatch };
|
|
216
|
+
}
|
|
217
|
+
}
|
|
182
218
|
if (context.attempt.messages.length > 0) {
|
|
183
219
|
return void 0;
|
|
184
220
|
}
|
|
@@ -192,10 +228,40 @@ async function isApiTurnWork(context) {
|
|
|
192
228
|
// src/chat/api-turns/work.ts
|
|
193
229
|
import { createHash } from "crypto";
|
|
194
230
|
import {
|
|
231
|
+
createLocalSource,
|
|
195
232
|
createWebSource,
|
|
196
233
|
localDestinationSchema
|
|
197
234
|
} from "@sentry/junior-plugin-api";
|
|
198
235
|
|
|
236
|
+
// src/chat/api-turns/accepted-reply.ts
|
|
237
|
+
async function commitWebAcceptedReply(args) {
|
|
238
|
+
const conversationMessageId = recordDeliveredAssistantMessage({
|
|
239
|
+
conversation: args.conversation,
|
|
240
|
+
sessionId: args.sessionId,
|
|
241
|
+
source: "web",
|
|
242
|
+
text: args.text,
|
|
243
|
+
userMessageId: args.userMessageId
|
|
244
|
+
});
|
|
245
|
+
try {
|
|
246
|
+
await persistWithRetry(
|
|
247
|
+
() => commitAcceptedReply({
|
|
248
|
+
...args.agentMessage ? { agentMessage: args.agentMessage } : void 0,
|
|
249
|
+
conversation: args.conversation,
|
|
250
|
+
conversationMessageId,
|
|
251
|
+
conversationId: args.conversationId
|
|
252
|
+
})
|
|
253
|
+
);
|
|
254
|
+
} catch (error) {
|
|
255
|
+
logException(
|
|
256
|
+
new Error("Accepted assistant message persistence failed"),
|
|
257
|
+
"api.assistant.message_post_delivery_persist.failed",
|
|
258
|
+
{
|
|
259
|
+
"error.type": error instanceof Error ? error.name : typeof error
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
199
265
|
// src/chat/api-turns/cancellation.ts
|
|
200
266
|
function createApiTurnCancellation() {
|
|
201
267
|
const active = /* @__PURE__ */ new Map();
|
|
@@ -217,7 +283,7 @@ function createApiTurnCancellation() {
|
|
|
217
283
|
if (!entry) {
|
|
218
284
|
return false;
|
|
219
285
|
}
|
|
220
|
-
entry.controller.abort(new
|
|
286
|
+
entry.controller.abort(new DOMException("Turn cancelled", "AbortError"));
|
|
221
287
|
return true;
|
|
222
288
|
},
|
|
223
289
|
disconnect(conversationId, signal) {
|
|
@@ -257,7 +323,7 @@ async function completeCancelledApiTurn(args) {
|
|
|
257
323
|
await abandonTurnRecord({
|
|
258
324
|
conversationId: args.conversationId,
|
|
259
325
|
turnId: args.turnId,
|
|
260
|
-
errorMessage: "
|
|
326
|
+
errorMessage: "Turn cancelled"
|
|
261
327
|
});
|
|
262
328
|
clearPendingAuth(args.conversation, args.turnId);
|
|
263
329
|
markConversationMessage(args.conversation, args.userMessageId, {
|
|
@@ -279,7 +345,9 @@ async function completeCancelledApiTurn(args) {
|
|
|
279
345
|
conversation: args.conversation,
|
|
280
346
|
sandboxRef: args.sandboxRef
|
|
281
347
|
});
|
|
282
|
-
await
|
|
348
|
+
await new ConversationTurnLifecycleService(
|
|
349
|
+
getConversationEventStore()
|
|
350
|
+
).complete({
|
|
283
351
|
conversationId: args.conversationId,
|
|
284
352
|
createdAtMs: Date.now(),
|
|
285
353
|
outcome: "cancelled",
|
|
@@ -293,6 +361,19 @@ async function completeCancelledApiTurn(args) {
|
|
|
293
361
|
await args.acknowledge();
|
|
294
362
|
}
|
|
295
363
|
|
|
364
|
+
// src/chat/api-turns/mailbox-input.ts
|
|
365
|
+
function joinMailboxText(messages) {
|
|
366
|
+
return messages.map((message) => message.input.text.trim()).filter(Boolean).join("\n\n");
|
|
367
|
+
}
|
|
368
|
+
function localResourceEventActor() {
|
|
369
|
+
return {
|
|
370
|
+
platform: "local",
|
|
371
|
+
userId: RESOURCE_EVENT_AUTHOR_ID,
|
|
372
|
+
userName: "junior-event",
|
|
373
|
+
fullName: "Junior event"
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
296
377
|
// src/chat/api-turns/work.ts
|
|
297
378
|
function normalizeEmail(email) {
|
|
298
379
|
return email.trim().toLowerCase();
|
|
@@ -504,66 +585,97 @@ function hasLostTurnInputCommit(error) {
|
|
|
504
585
|
const cause = getConversationTurnBoundaryError(error)?.cause ?? error;
|
|
505
586
|
return isTurnInputCommitLostError(error) || isTurnInputCommitLostError(cause);
|
|
506
587
|
}
|
|
507
|
-
function createApiTurnWorker(
|
|
588
|
+
function createApiTurnWorker(agentRunner, cancellation) {
|
|
508
589
|
return async (context) => {
|
|
509
590
|
const resolved = await resolveApiTurnWork(context);
|
|
510
591
|
if (!resolved) {
|
|
511
592
|
throw new Error(
|
|
512
|
-
`API
|
|
593
|
+
`Conversation API worker received other work for ${context.conversationId}`
|
|
513
594
|
);
|
|
514
595
|
}
|
|
515
596
|
if (context.publishExternally) {
|
|
516
597
|
throw new Error(
|
|
517
|
-
`API
|
|
598
|
+
`Conversation API work cannot publish to a provider for ${context.conversationId}`
|
|
518
599
|
);
|
|
519
600
|
}
|
|
520
|
-
const lifecycle =
|
|
601
|
+
const lifecycle = new ConversationTurnLifecycleService(
|
|
602
|
+
getConversationEventStore()
|
|
603
|
+
);
|
|
521
604
|
const isResume = resolved.kind === "resume";
|
|
605
|
+
let isResourceEvent = resolved.kind === "resource_event";
|
|
522
606
|
let actor;
|
|
523
|
-
let text;
|
|
524
|
-
let turnId;
|
|
525
|
-
let userMessageId;
|
|
526
|
-
let startedAtMs;
|
|
527
|
-
let inputMessageIds;
|
|
607
|
+
let text = "";
|
|
608
|
+
let turnId = "";
|
|
609
|
+
let userMessageId = "";
|
|
610
|
+
let startedAtMs = Date.now();
|
|
611
|
+
let inputMessageIds = [];
|
|
528
612
|
const storedConversation = await getConversationStore().get({
|
|
529
613
|
conversationId: context.conversationId
|
|
530
614
|
});
|
|
615
|
+
const mailboxDestination = resolved.kind === "mailbox" || resolved.kind === "resource_event" ? resolved.batch[0]?.message.destination : void 0;
|
|
616
|
+
const destination = resolveApiTurnDestination({
|
|
617
|
+
conversationId: context.conversationId,
|
|
618
|
+
existing: context.destination ?? mailboxDestination ?? storedConversation?.destination
|
|
619
|
+
});
|
|
620
|
+
const sourceFor = (resourceEvent) => resourceEvent ? createLocalSource(context.conversationId) : webSourceForConversation({
|
|
621
|
+
conversationId: context.conversationId,
|
|
622
|
+
visibility: storedConversation?.visibility
|
|
623
|
+
});
|
|
624
|
+
let source = sourceFor(isResourceEvent);
|
|
531
625
|
if (resolved.kind === "mailbox") {
|
|
532
626
|
const first = resolved.batch[0];
|
|
533
|
-
text = resolved.batch.map((entry) => entry.message
|
|
627
|
+
text = joinMailboxText(resolved.batch.map((entry) => entry.message));
|
|
534
628
|
actor = actorFromMetadata(first.metadata);
|
|
535
629
|
turnId = apiTurnIdForMessage(first.metadata.messageId);
|
|
536
630
|
userMessageId = first.metadata.messageId;
|
|
537
631
|
startedAtMs = first.message.createdAtMs;
|
|
538
632
|
inputMessageIds = resolved.batch.map((entry) => entry.metadata.messageId);
|
|
633
|
+
} else if (resolved.kind === "resource_event") {
|
|
634
|
+
const first = resolved.batch[0];
|
|
635
|
+
text = joinMailboxText(resolved.batch.map((entry) => entry.message));
|
|
636
|
+
actor = localResourceEventActor();
|
|
637
|
+
turnId = apiTurnIdForMessage(first.message.inboundMessageId);
|
|
638
|
+
userMessageId = first.message.inboundMessageId;
|
|
639
|
+
startedAtMs = first.message.createdAtMs;
|
|
640
|
+
inputMessageIds = resolved.batch.map(
|
|
641
|
+
(entry) => entry.message.inboundMessageId
|
|
642
|
+
);
|
|
539
643
|
} else {
|
|
540
644
|
turnId = resolved.turnId;
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
645
|
+
actor = actorFromStoredConversation(storedConversation?.actor);
|
|
646
|
+
}
|
|
647
|
+
const persisted = await getPersistedThreadState(context.conversationId);
|
|
648
|
+
const conversation = coerceThreadConversationState(persisted);
|
|
649
|
+
await hydrateConversationMessages({
|
|
650
|
+
conversation,
|
|
651
|
+
conversationId: context.conversationId
|
|
652
|
+
});
|
|
653
|
+
if (isResume) {
|
|
654
|
+
const userMessage = getTurnUserMessage(conversation, turnId);
|
|
655
|
+
if (!userMessage) {
|
|
545
656
|
throw new Error(
|
|
546
|
-
`
|
|
657
|
+
`Unable to locate the persisted user message for Turn "${turnId}"`
|
|
547
658
|
);
|
|
548
659
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
660
|
+
if (isResourceEventConversationMessage(userMessage)) {
|
|
661
|
+
isResourceEvent = true;
|
|
662
|
+
actor = localResourceEventActor();
|
|
663
|
+
source = sourceFor(true);
|
|
664
|
+
}
|
|
665
|
+
userMessageId = userMessage.id;
|
|
666
|
+
text = userMessage.text;
|
|
667
|
+
startedAtMs = userMessage.createdAtMs;
|
|
668
|
+
inputMessageIds = [userMessageId];
|
|
669
|
+
}
|
|
670
|
+
if (!actor) {
|
|
671
|
+
throw new Error(
|
|
672
|
+
`Conversation API resume missing actor for ${context.conversationId}`
|
|
673
|
+
);
|
|
554
674
|
}
|
|
555
|
-
const destination = resolveApiTurnDestination({
|
|
556
|
-
conversationId: context.conversationId,
|
|
557
|
-
existing: context.destination ?? (resolved.kind === "mailbox" ? resolved.batch[0]?.message.destination : void 0) ?? storedConversation?.destination
|
|
558
|
-
});
|
|
559
|
-
const source = webSourceForConversation({
|
|
560
|
-
conversationId: context.conversationId,
|
|
561
|
-
visibility: storedConversation?.visibility
|
|
562
|
-
});
|
|
563
675
|
return await withLogContext(
|
|
564
676
|
{
|
|
565
677
|
conversationId: context.conversationId,
|
|
566
|
-
platform: "web",
|
|
678
|
+
platform: isResourceEvent ? "local" : "web",
|
|
567
679
|
userId: actor.userId,
|
|
568
680
|
...actor.userName ? { userName: actor.userName } : void 0
|
|
569
681
|
},
|
|
@@ -577,25 +689,19 @@ function createApiTurnWorker(options) {
|
|
|
577
689
|
await context.attempt.ack();
|
|
578
690
|
} catch {
|
|
579
691
|
throw new TurnInputCommitLostError(
|
|
580
|
-
`Conversation work lease lost before API
|
|
692
|
+
`Conversation work lease lost before Conversation API message acknowledgement for ${context.conversationId}`
|
|
581
693
|
);
|
|
582
694
|
}
|
|
583
695
|
acknowledged = true;
|
|
584
696
|
};
|
|
585
|
-
const persisted = await getPersistedThreadState(context.conversationId);
|
|
586
|
-
const conversation = coerceThreadConversationState(persisted);
|
|
587
|
-
await hydrateConversationMessages({
|
|
588
|
-
conversation,
|
|
589
|
-
conversationId: context.conversationId
|
|
590
|
-
});
|
|
591
697
|
let sandboxRef = getPersistedSandboxState(persisted);
|
|
592
698
|
const initialSandboxRef = sandboxRef;
|
|
593
|
-
const appSignal =
|
|
699
|
+
const appSignal = cancellation?.signal(context.conversationId);
|
|
594
700
|
const stopSignal = context.stopSignal?.();
|
|
595
701
|
const cancellationSignal = appSignal && stopSignal ? AbortSignal.any([appSignal, stopSignal]) : appSignal ?? stopSignal;
|
|
596
702
|
const finishCancellation = () => {
|
|
597
703
|
if (appSignal) {
|
|
598
|
-
|
|
704
|
+
cancellation?.finish(context.conversationId, appSignal);
|
|
599
705
|
}
|
|
600
706
|
};
|
|
601
707
|
const completeCancelledTurn = async () => {
|
|
@@ -603,10 +709,9 @@ function createApiTurnWorker(options) {
|
|
|
603
709
|
await completeCancelledApiTurn({
|
|
604
710
|
acknowledge,
|
|
605
711
|
actorId: actor.userId,
|
|
606
|
-
cancellation
|
|
712
|
+
cancellation,
|
|
607
713
|
conversation,
|
|
608
714
|
conversationId: context.conversationId,
|
|
609
|
-
lifecycle,
|
|
610
715
|
sandboxRef,
|
|
611
716
|
signal: appSignal,
|
|
612
717
|
turnId,
|
|
@@ -621,16 +726,6 @@ function createApiTurnWorker(options) {
|
|
|
621
726
|
return { status: "completed" };
|
|
622
727
|
};
|
|
623
728
|
if (isResume) {
|
|
624
|
-
const userMessage = getTurnUserMessage(conversation, turnId);
|
|
625
|
-
if (!userMessage) {
|
|
626
|
-
throw new Error(
|
|
627
|
-
`Unable to locate the persisted user message for API turn "${turnId}"`
|
|
628
|
-
);
|
|
629
|
-
}
|
|
630
|
-
userMessageId = userMessage.id;
|
|
631
|
-
text = userMessage.text;
|
|
632
|
-
startedAtMs = userMessage.createdAtMs;
|
|
633
|
-
inputMessageIds = [userMessageId];
|
|
634
729
|
if (cancellationSignal?.aborted) {
|
|
635
730
|
return await completeCancelledTurn();
|
|
636
731
|
}
|
|
@@ -649,7 +744,7 @@ function createApiTurnWorker(options) {
|
|
|
649
744
|
meta: {
|
|
650
745
|
explicitMention: true,
|
|
651
746
|
replied: false,
|
|
652
|
-
source: "web"
|
|
747
|
+
...isResourceEvent ? void 0 : { source: "web" }
|
|
653
748
|
}
|
|
654
749
|
});
|
|
655
750
|
await persistConversationMessages({
|
|
@@ -676,37 +771,23 @@ function createApiTurnWorker(options) {
|
|
|
676
771
|
let failureCode = "persistence_failed";
|
|
677
772
|
let modelFailureEventId;
|
|
678
773
|
let modelFailureCaptureAttempted = false;
|
|
679
|
-
let
|
|
774
|
+
let completedSuccessfully = false;
|
|
680
775
|
const deliverAssistantMessage = async (value) => {
|
|
776
|
+
const agentMessage = typeof value === "string" ? void 0 : value;
|
|
681
777
|
const replyText = typeof value === "string" ? value : getAssistantReplyText(value);
|
|
682
778
|
if (!replyText?.trim()) {
|
|
683
779
|
return;
|
|
684
780
|
}
|
|
685
781
|
failureCode = "delivery_failed";
|
|
686
782
|
assistantMessageDelivered = true;
|
|
687
|
-
|
|
783
|
+
await commitWebAcceptedReply({
|
|
784
|
+
...agentMessage ? { agentMessage } : void 0,
|
|
688
785
|
conversation,
|
|
786
|
+
conversationId: context.conversationId,
|
|
689
787
|
sessionId: turnId,
|
|
690
|
-
source: "web",
|
|
691
788
|
text: replyText,
|
|
692
789
|
userMessageId
|
|
693
790
|
});
|
|
694
|
-
try {
|
|
695
|
-
await persistWithRetry(
|
|
696
|
-
() => persistConversationMessages({
|
|
697
|
-
conversation,
|
|
698
|
-
conversationId: context.conversationId
|
|
699
|
-
})
|
|
700
|
-
);
|
|
701
|
-
} catch (error) {
|
|
702
|
-
logException(
|
|
703
|
-
new Error("Accepted assistant message persistence failed"),
|
|
704
|
-
"api.assistant.message_post_delivery_persist.failed",
|
|
705
|
-
{
|
|
706
|
-
"error.type": error instanceof Error ? error.name : typeof error
|
|
707
|
-
}
|
|
708
|
-
);
|
|
709
|
-
}
|
|
710
791
|
failureCode = "agent_run_failed";
|
|
711
792
|
};
|
|
712
793
|
try {
|
|
@@ -751,53 +832,112 @@ function createApiTurnWorker(options) {
|
|
|
751
832
|
failureCode = "agent_run_failed";
|
|
752
833
|
currentRunId = `api-run:${stableHex(turnId, String(startedAtMs))}`;
|
|
753
834
|
setTags({ runId: currentRunId });
|
|
754
|
-
const outcome = await
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
durability: {
|
|
782
|
-
onInputCommitted: acknowledge,
|
|
783
|
-
shouldYield: context.shouldYield,
|
|
784
|
-
onSandboxRefChanged: async (nextSandboxRef) => {
|
|
785
|
-
sandboxRef = nextSandboxRef;
|
|
786
|
-
await persistThreadStateById(context.conversationId, {
|
|
787
|
-
conversation,
|
|
788
|
-
sandboxRef
|
|
789
|
-
});
|
|
835
|
+
const outcome = await executeTurn(
|
|
836
|
+
agentRunner,
|
|
837
|
+
{
|
|
838
|
+
conversationId: context.conversationId,
|
|
839
|
+
turnId,
|
|
840
|
+
runId: currentRunId,
|
|
841
|
+
instruction: {
|
|
842
|
+
text,
|
|
843
|
+
context: buildConversationContext(conversation, {
|
|
844
|
+
excludeMessageId: userMessageId
|
|
845
|
+
})
|
|
846
|
+
},
|
|
847
|
+
history: piMessages,
|
|
848
|
+
actor,
|
|
849
|
+
credentialContext: credentialContextForActor(actor),
|
|
850
|
+
destination,
|
|
851
|
+
publishExternally: false,
|
|
852
|
+
source,
|
|
853
|
+
surface: "api",
|
|
854
|
+
...cancellationSignal ? { signal: cancellationSignal } : void 0,
|
|
855
|
+
authorization: createWebAuthorization({
|
|
856
|
+
actorId: actor.userId,
|
|
857
|
+
conversationId: context.conversationId
|
|
858
|
+
}),
|
|
859
|
+
state: {
|
|
860
|
+
pendingAuth: conversation.processing.pendingAuth,
|
|
861
|
+
sandboxRef
|
|
790
862
|
},
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
863
|
+
delivery: deliverAssistantMessage,
|
|
864
|
+
durability: {
|
|
865
|
+
onInputCommitted: acknowledge,
|
|
866
|
+
shouldYield: context.shouldYield,
|
|
867
|
+
onSandboxRefChanged: async (nextSandboxRef) => {
|
|
868
|
+
sandboxRef = nextSandboxRef;
|
|
869
|
+
await persistThreadStateById(context.conversationId, {
|
|
870
|
+
conversation,
|
|
871
|
+
sandboxRef
|
|
872
|
+
});
|
|
873
|
+
},
|
|
874
|
+
recordPendingAuth: async (pendingAuth) => {
|
|
875
|
+
conversation.processing.pendingAuth = pendingAuth;
|
|
876
|
+
await persistThreadStateById(context.conversationId, {
|
|
877
|
+
conversation,
|
|
878
|
+
sandboxRef
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
},
|
|
883
|
+
async (result) => {
|
|
884
|
+
if (cancellationSignal?.aborted) {
|
|
885
|
+
throw cancellationSignal.reason ?? new DOMException("Turn cancelled", "AbortError");
|
|
886
|
+
}
|
|
887
|
+
finishCancellation();
|
|
888
|
+
modelFailureCaptureAttempted = result.diagnostics.outcome !== "success";
|
|
889
|
+
const finalized = finalizeFailedTurnReplyWithEvent({
|
|
890
|
+
reply: result,
|
|
891
|
+
logException
|
|
892
|
+
});
|
|
893
|
+
const modelFailureReason = finalized.failureReason;
|
|
894
|
+
const reply = finalized.reply;
|
|
895
|
+
modelFailureEventId = finalized.eventId;
|
|
896
|
+
if (reply.diagnostics.outcome !== "success") {
|
|
897
|
+
await deliverAssistantMessage(reply.text);
|
|
898
|
+
}
|
|
899
|
+
const completedState = buildDeliveredTurnStatePatch({
|
|
900
|
+
conversation,
|
|
901
|
+
reply,
|
|
902
|
+
sessionId: turnId,
|
|
903
|
+
userMessageId
|
|
904
|
+
});
|
|
905
|
+
await persistThreadStateById(context.conversationId, {
|
|
906
|
+
conversation: completedState.conversation,
|
|
907
|
+
sandboxRef: reply.sandboxRef ?? sandboxRef
|
|
908
|
+
});
|
|
909
|
+
if (reply.piMessages?.length) {
|
|
910
|
+
const latest = await getTurnRecord(
|
|
911
|
+
context.conversationId,
|
|
912
|
+
turnId
|
|
913
|
+
);
|
|
914
|
+
await saveTurnCheckpoint({
|
|
915
|
+
mode: "completed",
|
|
916
|
+
conversationId: context.conversationId,
|
|
917
|
+
turnId,
|
|
918
|
+
sliceId: latest?.sliceId ?? 1,
|
|
919
|
+
messages: reply.piMessages,
|
|
920
|
+
durationMs: reply.diagnostics.durationMs,
|
|
921
|
+
usage: reply.diagnostics.usage,
|
|
922
|
+
destination,
|
|
923
|
+
publishExternally: false,
|
|
924
|
+
source,
|
|
925
|
+
actor,
|
|
926
|
+
surface: "api"
|
|
796
927
|
});
|
|
797
928
|
}
|
|
929
|
+
completedSuccessfully = reply.diagnostics.outcome === "success";
|
|
930
|
+
return completedSuccessfully ? {
|
|
931
|
+
outcome: assistantMessageDelivered ? "success" : "no_reply"
|
|
932
|
+
} : {
|
|
933
|
+
...modelFailureEventId ? { eventId: modelFailureEventId } : void 0,
|
|
934
|
+
failureCode: "model_execution_failed",
|
|
935
|
+
...modelFailureReason ? { failureReason: modelFailureReason } : void 0,
|
|
936
|
+
outcome: "failed"
|
|
937
|
+
};
|
|
798
938
|
}
|
|
799
|
-
|
|
800
|
-
if (cancellationSignal?.aborted) {
|
|
939
|
+
);
|
|
940
|
+
if (outcome.status !== "completed" && cancellationSignal?.aborted) {
|
|
801
941
|
return await completeCancelledTurn();
|
|
802
942
|
}
|
|
803
943
|
if (outcome.status === "suspended") {
|
|
@@ -813,57 +953,12 @@ function createApiTurnWorker(options) {
|
|
|
813
953
|
sandboxRef
|
|
814
954
|
});
|
|
815
955
|
if (appSignal) {
|
|
816
|
-
|
|
956
|
+
cancellation?.park(context.conversationId, appSignal);
|
|
817
957
|
}
|
|
818
958
|
await acknowledge();
|
|
819
959
|
return { status: "paused" };
|
|
820
960
|
}
|
|
821
|
-
|
|
822
|
-
reply = outcome.result;
|
|
823
|
-
modelFailureCaptureAttempted = reply.diagnostics.outcome !== "success";
|
|
824
|
-
const finalized = finalizeFailedTurnReplyWithEvent({
|
|
825
|
-
reply,
|
|
826
|
-
logException
|
|
827
|
-
});
|
|
828
|
-
reply = finalized.reply;
|
|
829
|
-
modelFailureEventId = finalized.eventId;
|
|
830
|
-
if (reply.diagnostics.outcome !== "success") {
|
|
831
|
-
await deliverAssistantMessage(reply.text);
|
|
832
|
-
}
|
|
833
|
-
const completedState = buildDeliveredTurnStatePatch({
|
|
834
|
-
conversation,
|
|
835
|
-
reply,
|
|
836
|
-
sessionId: turnId,
|
|
837
|
-
userMessageId
|
|
838
|
-
});
|
|
839
|
-
await persistThreadStateById(context.conversationId, {
|
|
840
|
-
conversation: completedState.conversation,
|
|
841
|
-
sandboxRef: reply.sandboxRef ?? sandboxRef
|
|
842
|
-
});
|
|
843
|
-
if (reply.piMessages?.length) {
|
|
844
|
-
const latest = await getTurnRecord(context.conversationId, turnId);
|
|
845
|
-
await saveTurnCheckpoint({
|
|
846
|
-
mode: "completed",
|
|
847
|
-
conversationId: context.conversationId,
|
|
848
|
-
turnId,
|
|
849
|
-
sliceId: latest?.sliceId ?? 1,
|
|
850
|
-
messages: reply.piMessages,
|
|
851
|
-
durationMs: reply.diagnostics.durationMs,
|
|
852
|
-
usage: reply.diagnostics.usage,
|
|
853
|
-
destination,
|
|
854
|
-
publishExternally: false,
|
|
855
|
-
source,
|
|
856
|
-
actor,
|
|
857
|
-
surface: "api"
|
|
858
|
-
});
|
|
859
|
-
}
|
|
860
|
-
if (reply.diagnostics.outcome === "success") {
|
|
861
|
-
await lifecycle.complete({
|
|
862
|
-
conversationId: context.conversationId,
|
|
863
|
-
createdAtMs: Date.now(),
|
|
864
|
-
outcome: assistantMessageDelivered ? "success" : "no_reply",
|
|
865
|
-
turnId
|
|
866
|
-
});
|
|
961
|
+
if (completedSuccessfully) {
|
|
867
962
|
try {
|
|
868
963
|
await scheduleSessionCompletedPluginTasks(
|
|
869
964
|
{
|
|
@@ -895,30 +990,23 @@ function createApiTurnWorker(options) {
|
|
|
895
990
|
turnId
|
|
896
991
|
});
|
|
897
992
|
}
|
|
898
|
-
} else {
|
|
899
|
-
await lifecycle.fail({
|
|
900
|
-
conversationId: context.conversationId,
|
|
901
|
-
createdAtMs: Date.now(),
|
|
902
|
-
...modelFailureEventId ? { eventId: modelFailureEventId } : void 0,
|
|
903
|
-
failureCode: "model_execution_failed",
|
|
904
|
-
turnId
|
|
905
|
-
});
|
|
906
993
|
}
|
|
907
994
|
await acknowledge();
|
|
908
995
|
return { status: "completed" };
|
|
909
996
|
} catch (error) {
|
|
910
|
-
|
|
997
|
+
const failure = error instanceof AgentRunError ? error.cause : error;
|
|
998
|
+
if (hasLostTurnInputCommit(failure)) {
|
|
911
999
|
return { status: "lost_lease" };
|
|
912
1000
|
}
|
|
913
1001
|
if (cancellationSignal?.aborted) {
|
|
914
1002
|
return await completeCancelledTurn();
|
|
915
1003
|
}
|
|
916
1004
|
if (!context.attempt.isFinalAttempt) {
|
|
917
|
-
throw
|
|
1005
|
+
throw failure;
|
|
918
1006
|
}
|
|
919
1007
|
const failureEventId = modelFailureCaptureAttempted && failureCode === "agent_run_failed" ? modelFailureEventId : captureApiBoundaryFailure({
|
|
920
1008
|
conversationId: context.conversationId,
|
|
921
|
-
error,
|
|
1009
|
+
error: failure,
|
|
922
1010
|
failureCode,
|
|
923
1011
|
runId: currentRunId,
|
|
924
1012
|
turnId
|
|
@@ -1197,6 +1285,91 @@ async function readConversationAccessFromSql(db, conversationIds, viewer) {
|
|
|
1197
1285
|
);
|
|
1198
1286
|
}
|
|
1199
1287
|
|
|
1288
|
+
// src/chat/slack/user.ts
|
|
1289
|
+
import { z as z2 } from "zod";
|
|
1290
|
+
var slackUserInfoSchema = z2.object({
|
|
1291
|
+
ok: z2.literal(true),
|
|
1292
|
+
user: z2.object({
|
|
1293
|
+
name: z2.string().nullish(),
|
|
1294
|
+
profile: z2.object({
|
|
1295
|
+
email: z2.string().nullish(),
|
|
1296
|
+
real_name: z2.string().nullish()
|
|
1297
|
+
}).optional()
|
|
1298
|
+
})
|
|
1299
|
+
});
|
|
1300
|
+
var USER_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
1301
|
+
var userCache = /* @__PURE__ */ new Map();
|
|
1302
|
+
function userCacheKey(teamId, userId) {
|
|
1303
|
+
return `${teamId}:${userId}`;
|
|
1304
|
+
}
|
|
1305
|
+
function readFromCache(teamId, userId) {
|
|
1306
|
+
const hit = userCache.get(userCacheKey(teamId, userId));
|
|
1307
|
+
if (!hit) return null;
|
|
1308
|
+
if (hit.expiresAt < Date.now()) {
|
|
1309
|
+
userCache.delete(userCacheKey(teamId, userId));
|
|
1310
|
+
return null;
|
|
1311
|
+
}
|
|
1312
|
+
return hit.value;
|
|
1313
|
+
}
|
|
1314
|
+
function writeToCache(teamId, userId, value) {
|
|
1315
|
+
userCache.set(userCacheKey(teamId, userId), {
|
|
1316
|
+
value,
|
|
1317
|
+
expiresAt: Date.now() + USER_CACHE_TTL_MS
|
|
1318
|
+
});
|
|
1319
|
+
}
|
|
1320
|
+
async function lookupSlackUser(teamId, userId) {
|
|
1321
|
+
if (!teamId || !userId) {
|
|
1322
|
+
return null;
|
|
1323
|
+
}
|
|
1324
|
+
const cached = readFromCache(teamId, userId);
|
|
1325
|
+
if (cached) {
|
|
1326
|
+
return cached;
|
|
1327
|
+
}
|
|
1328
|
+
const token = getSlackBotToken();
|
|
1329
|
+
if (!token) {
|
|
1330
|
+
return null;
|
|
1331
|
+
}
|
|
1332
|
+
try {
|
|
1333
|
+
const response = await fetch(
|
|
1334
|
+
`https://slack.com/api/users.info?user=${encodeURIComponent(userId)}`,
|
|
1335
|
+
{
|
|
1336
|
+
headers: {
|
|
1337
|
+
authorization: `Bearer ${token}`
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
);
|
|
1341
|
+
if (!response.ok) {
|
|
1342
|
+
logWarn("slack.user.lookup.failed", {
|
|
1343
|
+
"enduser.id": userId,
|
|
1344
|
+
"app.slack.team_id": teamId,
|
|
1345
|
+
"http.response.status_code": response.status
|
|
1346
|
+
});
|
|
1347
|
+
return null;
|
|
1348
|
+
}
|
|
1349
|
+
const parsed = slackUserInfoSchema.safeParse(await response.json());
|
|
1350
|
+
if (!parsed.success) {
|
|
1351
|
+
return null;
|
|
1352
|
+
}
|
|
1353
|
+
const payload = parsed.data;
|
|
1354
|
+
const userName = payload.user.name?.trim() || void 0;
|
|
1355
|
+
const fullName = payload.user.profile?.real_name?.trim() || void 0;
|
|
1356
|
+
const result = {
|
|
1357
|
+
userName,
|
|
1358
|
+
fullName,
|
|
1359
|
+
email: payload.user.profile?.email?.trim() || void 0
|
|
1360
|
+
};
|
|
1361
|
+
writeToCache(teamId, userId, result);
|
|
1362
|
+
return result;
|
|
1363
|
+
} catch (error) {
|
|
1364
|
+
logWarn("slack.user.lookup.failed", {
|
|
1365
|
+
"enduser.id": userId,
|
|
1366
|
+
"app.slack.team_id": teamId,
|
|
1367
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
1368
|
+
});
|
|
1369
|
+
return null;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1200
1373
|
// src/chat/slack/conversation-context.ts
|
|
1201
1374
|
function normalizeConversationName(type, channelName) {
|
|
1202
1375
|
const trimmed = channelName?.trim();
|
|
@@ -1285,6 +1458,7 @@ export {
|
|
|
1285
1458
|
readHealthReport,
|
|
1286
1459
|
GET,
|
|
1287
1460
|
createVercelAttachmentStorage,
|
|
1461
|
+
lookupSlackUser,
|
|
1288
1462
|
conversationVisibilityFromSlackChannelType,
|
|
1289
1463
|
resolveSlackChannelTypeFromMessage,
|
|
1290
1464
|
resolveSlackConversationContext,
|