@sentry/junior 0.82.0 → 0.84.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-5ZRILRC3.js → agent-hooks-XDS5DIVS.js} +8 -8
- package/dist/api-reference.d.ts +1 -1
- package/dist/app.d.ts +1 -1
- package/dist/app.js +3833 -3263
- package/dist/chat/config.d.ts +1 -1
- package/dist/chat/conversations/store.d.ts +1 -1
- package/dist/chat/plugins/agent-hooks.d.ts +3 -3
- package/dist/chat/resource-events/ingest.d.ts +23 -0
- package/dist/chat/resource-events/notification.d.ts +20 -0
- package/dist/chat/resource-events/store.d.ts +79 -0
- package/dist/chat/services/pending-auth.d.ts +5 -0
- package/dist/chat/slack/client.d.ts +3 -1
- package/dist/chat/state/session-log.d.ts +4 -0
- package/dist/chat/task-execution/slack-work.d.ts +22 -0
- package/dist/chat/task-execution/state.d.ts +8 -2
- package/dist/chat/task-execution/store.d.ts +30 -8
- package/dist/chat/tools/resource-events.d.ts +19 -0
- package/dist/{chunk-PQ2U2AO3.js → chunk-AAD2D7K3.js} +11 -11
- package/dist/{chunk-ABRNFE3N.js → chunk-ALL7GG6U.js} +7 -6
- package/dist/{chunk-4XHCVBXH.js → chunk-BAPRSWNW.js} +81 -13
- package/dist/{chunk-NC6LR6U4.js → chunk-BFPOSIYV.js} +725 -169
- package/dist/{chunk-GUO4EE7L.js → chunk-CWMMGUWM.js} +1 -1
- package/dist/{chunk-YA2JCC7G.js → chunk-JUI6XIHU.js} +13 -5
- package/dist/{chunk-TR2G37II.js → chunk-KNVN4HUT.js} +8 -8
- package/dist/{chunk-X2FL5ZH5.js → chunk-MJ4E2PK7.js} +1 -1
- package/dist/{chunk-3JKTVW4R.js → chunk-MZ4DUVFD.js} +2 -2
- package/dist/{chunk-2AJ4TEKE.js → chunk-S7SC5IUJ.js} +26 -4
- package/dist/{chunk-IOBSRZK5.js → chunk-SBYMRDH7.js} +1 -1
- package/dist/{chunk-LX5GBMEP.js → chunk-ZPD5YQFZ.js} +2 -2
- package/dist/cli/chat.js +4 -4
- package/dist/cli/plugins.js +6 -6
- package/dist/cli/snapshot-warmup.js +3 -3
- package/dist/cli/upgrade.js +16 -32
- package/dist/{db-GQJKBX5W.js → db-7RRWCYF3.js} +3 -3
- package/dist/handlers/github-webhook/check-suite.d.ts +3 -0
- package/dist/handlers/github-webhook/pull-request-review.d.ts +3 -0
- package/dist/handlers/github-webhook/pull-request.d.ts +3 -0
- package/dist/handlers/github-webhook/resource.d.ts +10 -0
- package/dist/handlers/github-webhook.d.ts +15 -0
- package/dist/nitro.js +3 -3
- package/dist/reporting/conversations.d.ts +79 -2
- package/dist/reporting.d.ts +12 -4
- package/dist/reporting.js +431 -27
- package/dist/{runner-VRFJLG2M.js → runner-CJL5IKFO.js} +9 -9
- package/package.json +6 -6
package/dist/reporting.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PluginOperationalReport } from "@sentry/junior-plugin-api";
|
|
2
|
-
import { type ConversationFeed, type PluginConversationSummary, type ConversationReport, type ConversationStatsReport } from "./reporting/conversations";
|
|
3
|
-
export type { PluginConversationStatus, PluginConversations, PluginConversationSummary, ConversationActivityReport, ConversationActivityStatus, ConversationFeed, ConversationReport, ConversationReportStatus, ConversationRunReport, ConversationSubagentActivityReport, ConversationStatsItem, ConversationStatsReport, ConversationSummaryReport, ConversationSurface, ConversationToolActivityReport, ConversationUsage, RequesterIdentity, TranscriptMessage, TranscriptPart, TranscriptPartType, TranscriptRole, } from "./reporting/conversations";
|
|
2
|
+
import { type ConversationFeed, type PluginConversationSummary, type ConversationReport, type ConversationSubagentTranscriptReport, type ConversationStatsReport, type RequesterDirectoryReport, type RequesterProfileReport } from "./reporting/conversations";
|
|
3
|
+
export type { PluginConversationStatus, PluginConversations, PluginConversationSummary, ConversationActivityReport, ConversationActivityStatus, ConversationFeed, ConversationReport, ConversationReportStatus, ConversationRunReport, ConversationSubagentActivityReport, ConversationSubagentTranscriptReport, ConversationStatsItem, ConversationStatsReport, ConversationSummaryReport, ConversationSurface, ConversationToolActivityReport, ConversationUsage, RequesterActivityDayReport, RequesterDirectoryReport, RequesterIdentity, RequesterProfileReport, RequesterSummaryReport, RequesterTotalsReport, TranscriptMessage, TranscriptPart, TranscriptPartType, TranscriptRole, } from "./reporting/conversations";
|
|
4
4
|
export interface HealthReport {
|
|
5
5
|
status: "ok";
|
|
6
6
|
service: string;
|
|
@@ -49,10 +49,14 @@ export interface JuniorReporting {
|
|
|
49
49
|
getPlugins(): Promise<PluginReport[]>;
|
|
50
50
|
/** Read discovered skill names for reporting consumers. */
|
|
51
51
|
getSkills(): Promise<SkillReport[]>;
|
|
52
|
-
/**
|
|
53
|
-
|
|
52
|
+
/** List recent conversation summaries for reporting consumers. */
|
|
53
|
+
listConversations(): Promise<ConversationFeed>;
|
|
54
54
|
/** Read aggregate conversation stats for reporting consumers. */
|
|
55
55
|
getConversationStats?(): Promise<ConversationStatsReport>;
|
|
56
|
+
/** List requester profiles derived from trusted conversation requester emails. */
|
|
57
|
+
listRequesters?(): Promise<RequesterDirectoryReport>;
|
|
58
|
+
/** Read one requester profile derived from trusted conversation requester emails. */
|
|
59
|
+
getRequesterProfile?(email: string): Promise<RequesterProfileReport>;
|
|
56
60
|
/** Read recent conversation summaries without transcript payloads. */
|
|
57
61
|
listRecentConversations?(options?: {
|
|
58
62
|
limit?: number;
|
|
@@ -67,10 +71,14 @@ export interface JuniorReporting {
|
|
|
67
71
|
* source. Avoid adding fields that require Redis-only transcript internals.
|
|
68
72
|
*/
|
|
69
73
|
getConversation(conversationId: string): Promise<ConversationReport>;
|
|
74
|
+
/** Load a child-agent transcript only when an operator opens that subagent. */
|
|
75
|
+
getConversationSubagentTranscript(conversationId: string, runId: string, subagentId: string): Promise<ConversationSubagentTranscriptReport>;
|
|
70
76
|
}
|
|
71
77
|
/** Create the read-only reporting boundary used by plugins and other consumers. */
|
|
72
78
|
export declare function createJuniorReporting(): JuniorReporting & {
|
|
73
79
|
getConversationStats(): Promise<ConversationStatsReport>;
|
|
80
|
+
listRequesters(): Promise<RequesterDirectoryReport>;
|
|
81
|
+
getRequesterProfile(email: string): Promise<RequesterProfileReport>;
|
|
74
82
|
listRecentConversations(options?: {
|
|
75
83
|
limit?: number;
|
|
76
84
|
}): Promise<PluginConversationSummary[]>;
|
package/dist/reporting.js
CHANGED
|
@@ -6,24 +6,26 @@ import {
|
|
|
6
6
|
getConversationDetails,
|
|
7
7
|
getConversationDetailsForIds,
|
|
8
8
|
resolveSlackConversationContextFromThreadId
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-ZPD5YQFZ.js";
|
|
10
10
|
import {
|
|
11
11
|
buildSystemPrompt,
|
|
12
12
|
getAgentTurnSessionRecord,
|
|
13
13
|
listAgentTurnSessionSummariesForConversation,
|
|
14
14
|
loadActivityEntries
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-JUI6XIHU.js";
|
|
16
16
|
import {
|
|
17
17
|
getPluginOperationalReports
|
|
18
|
-
} from "./chunk-
|
|
19
|
-
import "./chunk-
|
|
18
|
+
} from "./chunk-KNVN4HUT.js";
|
|
19
|
+
import "./chunk-CWMMGUWM.js";
|
|
20
20
|
import {
|
|
21
21
|
getConversationStore
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-AAD2D7K3.js";
|
|
23
23
|
import "./chunk-G3E7SCME.js";
|
|
24
|
-
import
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
import {
|
|
25
|
+
getStateAdapter
|
|
26
|
+
} from "./chunk-MJ4E2PK7.js";
|
|
27
|
+
import "./chunk-BAPRSWNW.js";
|
|
28
|
+
import "./chunk-ALL7GG6U.js";
|
|
27
29
|
import {
|
|
28
30
|
canExposeConversationPayload,
|
|
29
31
|
parseSlackThreadId,
|
|
@@ -53,11 +55,15 @@ import { readFileSync } from "fs";
|
|
|
53
55
|
import path from "path";
|
|
54
56
|
|
|
55
57
|
// src/reporting/conversations.ts
|
|
58
|
+
import { z } from "zod";
|
|
56
59
|
var HUNG_TURN_PROGRESS_MS = 5 * 60 * 1e3;
|
|
57
60
|
var SAFE_METADATA_KEY_LIMIT = 20;
|
|
58
61
|
var PRIVATE_CONVERSATION_LABEL = "Private Conversation";
|
|
59
62
|
var CONVERSATION_FEED_LIMIT = 50;
|
|
60
63
|
var CONVERSATION_STATS_LIMIT = 5e3;
|
|
64
|
+
var REQUESTER_PROFILE_SAMPLE_LIMIT = 5e3;
|
|
65
|
+
var REQUESTER_PROFILE_RECENT_LIMIT = 25;
|
|
66
|
+
var REQUESTER_PROFILE_ACTIVITY_DAYS = 366;
|
|
61
67
|
var RECENT_CONVERSATION_STATS_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
62
68
|
function conversationStore(options = {}) {
|
|
63
69
|
return options.conversationStore ?? getConversationStore();
|
|
@@ -132,9 +138,6 @@ function sessionReportFromSummary(summary, nowMs = Date.now(), details) {
|
|
|
132
138
|
channelName: effectiveChannelName
|
|
133
139
|
}) ?? surfaceFallbackLabel(effectiveSurface);
|
|
134
140
|
const requesterIdentity = requesterIdentityReport(details?.originRequester) ?? sessionRequesterIdentityReport(summary.requester);
|
|
135
|
-
const sentryConversationUrl = buildSentryConversationUrl(
|
|
136
|
-
summary.conversationId
|
|
137
|
-
);
|
|
138
141
|
const sentryTraceUrl = summary.traceId ? buildSentryTraceUrl(summary.traceId) : void 0;
|
|
139
142
|
const cumulativeUsage = usageReport(summary.cumulativeUsage);
|
|
140
143
|
return {
|
|
@@ -152,7 +155,6 @@ function sessionReportFromSummary(summary, nowMs = Date.now(), details) {
|
|
|
152
155
|
...requesterIdentity ? { requesterIdentity } : {},
|
|
153
156
|
...slackThread ? { channel: slackThread.channelId } : {},
|
|
154
157
|
...channelName ? { channelName } : {},
|
|
155
|
-
...sentryConversationUrl ? { sentryConversationUrl } : {},
|
|
156
158
|
...summary.traceId ? { traceId: summary.traceId } : {},
|
|
157
159
|
...sentryTraceUrl ? { sentryTraceUrl } : {}
|
|
158
160
|
};
|
|
@@ -239,9 +241,6 @@ function applyConversationIndexMetadata(args) {
|
|
|
239
241
|
}
|
|
240
242
|
function sessionReportFromConversation(conversation, nowMs, details) {
|
|
241
243
|
const surface = details?.originSurface ?? surfaceFromSource(conversation.source, conversation.conversationId);
|
|
242
|
-
const sentryConversationUrl = buildSentryConversationUrl(
|
|
243
|
-
conversation.conversationId
|
|
244
|
-
);
|
|
245
244
|
const requesterIdentity = requesterIdentityReport(
|
|
246
245
|
details?.originRequester ?? conversation.requester
|
|
247
246
|
);
|
|
@@ -261,8 +260,7 @@ function sessionReportFromConversation(conversation, nowMs, details) {
|
|
|
261
260
|
surface,
|
|
262
261
|
...requesterIdentity ? { requesterIdentity } : {},
|
|
263
262
|
...slackThread ? { channel: slackThread.channelId } : {},
|
|
264
|
-
...channelName ? { channelName } : {}
|
|
265
|
-
...sentryConversationUrl ? { sentryConversationUrl } : {}
|
|
263
|
+
...channelName ? { channelName } : {}
|
|
266
264
|
};
|
|
267
265
|
}
|
|
268
266
|
function reportTime(value) {
|
|
@@ -405,10 +403,10 @@ function newestRun(runs) {
|
|
|
405
403
|
function recentConversationGroups(args) {
|
|
406
404
|
const startMs = args.nowMs - RECENT_CONVERSATION_STATS_WINDOW_MS;
|
|
407
405
|
const groups = /* @__PURE__ */ new Map();
|
|
408
|
-
for (const
|
|
409
|
-
groups.set(
|
|
410
|
-
...groups.get(
|
|
411
|
-
|
|
406
|
+
for (const summary of args.summaries) {
|
|
407
|
+
groups.set(summary.conversationId, [
|
|
408
|
+
...groups.get(summary.conversationId) ?? [],
|
|
409
|
+
summary
|
|
412
410
|
]);
|
|
413
411
|
}
|
|
414
412
|
return [...groups.values()].map(
|
|
@@ -498,6 +496,277 @@ function buildConversationStatsReport(args) {
|
|
|
498
496
|
).toISOString()
|
|
499
497
|
};
|
|
500
498
|
}
|
|
499
|
+
function normalizeRequesterEmail(email) {
|
|
500
|
+
const normalized = email?.trim().toLowerCase();
|
|
501
|
+
return normalized || void 0;
|
|
502
|
+
}
|
|
503
|
+
function requesterIdentityWithEmail(requester) {
|
|
504
|
+
const email = normalizeRequesterEmail(requester?.email);
|
|
505
|
+
if (!email) return void 0;
|
|
506
|
+
return {
|
|
507
|
+
email,
|
|
508
|
+
...requester?.fullName ? { fullName: requester.fullName } : {},
|
|
509
|
+
...requester?.slackUserId ? { slackUserId: requester.slackUserId } : {},
|
|
510
|
+
...requester?.slackUserName ? { slackUserName: requester.slackUserName } : {}
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
function mergeRequesterIdentity(current, next) {
|
|
514
|
+
return {
|
|
515
|
+
email: current.email,
|
|
516
|
+
...current.fullName ?? next.fullName ? { fullName: current.fullName ?? next.fullName } : {},
|
|
517
|
+
...current.slackUserId ?? next.slackUserId ? { slackUserId: current.slackUserId ?? next.slackUserId } : {},
|
|
518
|
+
...current.slackUserName ?? next.slackUserName ? { slackUserName: current.slackUserName ?? next.slackUserName } : {}
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
function reportDate(value) {
|
|
522
|
+
const time = reportTime(value);
|
|
523
|
+
if (time === void 0) return void 0;
|
|
524
|
+
return new Date(time).toISOString().slice(0, 10);
|
|
525
|
+
}
|
|
526
|
+
function emptyRequesterTotals() {
|
|
527
|
+
return {
|
|
528
|
+
active: 0,
|
|
529
|
+
activeDays: 0,
|
|
530
|
+
conversations: 0,
|
|
531
|
+
durationMs: 0,
|
|
532
|
+
failed: 0,
|
|
533
|
+
hung: 0,
|
|
534
|
+
runs: 0
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
function addRequesterTokens(target, tokens) {
|
|
538
|
+
if (tokens !== void 0) {
|
|
539
|
+
target.tokens = (target.tokens ?? 0) + tokens;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
function addStatsSignals(item, signals) {
|
|
543
|
+
item.active += signals.active ? 1 : 0;
|
|
544
|
+
item.failed += signals.failed ? 1 : 0;
|
|
545
|
+
item.hung += signals.hung ? 1 : 0;
|
|
546
|
+
}
|
|
547
|
+
function addConversationSignals(target, signals) {
|
|
548
|
+
target.active += signals.active ? 1 : 0;
|
|
549
|
+
target.failed += signals.failed ? 1 : 0;
|
|
550
|
+
target.hung += signals.hung ? 1 : 0;
|
|
551
|
+
}
|
|
552
|
+
function directoryItem(accumulator) {
|
|
553
|
+
return {
|
|
554
|
+
active: accumulator.active,
|
|
555
|
+
activeDays: accumulator.activeDates.size,
|
|
556
|
+
conversations: accumulator.conversations,
|
|
557
|
+
durationMs: accumulator.durationMs,
|
|
558
|
+
failed: accumulator.failed,
|
|
559
|
+
firstSeenAt: new Date(accumulator.firstSeenMs).toISOString(),
|
|
560
|
+
hung: accumulator.hung,
|
|
561
|
+
lastSeenAt: new Date(accumulator.lastSeenMs).toISOString(),
|
|
562
|
+
requester: accumulator.requester,
|
|
563
|
+
runs: accumulator.runs,
|
|
564
|
+
...accumulator.tokens !== void 0 ? { tokens: accumulator.tokens } : {}
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
function emptyRequesterActivityDay(date) {
|
|
568
|
+
return {
|
|
569
|
+
active: 0,
|
|
570
|
+
conversations: 0,
|
|
571
|
+
date,
|
|
572
|
+
durationMs: 0,
|
|
573
|
+
failed: 0,
|
|
574
|
+
hung: 0,
|
|
575
|
+
runs: 0
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
function requesterActivityDays(args) {
|
|
579
|
+
const items = [];
|
|
580
|
+
const end = new Date(args.nowMs);
|
|
581
|
+
end.setUTCHours(0, 0, 0, 0);
|
|
582
|
+
const start = new Date(end);
|
|
583
|
+
start.setUTCDate(start.getUTCDate() - (REQUESTER_PROFILE_ACTIVITY_DAYS - 1));
|
|
584
|
+
for (const cursor = new Date(start); cursor.getTime() <= end.getTime(); cursor.setUTCDate(cursor.getUTCDate() + 1)) {
|
|
585
|
+
const date = cursor.toISOString().slice(0, 10);
|
|
586
|
+
items.push(args.days.get(date) ?? emptyRequesterActivityDay(date));
|
|
587
|
+
}
|
|
588
|
+
return items;
|
|
589
|
+
}
|
|
590
|
+
async function requesterSample(options = {}) {
|
|
591
|
+
const conversations = await conversationStore(options).listByActivity({
|
|
592
|
+
limit: REQUESTER_PROFILE_SAMPLE_LIMIT + 1
|
|
593
|
+
});
|
|
594
|
+
return {
|
|
595
|
+
conversations: conversations.slice(0, REQUESTER_PROFILE_SAMPLE_LIMIT),
|
|
596
|
+
truncated: conversations.length > REQUESTER_PROFILE_SAMPLE_LIMIT
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
async function readRequesterDirectoryReport(options = {}) {
|
|
600
|
+
const nowMs = Date.now();
|
|
601
|
+
const { conversations, truncated } = await requesterSample(options);
|
|
602
|
+
const detailsByConversationId = await getConversationDetailsForIds(
|
|
603
|
+
conversations.map((conversation) => conversation.conversationId)
|
|
604
|
+
);
|
|
605
|
+
const reportsByConversation = await reportsFromConversations({
|
|
606
|
+
conversations,
|
|
607
|
+
detailsByConversationId,
|
|
608
|
+
nowMs
|
|
609
|
+
});
|
|
610
|
+
const people = /* @__PURE__ */ new Map();
|
|
611
|
+
for (const conversation of conversations) {
|
|
612
|
+
const reports = [
|
|
613
|
+
...reportsByConversation.get(conversation.conversationId) ?? [
|
|
614
|
+
sessionReportFromConversation(
|
|
615
|
+
conversation,
|
|
616
|
+
nowMs,
|
|
617
|
+
detailsByConversationId.get(conversation.conversationId)
|
|
618
|
+
)
|
|
619
|
+
]
|
|
620
|
+
].sort(
|
|
621
|
+
(left, right) => (reportTime(left.startedAt) ?? 0) - (reportTime(right.startedAt) ?? 0) || left.id.localeCompare(right.id)
|
|
622
|
+
);
|
|
623
|
+
const newest = newestRun(reports);
|
|
624
|
+
const requester = requesterIdentityWithEmail(newest.requesterIdentity);
|
|
625
|
+
if (!requester) continue;
|
|
626
|
+
const lastSeenMs = reportTime(newest.lastSeenAt) ?? conversation.lastActivityAtMs;
|
|
627
|
+
const firstSeenMs = reportTime(reports[0]?.startedAt ?? newest.startedAt) ?? conversation.createdAtMs;
|
|
628
|
+
const contributions = runContributions(reports);
|
|
629
|
+
const signals = statusSignals(reports);
|
|
630
|
+
const date = reportDate(newest.lastSeenAt);
|
|
631
|
+
const email = requester.email;
|
|
632
|
+
const accumulator = people.get(email) ?? {
|
|
633
|
+
...emptyRequesterTotals(),
|
|
634
|
+
activeDates: /* @__PURE__ */ new Set(),
|
|
635
|
+
firstSeenMs,
|
|
636
|
+
lastSeenMs,
|
|
637
|
+
requester
|
|
638
|
+
};
|
|
639
|
+
accumulator.requester = mergeRequesterIdentity(
|
|
640
|
+
accumulator.requester,
|
|
641
|
+
requester
|
|
642
|
+
);
|
|
643
|
+
accumulator.conversations += 1;
|
|
644
|
+
accumulator.runs += reports.length;
|
|
645
|
+
accumulator.durationMs += contributionDurationTotal(contributions);
|
|
646
|
+
addRequesterTokens(accumulator, contributionTokenTotal(contributions));
|
|
647
|
+
addConversationSignals(accumulator, signals);
|
|
648
|
+
accumulator.firstSeenMs = Math.min(accumulator.firstSeenMs, firstSeenMs);
|
|
649
|
+
accumulator.lastSeenMs = Math.max(accumulator.lastSeenMs, lastSeenMs);
|
|
650
|
+
if (date) accumulator.activeDates.add(date);
|
|
651
|
+
people.set(email, accumulator);
|
|
652
|
+
}
|
|
653
|
+
return {
|
|
654
|
+
generatedAt: new Date(nowMs).toISOString(),
|
|
655
|
+
people: [...people.values()].map(directoryItem).sort(
|
|
656
|
+
(left, right) => (reportTime(right.lastSeenAt) ?? 0) - (reportTime(left.lastSeenAt) ?? 0) || right.conversations - left.conversations || left.requester.email.localeCompare(right.requester.email)
|
|
657
|
+
),
|
|
658
|
+
sampleLimit: REQUESTER_PROFILE_SAMPLE_LIMIT,
|
|
659
|
+
sampleSize: conversations.length,
|
|
660
|
+
source: "conversation_index",
|
|
661
|
+
truncated
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
async function readRequesterProfileReport(email, options = {}) {
|
|
665
|
+
const nowMs = Date.now();
|
|
666
|
+
const normalizedEmail = normalizeRequesterEmail(email) ?? email;
|
|
667
|
+
const { conversations, truncated } = await requesterSample(options);
|
|
668
|
+
const detailsByConversationId = await getConversationDetailsForIds(
|
|
669
|
+
conversations.map((conversation) => conversation.conversationId)
|
|
670
|
+
);
|
|
671
|
+
const reportsByConversation = await reportsFromConversations({
|
|
672
|
+
conversations,
|
|
673
|
+
detailsByConversationId,
|
|
674
|
+
nowMs
|
|
675
|
+
});
|
|
676
|
+
const matchingConversations = conversations.filter((conversation) => {
|
|
677
|
+
const reports = reportsByConversation.get(conversation.conversationId) ?? [];
|
|
678
|
+
const report = reports.length > 0 ? newestRun(reports) : sessionReportFromConversation(
|
|
679
|
+
conversation,
|
|
680
|
+
nowMs,
|
|
681
|
+
detailsByConversationId.get(conversation.conversationId)
|
|
682
|
+
);
|
|
683
|
+
return requesterIdentityWithEmail(report.requesterIdentity)?.email === normalizedEmail;
|
|
684
|
+
});
|
|
685
|
+
let requester;
|
|
686
|
+
const totals = emptyRequesterTotals();
|
|
687
|
+
const activeDates = /* @__PURE__ */ new Set();
|
|
688
|
+
const activityDays = /* @__PURE__ */ new Map();
|
|
689
|
+
const locations = /* @__PURE__ */ new Map();
|
|
690
|
+
const surfaces = /* @__PURE__ */ new Map();
|
|
691
|
+
const recentConversations = [];
|
|
692
|
+
for (const conversation of matchingConversations) {
|
|
693
|
+
const reports = [
|
|
694
|
+
...reportsByConversation.get(conversation.conversationId) ?? [
|
|
695
|
+
sessionReportFromConversation(
|
|
696
|
+
conversation,
|
|
697
|
+
nowMs,
|
|
698
|
+
detailsByConversationId.get(conversation.conversationId)
|
|
699
|
+
)
|
|
700
|
+
]
|
|
701
|
+
].sort(
|
|
702
|
+
(left, right) => (reportTime(left.startedAt) ?? 0) - (reportTime(right.startedAt) ?? 0) || left.id.localeCompare(right.id)
|
|
703
|
+
);
|
|
704
|
+
const newest = newestRun(reports);
|
|
705
|
+
const identity = requesterIdentityWithEmail(newest.requesterIdentity);
|
|
706
|
+
if (identity) {
|
|
707
|
+
requester = requester ? mergeRequesterIdentity(requester, identity) : identity;
|
|
708
|
+
}
|
|
709
|
+
recentConversations.push(newest);
|
|
710
|
+
const contributions = runContributions(reports);
|
|
711
|
+
const signals = statusSignals(reports);
|
|
712
|
+
const durationMs = contributionDurationTotal(contributions);
|
|
713
|
+
const tokens = contributionTokenTotal(contributions);
|
|
714
|
+
const date = reportDate(newest.lastSeenAt);
|
|
715
|
+
totals.conversations += 1;
|
|
716
|
+
totals.runs += reports.length;
|
|
717
|
+
totals.durationMs += durationMs;
|
|
718
|
+
addRequesterTokens(totals, tokens);
|
|
719
|
+
addConversationSignals(totals, signals);
|
|
720
|
+
if (date) {
|
|
721
|
+
activeDates.add(date);
|
|
722
|
+
const day = activityDays.get(date) ?? emptyRequesterActivityDay(date);
|
|
723
|
+
day.conversations += 1;
|
|
724
|
+
day.runs += reports.length;
|
|
725
|
+
day.durationMs += durationMs;
|
|
726
|
+
addRequesterTokens(day, tokens);
|
|
727
|
+
addConversationSignals(day, signals);
|
|
728
|
+
activityDays.set(date, day);
|
|
729
|
+
}
|
|
730
|
+
const location = locationLabel(newest);
|
|
731
|
+
const locationItem = locations.get(location) ?? emptyStatsItem(location);
|
|
732
|
+
locationItem.conversations += 1;
|
|
733
|
+
locationItem.runs += reports.length;
|
|
734
|
+
locationItem.durationMs += durationMs;
|
|
735
|
+
addItemTokens(locationItem, tokens);
|
|
736
|
+
addStatsSignals(locationItem, signals);
|
|
737
|
+
locations.set(location, locationItem);
|
|
738
|
+
const surface = surfaceFallbackLabel(newest.surface);
|
|
739
|
+
const surfaceItem = surfaces.get(surface) ?? emptyStatsItem(surface);
|
|
740
|
+
surfaceItem.conversations += 1;
|
|
741
|
+
surfaceItem.runs += reports.length;
|
|
742
|
+
surfaceItem.durationMs += durationMs;
|
|
743
|
+
addItemTokens(surfaceItem, tokens);
|
|
744
|
+
addStatsSignals(surfaceItem, signals);
|
|
745
|
+
surfaces.set(surface, surfaceItem);
|
|
746
|
+
}
|
|
747
|
+
totals.activeDays = activeDates.size;
|
|
748
|
+
const end = new Date(nowMs);
|
|
749
|
+
end.setUTCHours(0, 0, 0, 0);
|
|
750
|
+
const start = new Date(end);
|
|
751
|
+
start.setUTCDate(start.getUTCDate() - (REQUESTER_PROFILE_ACTIVITY_DAYS - 1));
|
|
752
|
+
return {
|
|
753
|
+
activityDays: requesterActivityDays({ days: activityDays, nowMs }),
|
|
754
|
+
generatedAt: new Date(nowMs).toISOString(),
|
|
755
|
+
locations: statsItems(locations),
|
|
756
|
+
recentConversations: recentConversations.sort(
|
|
757
|
+
(left, right) => (reportTime(right.lastSeenAt) ?? 0) - (reportTime(left.lastSeenAt) ?? 0) || right.conversationId.localeCompare(left.conversationId)
|
|
758
|
+
).slice(0, REQUESTER_PROFILE_RECENT_LIMIT),
|
|
759
|
+
requester: requester ?? { email: normalizedEmail },
|
|
760
|
+
sampleLimit: REQUESTER_PROFILE_SAMPLE_LIMIT,
|
|
761
|
+
sampleSize: conversations.length,
|
|
762
|
+
source: "conversation_index",
|
|
763
|
+
surfaces: statsItems(surfaces),
|
|
764
|
+
totals,
|
|
765
|
+
truncated,
|
|
766
|
+
windowEnd: end.toISOString(),
|
|
767
|
+
windowStart: start.toISOString()
|
|
768
|
+
};
|
|
769
|
+
}
|
|
501
770
|
function canExposeConversationTranscript(summary) {
|
|
502
771
|
return canExposeConversationPayload({
|
|
503
772
|
conversationId: summary.conversationId
|
|
@@ -694,7 +963,8 @@ function subagentActivity(entry, options) {
|
|
|
694
963
|
...end ? {
|
|
695
964
|
endedAt: new Date(end.createdAtMs).toISOString(),
|
|
696
965
|
outcome: end.outcome,
|
|
697
|
-
status: end.outcome
|
|
966
|
+
status: end.outcome,
|
|
967
|
+
...options.canExposeTranscript && end.transcriptStartMessageIndex !== void 0 && end.transcriptEndMessageIndex !== void 0 ? { transcriptAvailable: true } : {}
|
|
698
968
|
} : { status: options.parentStatus ?? "running" }
|
|
699
969
|
};
|
|
700
970
|
}
|
|
@@ -714,6 +984,7 @@ function buildConversationActivity(args) {
|
|
|
714
984
|
}
|
|
715
985
|
const parentStatus = entry.parentToolCallId ? toolStatuses.get(entry.parentToolCallId) : void 0;
|
|
716
986
|
const activity = subagentActivity(entry, {
|
|
987
|
+
canExposeTranscript: args.canExposePayload,
|
|
717
988
|
end: subagentEnds.get(entry.subagentInvocationId),
|
|
718
989
|
parentStatus
|
|
719
990
|
});
|
|
@@ -810,6 +1081,62 @@ function traceIdFromTranscript(transcript) {
|
|
|
810
1081
|
}
|
|
811
1082
|
return void 0;
|
|
812
1083
|
}
|
|
1084
|
+
function subagentTranscriptReport(activity, options = {}) {
|
|
1085
|
+
return {
|
|
1086
|
+
type: "subagent",
|
|
1087
|
+
...options.subagentConversationId ? { subagentConversationId: options.subagentConversationId } : {},
|
|
1088
|
+
createdAt: activity.createdAt,
|
|
1089
|
+
id: activity.id,
|
|
1090
|
+
status: activity.status,
|
|
1091
|
+
...options.subagentSentryConversationUrl ? { subagentSentryConversationUrl: options.subagentSentryConversationUrl } : {},
|
|
1092
|
+
subagentKind: activity.subagentKind,
|
|
1093
|
+
transcript: options.transcript ?? [],
|
|
1094
|
+
transcriptAvailable: Boolean(options.transcript?.length),
|
|
1095
|
+
...activity.endedAt ? { endedAt: activity.endedAt } : {},
|
|
1096
|
+
...activity.outcome ? { outcome: activity.outcome } : {},
|
|
1097
|
+
...activity.parentToolCallId ? { parentToolCallId: activity.parentToolCallId } : {},
|
|
1098
|
+
...options.transcriptMessageCount !== void 0 ? { transcriptMessageCount: options.transcriptMessageCount } : {},
|
|
1099
|
+
...options.transcriptRedacted ? { transcriptRedacted: options.transcriptRedacted } : {},
|
|
1100
|
+
...options.transcriptRedactionReason ? { transcriptRedactionReason: options.transcriptRedactionReason } : {},
|
|
1101
|
+
...options.unavailableReason ? { unavailableReason: options.unavailableReason } : {}
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
function subagentConversationFields(ref) {
|
|
1105
|
+
if (ref.type !== "advisor_session") {
|
|
1106
|
+
return {};
|
|
1107
|
+
}
|
|
1108
|
+
const subagentConversationId = ref.key;
|
|
1109
|
+
const subagentSentryConversationUrl = buildSentryConversationUrl(
|
|
1110
|
+
subagentConversationId
|
|
1111
|
+
);
|
|
1112
|
+
return {
|
|
1113
|
+
subagentConversationId,
|
|
1114
|
+
...subagentSentryConversationUrl ? { subagentSentryConversationUrl } : {}
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
var piMessageSchema = z.object({
|
|
1118
|
+
content: z.array(z.unknown()),
|
|
1119
|
+
role: z.string().min(1)
|
|
1120
|
+
}).passthrough().transform((value) => value);
|
|
1121
|
+
async function readTranscriptRefMessages(ref) {
|
|
1122
|
+
if (ref.type !== "advisor_session") {
|
|
1123
|
+
return [];
|
|
1124
|
+
}
|
|
1125
|
+
const stateAdapter = getStateAdapter();
|
|
1126
|
+
await stateAdapter.connect();
|
|
1127
|
+
const value = await stateAdapter.get(ref.key);
|
|
1128
|
+
const parsed = z.array(piMessageSchema).safeParse(value);
|
|
1129
|
+
return parsed.success ? parsed.data : [];
|
|
1130
|
+
}
|
|
1131
|
+
function transcriptSliceBounds(end) {
|
|
1132
|
+
if (end?.transcriptStartMessageIndex === void 0 || end.transcriptEndMessageIndex === void 0 || end.transcriptEndMessageIndex < end.transcriptStartMessageIndex) {
|
|
1133
|
+
return void 0;
|
|
1134
|
+
}
|
|
1135
|
+
return {
|
|
1136
|
+
end: end.transcriptEndMessageIndex,
|
|
1137
|
+
start: end.transcriptStartMessageIndex
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
813
1140
|
async function summariesByConversation(conversations) {
|
|
814
1141
|
const entries = await Promise.all(
|
|
815
1142
|
conversations.map(async (conversation) => {
|
|
@@ -858,7 +1185,7 @@ async function readConversationFeed(options = {}) {
|
|
|
858
1185
|
return {
|
|
859
1186
|
source: "conversation_index",
|
|
860
1187
|
generatedAt: new Date(nowMs).toISOString(),
|
|
861
|
-
|
|
1188
|
+
conversations: conversations.map(
|
|
862
1189
|
(conversation) => newestRun(
|
|
863
1190
|
reportsByConversation.get(conversation.conversationId) ?? [
|
|
864
1191
|
sessionReportFromConversation(
|
|
@@ -880,7 +1207,7 @@ async function readConversationStatsReport(options = {}) {
|
|
|
880
1207
|
});
|
|
881
1208
|
const truncated = conversations.length > CONVERSATION_STATS_LIMIT;
|
|
882
1209
|
const sampledConversations = conversations.slice(0, CONVERSATION_STATS_LIMIT);
|
|
883
|
-
const
|
|
1210
|
+
const summaries = sampledConversations.map(
|
|
884
1211
|
(conversation) => sessionReportFromConversation(conversation, nowMs)
|
|
885
1212
|
);
|
|
886
1213
|
return buildConversationStatsReport({
|
|
@@ -888,7 +1215,7 @@ async function readConversationStatsReport(options = {}) {
|
|
|
888
1215
|
nowMs,
|
|
889
1216
|
sampleLimit: CONVERSATION_STATS_LIMIT,
|
|
890
1217
|
sampleSize: sampledConversations.length,
|
|
891
|
-
|
|
1218
|
+
summaries,
|
|
892
1219
|
truncated
|
|
893
1220
|
});
|
|
894
1221
|
}
|
|
@@ -1008,13 +1335,83 @@ async function readConversationReport(conversationId, options = {}) {
|
|
|
1008
1335
|
];
|
|
1009
1336
|
const firstRun = effectiveRuns[0];
|
|
1010
1337
|
const displayTitle = firstRun?.displayTitle ?? displayTitleFromDetails(conversationId, details) ?? surfaceFallbackLabel(firstRun?.surface ?? "slack");
|
|
1338
|
+
const sentryConversationUrl = buildSentryConversationUrl(conversationId);
|
|
1011
1339
|
return {
|
|
1012
1340
|
conversationId,
|
|
1013
1341
|
displayTitle,
|
|
1014
1342
|
generatedAt: new Date(nowMs).toISOString(),
|
|
1343
|
+
...sentryConversationUrl ? { sentryConversationUrl } : {},
|
|
1015
1344
|
runs: effectiveRuns
|
|
1016
1345
|
};
|
|
1017
1346
|
}
|
|
1347
|
+
async function readConversationSubagentTranscriptReport(conversationId, runId, subagentId) {
|
|
1348
|
+
const summaries = await listAgentTurnSessionSummariesForConversation(conversationId);
|
|
1349
|
+
const summary = summaries.find((candidate) => candidate.sessionId === runId);
|
|
1350
|
+
if (!summary) {
|
|
1351
|
+
return {
|
|
1352
|
+
type: "subagent",
|
|
1353
|
+
createdAt: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
1354
|
+
id: subagentId,
|
|
1355
|
+
status: "error",
|
|
1356
|
+
subagentKind: "unknown",
|
|
1357
|
+
transcript: [],
|
|
1358
|
+
transcriptAvailable: false,
|
|
1359
|
+
unavailableReason: "not_found"
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
const entries = await loadActivityEntries({
|
|
1363
|
+
conversationId,
|
|
1364
|
+
sessionId: runId
|
|
1365
|
+
});
|
|
1366
|
+
const start = entries.find(
|
|
1367
|
+
(entry) => entry.type === "subagent_started" && entry.subagentInvocationId === subagentId
|
|
1368
|
+
);
|
|
1369
|
+
const end = entries.find(
|
|
1370
|
+
(entry) => entry.type === "subagent_ended" && entry.subagentInvocationId === subagentId
|
|
1371
|
+
);
|
|
1372
|
+
if (!start) {
|
|
1373
|
+
return {
|
|
1374
|
+
type: "subagent",
|
|
1375
|
+
createdAt: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
1376
|
+
id: subagentId,
|
|
1377
|
+
status: "error",
|
|
1378
|
+
subagentKind: "unknown",
|
|
1379
|
+
transcript: [],
|
|
1380
|
+
transcriptAvailable: false,
|
|
1381
|
+
unavailableReason: "not_found"
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
const canExposeTranscript = canExposeConversationTranscript(summary);
|
|
1385
|
+
const activity = subagentActivity(start, { canExposeTranscript, end });
|
|
1386
|
+
const conversationFields = subagentConversationFields(start.transcriptRef);
|
|
1387
|
+
if (!canExposeTranscript) {
|
|
1388
|
+
return subagentTranscriptReport(activity, {
|
|
1389
|
+
...conversationFields,
|
|
1390
|
+
transcriptRedacted: true,
|
|
1391
|
+
transcriptRedactionReason: "non_public_conversation"
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
const bounds = transcriptSliceBounds(end);
|
|
1395
|
+
if (!bounds) {
|
|
1396
|
+
return subagentTranscriptReport(activity, {
|
|
1397
|
+
...conversationFields,
|
|
1398
|
+
unavailableReason: "missing_transcript_range"
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1401
|
+
const messages = await readTranscriptRefMessages(start.transcriptRef);
|
|
1402
|
+
if (messages.length === 0) {
|
|
1403
|
+
return subagentTranscriptReport(activity, {
|
|
1404
|
+
...conversationFields,
|
|
1405
|
+
unavailableReason: "missing_transcript_ref"
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
const transcript = messages.slice(0, bounds.end).map(normalizeTranscriptMessage);
|
|
1409
|
+
return subagentTranscriptReport(activity, {
|
|
1410
|
+
...conversationFields,
|
|
1411
|
+
transcript,
|
|
1412
|
+
transcriptMessageCount: countConversationMessages(transcript)
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1018
1415
|
|
|
1019
1416
|
// src/reporting.ts
|
|
1020
1417
|
function readDescriptionText() {
|
|
@@ -1068,8 +1465,10 @@ function createJuniorReporting() {
|
|
|
1068
1465
|
},
|
|
1069
1466
|
getPlugins: readPlugins,
|
|
1070
1467
|
getSkills: readSkills,
|
|
1071
|
-
|
|
1468
|
+
listConversations: () => readConversationFeed({ conversationStore: conversationStore2 }),
|
|
1072
1469
|
getConversationStats: () => readConversationStatsReport({ conversationStore: conversationStore2 }),
|
|
1470
|
+
listRequesters: () => readRequesterDirectoryReport({ conversationStore: conversationStore2 }),
|
|
1471
|
+
getRequesterProfile: (email) => readRequesterProfileReport(email, { conversationStore: conversationStore2 }),
|
|
1073
1472
|
listRecentConversations: listRecent,
|
|
1074
1473
|
getPluginOperationalReports: async () => {
|
|
1075
1474
|
const nowMs = Date.now();
|
|
@@ -1081,7 +1480,12 @@ function createJuniorReporting() {
|
|
|
1081
1480
|
})
|
|
1082
1481
|
};
|
|
1083
1482
|
},
|
|
1084
|
-
getConversation: (conversationId) => readConversationReport(conversationId, { conversationStore: conversationStore2 })
|
|
1483
|
+
getConversation: (conversationId) => readConversationReport(conversationId, { conversationStore: conversationStore2 }),
|
|
1484
|
+
getConversationSubagentTranscript: (conversationId, runId, subagentId) => readConversationSubagentTranscriptReport(
|
|
1485
|
+
conversationId,
|
|
1486
|
+
runId,
|
|
1487
|
+
subagentId
|
|
1488
|
+
)
|
|
1085
1489
|
};
|
|
1086
1490
|
}
|
|
1087
1491
|
export {
|
|
@@ -14,26 +14,26 @@ import {
|
|
|
14
14
|
startActiveTurn,
|
|
15
15
|
updateConversationStats,
|
|
16
16
|
upsertConversationMessage
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-BFPOSIYV.js";
|
|
18
18
|
import {
|
|
19
19
|
coerceThreadConversationState
|
|
20
20
|
} from "./chunk-66NX7MNW.js";
|
|
21
|
-
import "./chunk-
|
|
21
|
+
import "./chunk-MZ4DUVFD.js";
|
|
22
22
|
import "./chunk-KNFROR7R.js";
|
|
23
23
|
import {
|
|
24
24
|
commitMessages,
|
|
25
25
|
loadProjection
|
|
26
|
-
} from "./chunk-
|
|
27
|
-
import "./chunk-
|
|
28
|
-
import "./chunk-
|
|
29
|
-
import "./chunk-
|
|
26
|
+
} from "./chunk-JUI6XIHU.js";
|
|
27
|
+
import "./chunk-KNVN4HUT.js";
|
|
28
|
+
import "./chunk-CWMMGUWM.js";
|
|
29
|
+
import "./chunk-AAD2D7K3.js";
|
|
30
30
|
import "./chunk-G3E7SCME.js";
|
|
31
|
-
import "./chunk-
|
|
32
|
-
import "./chunk-
|
|
31
|
+
import "./chunk-MJ4E2PK7.js";
|
|
32
|
+
import "./chunk-BAPRSWNW.js";
|
|
33
33
|
import {
|
|
34
34
|
stripRuntimeTurnContext,
|
|
35
35
|
trimTrailingAssistantMessages
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-ALL7GG6U.js";
|
|
37
37
|
import "./chunk-DR75T7J3.js";
|
|
38
38
|
import "./chunk-WCXVEQPI.js";
|
|
39
39
|
import "./chunk-TW23AT22.js";
|