@sentry/junior 0.57.0 → 0.58.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/app.js +1301 -1278
- package/dist/chat/agent-dispatch/types.d.ts +0 -1
- package/dist/chat/conversation-privacy.d.ts +23 -0
- package/dist/chat/logging.d.ts +2 -0
- package/dist/chat/mcp/tool-manager.d.ts +18 -5
- package/dist/chat/mcp/tool-name.d.ts +2 -0
- package/dist/chat/pi/client.d.ts +2 -0
- package/dist/chat/pi/derived-state.d.ts +5 -0
- package/dist/chat/pi/traced-stream.d.ts +5 -1
- package/dist/chat/prompt.d.ts +3 -9
- package/dist/chat/respond-helpers.d.ts +5 -3
- package/dist/chat/respond.d.ts +1 -0
- package/dist/chat/runtime/conversation-message.d.ts +10 -0
- package/dist/chat/runtime/processing-reaction.d.ts +2 -4
- package/dist/chat/runtime/reply-executor.d.ts +13 -16
- package/dist/chat/runtime/slack-runtime.d.ts +19 -32
- package/dist/chat/runtime/thread-state.d.ts +1 -1
- package/dist/chat/runtime/turn-input.d.ts +29 -0
- package/dist/chat/runtime/turn-preparation.d.ts +4 -24
- package/dist/chat/runtime/turn.d.ts +2 -3
- package/dist/chat/sentry-links.d.ts +4 -0
- package/dist/chat/services/context-compaction.d.ts +3 -4
- package/dist/chat/services/pending-auth.d.ts +1 -1
- package/dist/chat/services/subscribed-reply-policy.d.ts +2 -13
- package/dist/chat/services/timeout-resume.d.ts +1 -2
- package/dist/chat/services/turn-session-record.d.ts +82 -0
- package/dist/chat/slack/assistant-thread/title.d.ts +4 -1
- package/dist/chat/state/artifacts.d.ts +1 -0
- package/dist/chat/state/conversation.d.ts +0 -1
- package/dist/chat/state/session-log.d.ts +117 -0
- package/dist/chat/state/ttl.d.ts +2 -0
- package/dist/chat/state/turn-session.d.ts +89 -0
- package/dist/chat/tools/advisor/tool.d.ts +2 -0
- package/dist/chat/tools/agent-tools.d.ts +2 -1
- package/dist/chat/tools/skill/call-mcp-tool.d.ts +7 -3
- package/dist/chat/tools/skill/search-mcp-tools.d.ts +15 -3
- package/dist/chat/tools/types.d.ts +0 -1
- package/dist/{chunk-AA5TIFN5.js → chunk-FKEKRBUB.js} +267 -735
- package/dist/{chunk-TTUY467K.js → chunk-H652GMDH.js} +30 -14
- package/dist/chunk-I4FDGMFI.js +950 -0
- package/dist/{chunk-D3G3YOU4.js → chunk-ITOW4DED.js} +1 -1
- package/dist/chunk-QDGD5WVN.js +708 -0
- package/dist/cli/check.js +2 -2
- package/dist/cli/init.js +0 -1
- package/dist/cli/snapshot-warmup.js +5 -3
- package/dist/instrumentation.js +3 -0
- package/dist/reporting.d.ts +113 -0
- package/dist/reporting.js +390 -0
- package/package.json +25 -11
- package/dist/chat/services/turn-checkpoint.d.ts +0 -74
- package/dist/chat/state/pi-session-message-store.d.ts +0 -15
- package/dist/chat/state/turn-session-store.d.ts +0 -49
- package/dist/handlers/diagnostics-dashboard.d.ts +0 -2
- package/dist/handlers/diagnostics.d.ts +0 -2
package/dist/cli/check.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseSkillFile
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-ITOW4DED.js";
|
|
4
4
|
import {
|
|
5
5
|
parsePluginManifest
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-H652GMDH.js";
|
|
7
7
|
import "../chunk-Z3YD6NHK.js";
|
|
8
8
|
import "../chunk-5LUISFEY.js";
|
|
9
9
|
import "../chunk-2KG3PWR4.js";
|
package/dist/cli/init.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
-
disconnectStateAdapter,
|
|
3
2
|
resolveRuntimeDependencySnapshot
|
|
4
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-QDGD5WVN.js";
|
|
4
|
+
import {
|
|
5
|
+
disconnectStateAdapter
|
|
6
|
+
} from "../chunk-FKEKRBUB.js";
|
|
5
7
|
import {
|
|
6
8
|
getPluginProviders,
|
|
7
9
|
getPluginRuntimeDependencies,
|
|
8
10
|
getPluginRuntimePostinstall
|
|
9
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-H652GMDH.js";
|
|
10
12
|
import "../chunk-Z3YD6NHK.js";
|
|
11
13
|
import "../chunk-5LUISFEY.js";
|
|
12
14
|
import "../chunk-2KG3PWR4.js";
|
package/dist/instrumentation.js
CHANGED
|
@@ -17,6 +17,9 @@ function getBoolean(value, fallback) {
|
|
|
17
17
|
return fallback;
|
|
18
18
|
}
|
|
19
19
|
function initSentry() {
|
|
20
|
+
if (sentry_exports.getClient()) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
20
23
|
const dsn = process.env.SENTRY_DSN;
|
|
21
24
|
const enableLogs = getBoolean(process.env.SENTRY_ENABLE_LOGS, Boolean(dsn));
|
|
22
25
|
sentry_exports.init({
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { AgentTurnUsage } from "@/chat/usage";
|
|
2
|
+
import { getPluginPackageContent } from "@/chat/plugins/registry";
|
|
3
|
+
import { type AgentTurnRequester } from "@/chat/state/turn-session";
|
|
4
|
+
export interface HealthReport {
|
|
5
|
+
status: "ok";
|
|
6
|
+
service: string;
|
|
7
|
+
timestamp: string;
|
|
8
|
+
}
|
|
9
|
+
export interface PluginReport {
|
|
10
|
+
name: string;
|
|
11
|
+
}
|
|
12
|
+
export interface SkillReport {
|
|
13
|
+
name: string;
|
|
14
|
+
pluginProvider?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface RuntimeInfoReport {
|
|
17
|
+
cwd: string;
|
|
18
|
+
homeDir: string;
|
|
19
|
+
descriptionText?: string;
|
|
20
|
+
providers: string[];
|
|
21
|
+
skills: SkillReport[];
|
|
22
|
+
packagedContent: ReturnType<typeof getPluginPackageContent>;
|
|
23
|
+
}
|
|
24
|
+
export interface DashboardSessionReport {
|
|
25
|
+
conversationTitle?: string;
|
|
26
|
+
cumulativeDurationMs?: number;
|
|
27
|
+
cumulativeUsage?: AgentTurnUsage;
|
|
28
|
+
conversationId: string;
|
|
29
|
+
id: string;
|
|
30
|
+
status: "active" | "completed" | "failed" | "hung" | "superseded";
|
|
31
|
+
startedAt: string;
|
|
32
|
+
lastSeenAt: string;
|
|
33
|
+
lastProgressAt: string;
|
|
34
|
+
completedAt?: string;
|
|
35
|
+
surface?: "slack" | "api" | "scheduler" | "internal";
|
|
36
|
+
title?: string;
|
|
37
|
+
requester?: string;
|
|
38
|
+
requesterIdentity?: AgentTurnRequester;
|
|
39
|
+
channel?: string;
|
|
40
|
+
channelName?: string;
|
|
41
|
+
sentryConversationUrl?: string;
|
|
42
|
+
sentryTraceUrl?: string;
|
|
43
|
+
traceId?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface DashboardTranscriptPart {
|
|
46
|
+
bytes?: number;
|
|
47
|
+
chars?: number;
|
|
48
|
+
id?: string;
|
|
49
|
+
input?: unknown;
|
|
50
|
+
inputKeys?: string[];
|
|
51
|
+
inputSizeBytes?: number;
|
|
52
|
+
inputSizeChars?: number;
|
|
53
|
+
inputType?: string;
|
|
54
|
+
name?: string;
|
|
55
|
+
output?: unknown;
|
|
56
|
+
outputKeys?: string[];
|
|
57
|
+
outputSizeBytes?: number;
|
|
58
|
+
outputSizeChars?: number;
|
|
59
|
+
outputType?: string;
|
|
60
|
+
redacted?: boolean;
|
|
61
|
+
text?: string;
|
|
62
|
+
type: string;
|
|
63
|
+
}
|
|
64
|
+
export interface DashboardTranscriptMessage {
|
|
65
|
+
parts: DashboardTranscriptPart[];
|
|
66
|
+
role: string;
|
|
67
|
+
timestamp?: number;
|
|
68
|
+
}
|
|
69
|
+
export interface DashboardTurnReport extends DashboardSessionReport {
|
|
70
|
+
transcriptAvailable: boolean;
|
|
71
|
+
transcriptMetadata?: DashboardTranscriptMessage[];
|
|
72
|
+
transcriptMessageCount?: number;
|
|
73
|
+
transcriptRedacted?: boolean;
|
|
74
|
+
transcriptRedactionReason?: "non_public_conversation";
|
|
75
|
+
transcript: DashboardTranscriptMessage[];
|
|
76
|
+
}
|
|
77
|
+
export interface DashboardConversationReport {
|
|
78
|
+
conversationId: string;
|
|
79
|
+
generatedAt: string;
|
|
80
|
+
turns: DashboardTurnReport[];
|
|
81
|
+
}
|
|
82
|
+
export interface DashboardSessionFeed {
|
|
83
|
+
sessions: DashboardSessionReport[];
|
|
84
|
+
source: "turn_session_records";
|
|
85
|
+
generatedAt: string;
|
|
86
|
+
}
|
|
87
|
+
export interface JuniorReporting {
|
|
88
|
+
/** Read the public runtime health snapshot without exposing discovery data. */
|
|
89
|
+
getHealth(): Promise<HealthReport>;
|
|
90
|
+
/** Read authenticated dashboard runtime discovery data. */
|
|
91
|
+
getRuntimeInfo(): Promise<RuntimeInfoReport>;
|
|
92
|
+
/** Read configured plugin names for authenticated dashboard views. */
|
|
93
|
+
getPlugins(): Promise<PluginReport[]>;
|
|
94
|
+
/** Read discovered skill names for authenticated dashboard views. */
|
|
95
|
+
getSkills(): Promise<SkillReport[]>;
|
|
96
|
+
/**
|
|
97
|
+
* Read recent turn metadata for authenticated dashboard views.
|
|
98
|
+
*
|
|
99
|
+
* Keep this API trace-shaped: callers should rely on timestamps, status,
|
|
100
|
+
* actor, route, usage, and links that can later be reconstructed from spans.
|
|
101
|
+
*/
|
|
102
|
+
getSessions(): Promise<DashboardSessionFeed>;
|
|
103
|
+
/**
|
|
104
|
+
* Read one conversation transcript for the dashboard.
|
|
105
|
+
*
|
|
106
|
+
* The current implementation joins turn-session records with expiring session
|
|
107
|
+
* logs, but the API should stay compatible with a future Sentry trace-history
|
|
108
|
+
* source. Avoid adding fields that require Redis-only transcript internals.
|
|
109
|
+
*/
|
|
110
|
+
getConversation(conversationId: string): Promise<DashboardConversationReport>;
|
|
111
|
+
}
|
|
112
|
+
/** Create the read-only reporting boundary used by authenticated dashboard routes. */
|
|
113
|
+
export declare function createJuniorReporting(): JuniorReporting;
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GET,
|
|
3
|
+
buildSentryConversationUrl,
|
|
4
|
+
buildSentryTraceUrl,
|
|
5
|
+
getAgentTurnSessionRecord,
|
|
6
|
+
listAgentTurnSessionSummaries,
|
|
7
|
+
listAgentTurnSessionSummariesForConversation
|
|
8
|
+
} from "./chunk-I4FDGMFI.js";
|
|
9
|
+
import {
|
|
10
|
+
discoverSkills
|
|
11
|
+
} from "./chunk-ITOW4DED.js";
|
|
12
|
+
import {
|
|
13
|
+
canExposeConversationPayload,
|
|
14
|
+
parseSlackThreadId,
|
|
15
|
+
resolveConversationPrivacy
|
|
16
|
+
} from "./chunk-FKEKRBUB.js";
|
|
17
|
+
import {
|
|
18
|
+
getPluginPackageContent,
|
|
19
|
+
getPluginProviders,
|
|
20
|
+
isRecord
|
|
21
|
+
} from "./chunk-H652GMDH.js";
|
|
22
|
+
import "./chunk-Z3YD6NHK.js";
|
|
23
|
+
import {
|
|
24
|
+
homeDir
|
|
25
|
+
} from "./chunk-5LUISFEY.js";
|
|
26
|
+
import "./chunk-2KG3PWR4.js";
|
|
27
|
+
|
|
28
|
+
// src/reporting.ts
|
|
29
|
+
import { readFileSync } from "fs";
|
|
30
|
+
import path from "path";
|
|
31
|
+
var HUNG_TURN_PROGRESS_MS = 5 * 60 * 1e3;
|
|
32
|
+
var SAFE_METADATA_KEY_LIMIT = 20;
|
|
33
|
+
function readDescriptionText() {
|
|
34
|
+
try {
|
|
35
|
+
const raw = readFileSync(
|
|
36
|
+
path.join(homeDir(), "DESCRIPTION.md"),
|
|
37
|
+
"utf8"
|
|
38
|
+
).trim();
|
|
39
|
+
return raw || void 0;
|
|
40
|
+
} catch {
|
|
41
|
+
return void 0;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function readHealth() {
|
|
45
|
+
const res = GET();
|
|
46
|
+
return await res.json();
|
|
47
|
+
}
|
|
48
|
+
async function readSkills() {
|
|
49
|
+
const skills = await discoverSkills();
|
|
50
|
+
return skills.map((skill) => ({
|
|
51
|
+
name: skill.name,
|
|
52
|
+
pluginProvider: skill.pluginProvider
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
async function readPlugins() {
|
|
56
|
+
return getPluginProviders().map((plugin) => ({
|
|
57
|
+
name: plugin.manifest.name
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
function statusFromCheckpoint(summary) {
|
|
61
|
+
const state = summary.state;
|
|
62
|
+
if (state === "running" && Date.now() - summary.lastProgressAtMs > HUNG_TURN_PROGRESS_MS) {
|
|
63
|
+
return "hung";
|
|
64
|
+
}
|
|
65
|
+
if (state === "running" || state === "awaiting_resume") {
|
|
66
|
+
return "active";
|
|
67
|
+
}
|
|
68
|
+
if (state === "abandoned") {
|
|
69
|
+
return "superseded";
|
|
70
|
+
}
|
|
71
|
+
return state;
|
|
72
|
+
}
|
|
73
|
+
function surfaceFromConversationId(conversationId) {
|
|
74
|
+
return parseSlackThreadId(conversationId) ? "slack" : "internal";
|
|
75
|
+
}
|
|
76
|
+
function titleFromSummary(summary) {
|
|
77
|
+
if (summary.state === "awaiting_resume" && summary.resumeReason) {
|
|
78
|
+
return `Awaiting ${summary.resumeReason} resume`;
|
|
79
|
+
}
|
|
80
|
+
return `Turn ${summary.sessionId}`;
|
|
81
|
+
}
|
|
82
|
+
function requesterLabel(requester) {
|
|
83
|
+
if (!requester) return void 0;
|
|
84
|
+
return requester.email ?? requester.slackUserName ?? requester.fullName ?? requester.slackUserId;
|
|
85
|
+
}
|
|
86
|
+
function safePrivateLabel(summary) {
|
|
87
|
+
const slackThread = parseSlackThreadId(summary.conversationId);
|
|
88
|
+
if (slackThread?.channelId.startsWith("D")) {
|
|
89
|
+
return "Direct Message";
|
|
90
|
+
}
|
|
91
|
+
if (slackThread?.channelId.startsWith("G")) {
|
|
92
|
+
return summary.channelName?.startsWith("mpdm-") ? "Group DM" : "Private Channel";
|
|
93
|
+
}
|
|
94
|
+
return "Private Channel";
|
|
95
|
+
}
|
|
96
|
+
function sessionReportFromSummary(summary) {
|
|
97
|
+
const slackThread = parseSlackThreadId(summary.conversationId);
|
|
98
|
+
const privacy = resolveConversationPrivacy({
|
|
99
|
+
conversationId: summary.conversationId
|
|
100
|
+
});
|
|
101
|
+
const privateLabel = privacy !== "public" ? safePrivateLabel(summary) : void 0;
|
|
102
|
+
const conversationTitle = privateLabel ?? summary.conversationTitle;
|
|
103
|
+
const channelName = privateLabel ?? summary.channelName;
|
|
104
|
+
const requester = requesterLabel(summary.requester);
|
|
105
|
+
const sentryConversationUrl = buildSentryConversationUrl(
|
|
106
|
+
summary.conversationId
|
|
107
|
+
);
|
|
108
|
+
const sentryTraceUrl = summary.traceId ? buildSentryTraceUrl(summary.traceId) : void 0;
|
|
109
|
+
return {
|
|
110
|
+
conversationId: summary.conversationId,
|
|
111
|
+
...conversationTitle ? { conversationTitle } : {},
|
|
112
|
+
id: summary.sessionId,
|
|
113
|
+
status: statusFromCheckpoint(summary),
|
|
114
|
+
startedAt: new Date(summary.startedAtMs).toISOString(),
|
|
115
|
+
lastProgressAt: new Date(summary.lastProgressAtMs).toISOString(),
|
|
116
|
+
lastSeenAt: new Date(summary.updatedAtMs).toISOString(),
|
|
117
|
+
...summary.state === "completed" ? { completedAt: new Date(summary.updatedAtMs).toISOString() } : {},
|
|
118
|
+
...summary.cumulativeDurationMs !== void 0 ? { cumulativeDurationMs: summary.cumulativeDurationMs } : {},
|
|
119
|
+
...summary.cumulativeUsage ? { cumulativeUsage: summary.cumulativeUsage } : {},
|
|
120
|
+
surface: surfaceFromConversationId(summary.conversationId),
|
|
121
|
+
title: titleFromSummary(summary),
|
|
122
|
+
...requester ? { requester } : {},
|
|
123
|
+
...summary.requester ? { requesterIdentity: summary.requester } : {},
|
|
124
|
+
...slackThread ? { channel: slackThread.channelId } : {},
|
|
125
|
+
...channelName ? { channelName } : {},
|
|
126
|
+
...sentryConversationUrl ? { sentryConversationUrl } : {},
|
|
127
|
+
...summary.traceId ? { traceId: summary.traceId } : {},
|
|
128
|
+
...sentryTraceUrl ? { sentryTraceUrl } : {}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function canExposeConversationTranscript(summary) {
|
|
132
|
+
return canExposeConversationPayload({
|
|
133
|
+
conversationId: summary.conversationId
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
function textPart(text) {
|
|
137
|
+
return { type: "text", text };
|
|
138
|
+
}
|
|
139
|
+
function recordField(value, names) {
|
|
140
|
+
for (const name of names) {
|
|
141
|
+
if (value[name] !== void 0) {
|
|
142
|
+
return value[name];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return void 0;
|
|
146
|
+
}
|
|
147
|
+
function normalizeTranscriptPart(part) {
|
|
148
|
+
if (typeof part === "string") {
|
|
149
|
+
return textPart(part);
|
|
150
|
+
}
|
|
151
|
+
if (!isRecord(part)) {
|
|
152
|
+
return { type: "unknown", output: part };
|
|
153
|
+
}
|
|
154
|
+
const rawType = typeof part.type === "string" ? part.type : "unknown";
|
|
155
|
+
if (rawType === "text") {
|
|
156
|
+
const text = recordField(part, ["text", "content"]);
|
|
157
|
+
return textPart(
|
|
158
|
+
typeof text === "string" ? text : JSON.stringify(text) ?? ""
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
if (rawType === "toolCall") {
|
|
162
|
+
return {
|
|
163
|
+
type: "tool_call",
|
|
164
|
+
...typeof part.id === "string" ? { id: part.id } : {},
|
|
165
|
+
...typeof part.name === "string" ? { name: part.name } : {},
|
|
166
|
+
input: recordField(part, ["arguments", "input", "args"])
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (rawType === "toolResult") {
|
|
170
|
+
return {
|
|
171
|
+
type: "tool_result",
|
|
172
|
+
...typeof part.id === "string" ? { id: part.id } : {},
|
|
173
|
+
...typeof part.name === "string" ? { name: part.name } : {},
|
|
174
|
+
output: recordField(part, ["result", "output", "content"])
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (rawType === "thinking") {
|
|
178
|
+
return {
|
|
179
|
+
type: "thinking",
|
|
180
|
+
output: recordField(part, ["thinking", "text", "content", "output"])
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
type: rawType,
|
|
185
|
+
output: part
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function normalizeToolResultMessage(record) {
|
|
189
|
+
const content = record.content;
|
|
190
|
+
let output = content;
|
|
191
|
+
if (Array.isArray(content) && content.length === 1 && isRecord(content[0])) {
|
|
192
|
+
const extracted = recordField(content[0], [
|
|
193
|
+
"text",
|
|
194
|
+
"content",
|
|
195
|
+
"output",
|
|
196
|
+
"result"
|
|
197
|
+
]);
|
|
198
|
+
output = extracted !== void 0 ? extracted : content;
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
type: "tool_result",
|
|
202
|
+
...typeof record.toolCallId === "string" ? { id: record.toolCallId } : {},
|
|
203
|
+
...typeof record.name === "string" ? { name: record.name } : typeof record.toolName === "string" ? { name: record.toolName } : {},
|
|
204
|
+
output
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function normalizeTranscriptMessage(message) {
|
|
208
|
+
const record = message;
|
|
209
|
+
const content = record.content;
|
|
210
|
+
const role = typeof record.role === "string" ? record.role : "unknown";
|
|
211
|
+
return {
|
|
212
|
+
role,
|
|
213
|
+
...typeof record.timestamp === "number" ? { timestamp: record.timestamp } : {},
|
|
214
|
+
parts: role === "toolResult" ? [normalizeToolResultMessage(record)] : Array.isArray(content) ? content.map(normalizeTranscriptPart) : [normalizeTranscriptPart(content)]
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function serializedChars(value) {
|
|
218
|
+
if (typeof value === "string") return value.length;
|
|
219
|
+
return JSON.stringify(value)?.length ?? 0;
|
|
220
|
+
}
|
|
221
|
+
function serializedBytes(value) {
|
|
222
|
+
const serialized = typeof value === "string" ? value : JSON.stringify(value);
|
|
223
|
+
return new TextEncoder().encode(serialized ?? "").byteLength;
|
|
224
|
+
}
|
|
225
|
+
function payloadType(value) {
|
|
226
|
+
return Array.isArray(value) ? "array" : typeof value;
|
|
227
|
+
}
|
|
228
|
+
function payloadKeys(value) {
|
|
229
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
230
|
+
return void 0;
|
|
231
|
+
}
|
|
232
|
+
const keys = Object.keys(value).slice(
|
|
233
|
+
0,
|
|
234
|
+
SAFE_METADATA_KEY_LIMIT
|
|
235
|
+
);
|
|
236
|
+
return keys.length > 0 ? keys : void 0;
|
|
237
|
+
}
|
|
238
|
+
function redactedPayloadFields(prefix, value) {
|
|
239
|
+
const keys = payloadKeys(value);
|
|
240
|
+
return {
|
|
241
|
+
[`${prefix}Type`]: payloadType(value),
|
|
242
|
+
[`${prefix}SizeBytes`]: serializedBytes(value),
|
|
243
|
+
[`${prefix}SizeChars`]: serializedChars(value),
|
|
244
|
+
...keys ? { [`${prefix}Keys`]: keys } : {}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function redactTranscriptPart(part) {
|
|
248
|
+
if (part.type === "text") {
|
|
249
|
+
return {
|
|
250
|
+
type: "text",
|
|
251
|
+
redacted: true,
|
|
252
|
+
bytes: serializedBytes(part.text ?? ""),
|
|
253
|
+
chars: serializedChars(part.text ?? "")
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
if (part.type === "thinking") {
|
|
257
|
+
return {
|
|
258
|
+
type: "thinking",
|
|
259
|
+
redacted: true,
|
|
260
|
+
...redactedPayloadFields("output", part.output)
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
if (part.type === "tool_call") {
|
|
264
|
+
return {
|
|
265
|
+
type: "tool_call",
|
|
266
|
+
redacted: true,
|
|
267
|
+
...part.id ? { id: part.id } : {},
|
|
268
|
+
...part.name ? { name: part.name } : {},
|
|
269
|
+
...redactedPayloadFields("input", part.input)
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
if (part.type === "tool_result") {
|
|
273
|
+
return {
|
|
274
|
+
type: "tool_result",
|
|
275
|
+
redacted: true,
|
|
276
|
+
...part.id ? { id: part.id } : {},
|
|
277
|
+
...part.name ? { name: part.name } : {},
|
|
278
|
+
...redactedPayloadFields("output", part.output)
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
return {
|
|
282
|
+
type: part.type,
|
|
283
|
+
redacted: true,
|
|
284
|
+
...redactedPayloadFields("output", part.output ?? part.input ?? part.text)
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function redactTranscriptMessage(message) {
|
|
288
|
+
return {
|
|
289
|
+
role: message.role,
|
|
290
|
+
...typeof message.timestamp === "number" ? { timestamp: message.timestamp } : {},
|
|
291
|
+
parts: message.parts.map(redactTranscriptPart)
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
function turnScopedMessages(messages) {
|
|
295
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
296
|
+
const record = messages[index];
|
|
297
|
+
if (record.role === "user") {
|
|
298
|
+
return messages.slice(index);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return messages;
|
|
302
|
+
}
|
|
303
|
+
function traceIdFromTranscript(transcript) {
|
|
304
|
+
for (const message of transcript) {
|
|
305
|
+
for (const part of message.parts) {
|
|
306
|
+
const text = part.text ?? (typeof part.output === "string" ? part.output : typeof part.input === "string" ? part.input : void 0);
|
|
307
|
+
const match = text?.match(
|
|
308
|
+
/\btrace[_-]?id["']?\s*[:=]\s*["']?([a-f0-9]{16,32})\b/i
|
|
309
|
+
);
|
|
310
|
+
if (match?.[1]) {
|
|
311
|
+
return match[1];
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return void 0;
|
|
316
|
+
}
|
|
317
|
+
async function readSessions() {
|
|
318
|
+
const summaries = await listAgentTurnSessionSummaries(50);
|
|
319
|
+
return {
|
|
320
|
+
source: "turn_session_records",
|
|
321
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
322
|
+
sessions: summaries.map(sessionReportFromSummary)
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
async function readConversation(conversationId) {
|
|
326
|
+
const summaries = (await listAgentTurnSessionSummariesForConversation(conversationId)).sort(
|
|
327
|
+
(left, right) => left.startedAtMs - right.startedAtMs || left.updatedAtMs - right.updatedAtMs || left.sessionId.localeCompare(right.sessionId)
|
|
328
|
+
);
|
|
329
|
+
const turns = await Promise.all(
|
|
330
|
+
summaries.map(async (summary) => {
|
|
331
|
+
const sessionRecord = await getAgentTurnSessionRecord(
|
|
332
|
+
summary.conversationId,
|
|
333
|
+
summary.sessionId
|
|
334
|
+
);
|
|
335
|
+
const scopedMessages = sessionRecord?.piMessages ? turnScopedMessages(sessionRecord.piMessages) : [];
|
|
336
|
+
const canExposeTranscript = canExposeConversationTranscript(summary);
|
|
337
|
+
const normalizedTranscript = scopedMessages.map(
|
|
338
|
+
normalizeTranscriptMessage
|
|
339
|
+
);
|
|
340
|
+
const transcript = canExposeTranscript ? normalizedTranscript : [];
|
|
341
|
+
const transcriptMetadata = canExposeTranscript ? void 0 : normalizedTranscript.map(redactTranscriptMessage);
|
|
342
|
+
const traceId = summary.traceId ?? sessionRecord?.traceId ?? (canExposeTranscript ? traceIdFromTranscript(transcript) : void 0);
|
|
343
|
+
const sentryTraceUrl = traceId ? buildSentryTraceUrl(traceId) : void 0;
|
|
344
|
+
return {
|
|
345
|
+
...sessionReportFromSummary(summary),
|
|
346
|
+
...traceId ? { traceId } : {},
|
|
347
|
+
...sentryTraceUrl ? { sentryTraceUrl } : {},
|
|
348
|
+
transcriptAvailable: Boolean(sessionRecord) && canExposeTranscript,
|
|
349
|
+
...sessionRecord && scopedMessages.length > 0 ? { transcriptMessageCount: scopedMessages.length } : {},
|
|
350
|
+
...!canExposeTranscript ? {
|
|
351
|
+
transcriptMetadata,
|
|
352
|
+
transcriptRedacted: true,
|
|
353
|
+
transcriptRedactionReason: "non_public_conversation"
|
|
354
|
+
} : {},
|
|
355
|
+
transcript
|
|
356
|
+
};
|
|
357
|
+
})
|
|
358
|
+
);
|
|
359
|
+
return {
|
|
360
|
+
conversationId,
|
|
361
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
362
|
+
turns
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
function createJuniorReporting() {
|
|
366
|
+
return {
|
|
367
|
+
getHealth: readHealth,
|
|
368
|
+
async getRuntimeInfo() {
|
|
369
|
+
const [plugins, skills] = await Promise.all([
|
|
370
|
+
readPlugins(),
|
|
371
|
+
readSkills()
|
|
372
|
+
]);
|
|
373
|
+
return {
|
|
374
|
+
cwd: process.cwd(),
|
|
375
|
+
homeDir: homeDir(),
|
|
376
|
+
descriptionText: readDescriptionText(),
|
|
377
|
+
providers: plugins.map((plugin) => plugin.name),
|
|
378
|
+
skills,
|
|
379
|
+
packagedContent: getPluginPackageContent()
|
|
380
|
+
};
|
|
381
|
+
},
|
|
382
|
+
getPlugins: readPlugins,
|
|
383
|
+
getSkills: readSkills,
|
|
384
|
+
getSessions: readSessions,
|
|
385
|
+
getConversation: readConversation
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
export {
|
|
389
|
+
createJuniorReporting
|
|
390
|
+
};
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.58.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
|
+
"types": "./dist/app.d.ts",
|
|
9
10
|
"repository": {
|
|
10
11
|
"type": "git",
|
|
11
12
|
"url": "git+https://github.com/getsentry/junior.git",
|
|
@@ -15,10 +16,26 @@
|
|
|
15
16
|
"junior": "bin/junior.mjs"
|
|
16
17
|
},
|
|
17
18
|
"exports": {
|
|
18
|
-
".":
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/app.d.ts",
|
|
21
|
+
"default": "./dist/app.js"
|
|
22
|
+
},
|
|
23
|
+
"./instrumentation": {
|
|
24
|
+
"types": "./dist/instrumentation.d.ts",
|
|
25
|
+
"default": "./dist/instrumentation.js"
|
|
26
|
+
},
|
|
27
|
+
"./nitro": {
|
|
28
|
+
"types": "./dist/nitro.d.ts",
|
|
29
|
+
"default": "./dist/nitro.js"
|
|
30
|
+
},
|
|
31
|
+
"./reporting": {
|
|
32
|
+
"types": "./dist/reporting.d.ts",
|
|
33
|
+
"default": "./dist/reporting.js"
|
|
34
|
+
},
|
|
35
|
+
"./vercel": {
|
|
36
|
+
"types": "./dist/vercel.d.ts",
|
|
37
|
+
"default": "./dist/vercel.js"
|
|
38
|
+
}
|
|
22
39
|
},
|
|
23
40
|
"files": [
|
|
24
41
|
"dist",
|
|
@@ -35,6 +52,7 @@
|
|
|
35
52
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
36
53
|
"@sinclair/typebox": "^0.34.49",
|
|
37
54
|
"@slack/web-api": "^7.16.0",
|
|
55
|
+
"@sentry/node": "10.53.1",
|
|
38
56
|
"@vercel/functions": "^3.6.0",
|
|
39
57
|
"@vercel/sandbox": "2.0.0",
|
|
40
58
|
"ai": "^6.0.190",
|
|
@@ -46,13 +64,9 @@
|
|
|
46
64
|
"node-html-markdown": "^2.0.0",
|
|
47
65
|
"yaml": "^2.9.0",
|
|
48
66
|
"zod": "^4.4.3",
|
|
49
|
-
"@sentry/junior-plugin-api": "0.
|
|
50
|
-
},
|
|
51
|
-
"peerDependencies": {
|
|
52
|
-
"@sentry/node": ">=10.0.0"
|
|
67
|
+
"@sentry/junior-plugin-api": "0.58.0"
|
|
53
68
|
},
|
|
54
69
|
"devDependencies": {
|
|
55
|
-
"@sentry/node": "10.53.1",
|
|
56
70
|
"@types/node": "^25.9.1",
|
|
57
71
|
"dependency-cruiser": "^17.4.0",
|
|
58
72
|
"msw": "^2.14.6",
|
|
@@ -62,7 +76,7 @@
|
|
|
62
76
|
"typescript": "^6.0.3",
|
|
63
77
|
"vercel": "^54.4.0",
|
|
64
78
|
"vitest": "^4.1.7",
|
|
65
|
-
"@sentry/junior-scheduler": "0.
|
|
79
|
+
"@sentry/junior-scheduler": "0.58.0"
|
|
66
80
|
},
|
|
67
81
|
"scripts": {
|
|
68
82
|
"build": "tsup && tsc -p tsconfig.build.json --emitDeclarationOnly",
|