@sentry/junior 0.205.0 → 0.206.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-D4nu8MQV.js → agent-C9yuiZUF.js} +247 -67
- package/dist/{agent-hooks-DXHpTJ4m.js → agent-hooks-DNjlz2A-.js} +1078 -12
- package/dist/{agent-runner-C04colea.js → agent-runner-E67lFrDG.js} +5 -5
- package/dist/api/schema.d.ts +77 -0
- package/dist/api/schema.js +2 -2
- package/dist/api.js +44 -511
- package/dist/{app-Cb776lL8.d.ts → app-AxGmJHwK.d.ts} +7 -0
- package/dist/app.d.ts +1 -1
- package/dist/app.js +50 -80
- package/dist/{auth-pause-state-C2P3guS1.js → auth-pause-state-MVZ6xvEK.js} +6 -6
- package/dist/briefs-CvJFQvhJ.js +398 -0
- package/dist/{catalog-runtime-CXw3asJD.js → catalog-runtime-TBR8n4e1.js} +1 -1
- package/dist/{checkpoint-CrViJ8Ps.js → checkpoint-BY47jJCB.js} +7 -6
- package/dist/cli/chat.js +14 -12
- package/dist/cli/check.js +3 -3
- package/dist/cli/init.js +1 -0
- package/dist/cli/main.js +5 -1
- package/dist/cli/plugins.js +3 -3
- package/dist/cli/run.d.ts +2 -1
- package/dist/cli/run.js +2 -1
- package/dist/cli/snapshot-create.js +3 -3
- package/dist/cli/upgrade.js +3 -3
- package/dist/{config-BOtJQ7Pc.js → client-ceE7NtvC.js} +4 -367
- package/dist/config-CIE83sR2.js +386 -0
- package/dist/{schema-Rn8-XEaN.js → conversation-AA4R1VUn.js} +580 -558
- package/dist/{conversation-privacy-BZRA6Pes.js → conversation-privacy-Auyq26XQ.js} +1 -1
- package/dist/{credential-sync-Bep34IB_.js → credential-sync-BO33aNQd.js} +20 -19
- package/dist/{execution-stats-B6lgWxCK.js → execution-stats-SiS2FLcv.js} +4 -4
- package/dist/{executor-BlUhFL7_.js → executor-BfbYPW63.js} +31 -3
- package/dist/instrumentation.js +2 -2
- package/dist/{job-queue-DLMqIK74.js → job-queue-CyTc24Es.js} +1 -1
- package/dist/{logging-BMJzdDlP.js → logging-D37bayDc.js} +1 -1
- package/dist/nitro.d.ts +1 -1
- package/dist/nitro.js +3 -3
- package/dist/{oauth-callback-server-WwG5_Zgm.js → oauth-callback-server-BtfOeHTZ.js} +2 -2
- package/dist/{pending-auth-CUbXq06G.js → pending-auth-BPUzcs7n.js} +2 -2
- package/dist/{profile-T0tXE4zu.js → profile-CSixooTi.js} +4 -3
- package/dist/prompt-3gqNY1Ka.js +552 -0
- package/dist/{registry-BA0__8ai.js → registry-DiR0-xRg.js} +1 -1
- package/dist/{resolve-bvD1ORdv.js → resolve-Bn5q__9b.js} +2 -2
- package/dist/{runner-BXYxfIUL.js → runner-DFx3dIJH.js} +9 -9
- package/dist/{sandbox-egress-signals-ngEPjSEW.js → sandbox-egress-signals-bwvjihxA.js} +1 -1
- package/dist/schema-8GXFfclK.js +559 -0
- package/dist/{skills-DJOlYdBs.js → skills-BhM5qyCF.js} +2 -2
- package/dist/{spawn-h0R1bTEo.js → spawn-Dk0PqdH6.js} +2 -2
- package/dist/{stats-3YZn-Swi.js → stats-DKRP4rsY.js} +6 -6
- package/dist/{task-queue-CoXwd2eq.js → task-queue-CdpTvu4R.js} +3 -3
- package/dist/{task-runner-B2_mJZj0.js → task-runner-alpjVSxQ.js} +10 -10
- package/dist/{thread-state-CqZ1m-es.js → thread-state-CRK7aA41.js} +5 -5
- package/dist/{turn-execution-fI6PZ0iI.js → turn-execution-XtyzIHog.js} +3 -3
- package/dist/{turn-lifecycle-CcnwrOcp.js → turn-lifecycle-VbwZLfBv.js} +1 -1
- package/dist/{usage-B5SfMb3H.js → usage-D4A48Bdl.js} +4 -125
- package/dist/{validation-DfWcvhuS.js → validation-jn_46o4U.js} +1 -1
- package/dist/{version-oD-oPrbO.js → version-DcfJxB4z.js} +1 -1
- package/dist/version.js +1 -1
- package/dist/web-authorization-CsfLSfEs.js +38 -0
- package/dist/{web-input-BOuq3oKK.js → web-input-BPp_sc7t.js} +6 -6
- package/dist/{work-B91LQKHr.js → work-Di-ubSGg.js} +9 -9
- package/dist/{worker-BcRRTe6k.js → worker-Cu_D5Mr8.js} +5 -4
- package/migrations/0040_conversation_briefs.sql +17 -0
- package/migrations/meta/0040_snapshot.json +3516 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +5 -5
- package/dist/history-CqTuSAdI.js +0 -441
- package/dist/usage-schema-CKaDkH_j.js +0 -20
- package/dist/web-authorization-Crbzlmzo.js +0 -76
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
import { M as conversationBriefSchema, N as parseBriefInput, j as briefOutcomeStatusSchema } from "./conversation-AA4R1VUn.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/chat/briefs/generate.ts
|
|
4
|
+
const MAX_INPUT_CHARS = 6e4;
|
|
5
|
+
const MAX_MESSAGE_TEXT_CHARS = 4e3;
|
|
6
|
+
const MAX_TOOL_TEXT_CHARS = 1500;
|
|
7
|
+
const MAX_LINKS = 40;
|
|
8
|
+
const SIZE_CAPS = {
|
|
9
|
+
small: {
|
|
10
|
+
decisions: 3,
|
|
11
|
+
openDecisions: 2,
|
|
12
|
+
facts: 5,
|
|
13
|
+
keywords: 5
|
|
14
|
+
},
|
|
15
|
+
medium: {
|
|
16
|
+
decisions: 8,
|
|
17
|
+
openDecisions: 5,
|
|
18
|
+
facts: 10,
|
|
19
|
+
keywords: 8
|
|
20
|
+
},
|
|
21
|
+
large: {
|
|
22
|
+
decisions: 20,
|
|
23
|
+
openDecisions: 10,
|
|
24
|
+
facts: 15,
|
|
25
|
+
keywords: 12
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const modelBriefSchema = z.object({
|
|
29
|
+
summary: z.string().trim().min(1),
|
|
30
|
+
intent: z.string().trim().min(1),
|
|
31
|
+
outcome: z.object({
|
|
32
|
+
status: briefOutcomeStatusSchema,
|
|
33
|
+
text: z.string().trim().min(1)
|
|
34
|
+
}).strict(),
|
|
35
|
+
decisions: z.array(z.object({
|
|
36
|
+
text: z.string().trim().min(1),
|
|
37
|
+
by: z.string().trim().min(1).optional(),
|
|
38
|
+
kind: z.enum([
|
|
39
|
+
"stated",
|
|
40
|
+
"confirmed",
|
|
41
|
+
"assumed"
|
|
42
|
+
])
|
|
43
|
+
}).strict()),
|
|
44
|
+
openDecisions: z.array(z.object({
|
|
45
|
+
text: z.string().trim().min(1),
|
|
46
|
+
owner: z.string().trim().min(1).optional()
|
|
47
|
+
}).strict()),
|
|
48
|
+
facts: z.array(z.string().trim().min(1)),
|
|
49
|
+
keywords: z.array(z.string().trim().min(1)),
|
|
50
|
+
urls: z.array(z.object({
|
|
51
|
+
label: z.string().trim().min(1),
|
|
52
|
+
url: z.string().url().max(2048)
|
|
53
|
+
}).strict())
|
|
54
|
+
}).strict();
|
|
55
|
+
function truncate(value, limit) {
|
|
56
|
+
const trimmed = value.trim();
|
|
57
|
+
return trimmed.length <= limit ? trimmed : trimmed.slice(0, limit);
|
|
58
|
+
}
|
|
59
|
+
function truncateSentence(value, limit) {
|
|
60
|
+
const trimmed = value.trim();
|
|
61
|
+
if (trimmed.length <= limit) return trimmed;
|
|
62
|
+
const bounded = trimmed.slice(0, limit);
|
|
63
|
+
const boundary = Math.max(bounded.lastIndexOf("."), bounded.lastIndexOf("!"), bounded.lastIndexOf("?"));
|
|
64
|
+
if (boundary + 1 >= limit * .6) return bounded.slice(0, boundary + 1).trim();
|
|
65
|
+
return `${trimmed.slice(0, limit - 1).trimEnd()}…`;
|
|
66
|
+
}
|
|
67
|
+
function uniqueStrings(values, limit) {
|
|
68
|
+
const unique = /* @__PURE__ */ new Set();
|
|
69
|
+
for (const value of values) {
|
|
70
|
+
const normalized = truncate(value, 400);
|
|
71
|
+
if (normalized) unique.add(normalized);
|
|
72
|
+
if (unique.size === limit) break;
|
|
73
|
+
}
|
|
74
|
+
return [...unique];
|
|
75
|
+
}
|
|
76
|
+
const RUNTIME_MARKER_PATTERN = /\[\[NO_REPLY\]\]|<\/?(?:active-asks|analysis-error|available-skills|brief-input|compaction|current-instruction|image-attachment|omitted-image-attachments|pi-history|plugin-contribution|resolved-or-replaced-asks|runtime-turn-context|thread-compactions|thread-context|turn-context)\b/i;
|
|
77
|
+
function hasRuntimeMarker(value) {
|
|
78
|
+
return RUNTIME_MARKER_PATTERN.test(value);
|
|
79
|
+
}
|
|
80
|
+
function normalizeModelBrief(args) {
|
|
81
|
+
let coercedDecisionKinds = 0;
|
|
82
|
+
let droppedAttributionCount = 0;
|
|
83
|
+
let droppedRuntimeMarkerCount = 0;
|
|
84
|
+
const participantNames = new Map(args.record.participants.map((participant) => [participant.name.toLowerCase(), participant.name]));
|
|
85
|
+
participantNames.set("junior", "Junior");
|
|
86
|
+
const attribution = (value) => {
|
|
87
|
+
if (!value?.trim()) return void 0;
|
|
88
|
+
const matched = participantNames.get(value.trim().toLowerCase());
|
|
89
|
+
if (!matched) droppedAttributionCount += 1;
|
|
90
|
+
return matched;
|
|
91
|
+
};
|
|
92
|
+
const decisions = args.model.decisions.flatMap((decision) => {
|
|
93
|
+
if (hasRuntimeMarker(decision.text)) {
|
|
94
|
+
droppedRuntimeMarkerCount += 1;
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
let by = attribution(decision.by);
|
|
98
|
+
let kind = decision.kind;
|
|
99
|
+
if (by === "Junior" && kind !== "assumed") {
|
|
100
|
+
kind = "assumed";
|
|
101
|
+
coercedDecisionKinds += 1;
|
|
102
|
+
} else if (by && by !== "Junior" && kind === "assumed") {
|
|
103
|
+
by = void 0;
|
|
104
|
+
coercedDecisionKinds += 1;
|
|
105
|
+
}
|
|
106
|
+
return [{
|
|
107
|
+
text: truncate(decision.text, 400),
|
|
108
|
+
...by ? { by } : void 0,
|
|
109
|
+
kind
|
|
110
|
+
}];
|
|
111
|
+
});
|
|
112
|
+
const openDecisions = args.model.openDecisions.flatMap((decision) => {
|
|
113
|
+
if (hasRuntimeMarker(decision.text)) {
|
|
114
|
+
droppedRuntimeMarkerCount += 1;
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
const owner = attribution(decision.owner);
|
|
118
|
+
return [{
|
|
119
|
+
text: truncate(decision.text, 400),
|
|
120
|
+
...owner ? { owner } : void 0
|
|
121
|
+
}];
|
|
122
|
+
});
|
|
123
|
+
const facts = args.model.facts.filter((fact) => {
|
|
124
|
+
const keep = !hasRuntimeMarker(fact);
|
|
125
|
+
if (!keep) droppedRuntimeMarkerCount += 1;
|
|
126
|
+
return keep;
|
|
127
|
+
});
|
|
128
|
+
return {
|
|
129
|
+
brief: conversationBriefSchema.parse({
|
|
130
|
+
schemaVersion: 1,
|
|
131
|
+
record: args.record,
|
|
132
|
+
summary: truncateSentence(args.model.summary, 600),
|
|
133
|
+
intent: truncateSentence(args.model.intent, 400),
|
|
134
|
+
outcome: {
|
|
135
|
+
status: args.model.outcome.status,
|
|
136
|
+
text: truncateSentence(args.model.outcome.text, 600)
|
|
137
|
+
},
|
|
138
|
+
decisions: decisions.slice(0, args.caps.decisions),
|
|
139
|
+
openDecisions: openDecisions.slice(0, args.caps.openDecisions),
|
|
140
|
+
facts: uniqueStrings(facts, args.caps.facts),
|
|
141
|
+
links: args.links,
|
|
142
|
+
keywords: uniqueStrings(args.model.keywords.map((keyword) => keyword.toLowerCase()), args.caps.keywords)
|
|
143
|
+
}),
|
|
144
|
+
coercedDecisionKinds,
|
|
145
|
+
droppedAttributionCount,
|
|
146
|
+
droppedRuntimeMarkerCount
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function inputEntries(input, throughIndex) {
|
|
150
|
+
return input.entries.filter((entry) => entry.index <= throughIndex).map((entry) => {
|
|
151
|
+
const limit = entry.role === "tool" ? MAX_TOOL_TEXT_CHARS : MAX_MESSAGE_TEXT_CHARS;
|
|
152
|
+
return {
|
|
153
|
+
...entry,
|
|
154
|
+
text: entry.text.length > limit ? `${entry.text.slice(0, limit - 1)}…` : entry.text
|
|
155
|
+
};
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function buildBriefRecord(input, entries) {
|
|
159
|
+
if (entries.length === 0) throw new Error("A Brief requires at least one entry through its index");
|
|
160
|
+
const participantByName = /* @__PURE__ */ new Map();
|
|
161
|
+
for (const entry of entries) {
|
|
162
|
+
if (entry.role !== "user" || !entry.author?.trim()) continue;
|
|
163
|
+
const key = entry.author.trim().toLowerCase();
|
|
164
|
+
const participant = participantByName.get(key);
|
|
165
|
+
if (participant) participant.messages += 1;
|
|
166
|
+
else participantByName.set(key, {
|
|
167
|
+
name: entry.author.trim(),
|
|
168
|
+
messages: 1
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
const activityTimes = entries.map((entry) => entry.createdAtMs);
|
|
172
|
+
const startedAtMs = Math.min(...activityTimes);
|
|
173
|
+
const lastActivityAtMs = Math.max(...activityTimes);
|
|
174
|
+
const turns = new Set(entries.flatMap((entry) => entry.turnId ? [entry.turnId] : [])).size;
|
|
175
|
+
return {
|
|
176
|
+
startedAt: new Date(startedAtMs).toISOString(),
|
|
177
|
+
lastActivityAt: new Date(lastActivityAtMs).toISOString(),
|
|
178
|
+
durationMs: lastActivityAtMs - startedAtMs,
|
|
179
|
+
participants: [...participantByName.values()],
|
|
180
|
+
userMessages: entries.filter((entry) => entry.role === "user").length,
|
|
181
|
+
assistantMessages: entries.filter((entry) => entry.role === "assistant").length,
|
|
182
|
+
toolResults: entries.filter((entry) => entry.role === "tool").length,
|
|
183
|
+
events: entries.filter((entry) => entry.role === "event").length,
|
|
184
|
+
...turns > 0 ? { turns } : void 0,
|
|
185
|
+
...input.location ? { location: input.location } : void 0,
|
|
186
|
+
codeChanges: input.codeChanges
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function briefSizeClass(record) {
|
|
190
|
+
if (record.userMessages <= 3) return "small";
|
|
191
|
+
if (record.userMessages <= 12) return "medium";
|
|
192
|
+
return "large";
|
|
193
|
+
}
|
|
194
|
+
function promptInput(args) {
|
|
195
|
+
const messages = args.entries.filter((entry) => entry.role !== "tool");
|
|
196
|
+
const tools = args.entries.filter((entry) => entry.role === "tool");
|
|
197
|
+
const keptMessages = [...messages];
|
|
198
|
+
const keptTools = [];
|
|
199
|
+
const base = {
|
|
200
|
+
conversation: {
|
|
201
|
+
id: args.input.conversationId,
|
|
202
|
+
title: args.input.title,
|
|
203
|
+
visibility: args.input.visibility,
|
|
204
|
+
location: args.input.location
|
|
205
|
+
},
|
|
206
|
+
record: args.record,
|
|
207
|
+
sizeClass: args.sizeClass,
|
|
208
|
+
caps: args.caps,
|
|
209
|
+
previousBrief: args.previous,
|
|
210
|
+
resources: args.input.resources
|
|
211
|
+
};
|
|
212
|
+
const serialize = () => {
|
|
213
|
+
const kept = /* @__PURE__ */ new Set([...keptMessages, ...keptTools]);
|
|
214
|
+
return JSON.stringify({
|
|
215
|
+
...base,
|
|
216
|
+
omitted: {
|
|
217
|
+
messages: messages.length - keptMessages.length,
|
|
218
|
+
toolResults: tools.length - keptTools.length
|
|
219
|
+
},
|
|
220
|
+
entries: args.entries.filter((entry) => kept.has(entry))
|
|
221
|
+
});
|
|
222
|
+
};
|
|
223
|
+
let text = serialize();
|
|
224
|
+
while (text.length > MAX_INPUT_CHARS && keptMessages.length > 0) {
|
|
225
|
+
keptMessages.shift();
|
|
226
|
+
text = serialize();
|
|
227
|
+
}
|
|
228
|
+
if (text.length > MAX_INPUT_CHARS) throw new Error("Brief metadata exceeds the 60,000 character input limit");
|
|
229
|
+
for (let index = tools.length - 1; index >= 0; index -= 1) {
|
|
230
|
+
keptTools.push(tools[index]);
|
|
231
|
+
const candidate = serialize();
|
|
232
|
+
if (candidate.length <= MAX_INPUT_CHARS) text = candidate;
|
|
233
|
+
else keptTools.pop();
|
|
234
|
+
}
|
|
235
|
+
return [
|
|
236
|
+
"<brief-input>",
|
|
237
|
+
text,
|
|
238
|
+
"</brief-input>",
|
|
239
|
+
"Return the next Brief as structured data."
|
|
240
|
+
].join("\n");
|
|
241
|
+
}
|
|
242
|
+
function deterministicLinks(input) {
|
|
243
|
+
return [...input.codeChanges.map((change) => ({
|
|
244
|
+
kind: "code_change",
|
|
245
|
+
label: truncate(`${change.repository}#${change.number}${change.title ? ` · ${change.title}` : ""}`, 400),
|
|
246
|
+
url: change.url,
|
|
247
|
+
status: change.state
|
|
248
|
+
})), ...input.resources.map((resource) => ({
|
|
249
|
+
kind: "resource",
|
|
250
|
+
label: truncate(resource.label, 400),
|
|
251
|
+
url: resource.url,
|
|
252
|
+
...resource.status ? { status: resource.status } : void 0
|
|
253
|
+
}))];
|
|
254
|
+
}
|
|
255
|
+
function decodeHtmlEntities(value) {
|
|
256
|
+
const entities = {
|
|
257
|
+
"'": "'",
|
|
258
|
+
"&": "&",
|
|
259
|
+
">": ">",
|
|
260
|
+
"<": "<",
|
|
261
|
+
""": "\""
|
|
262
|
+
};
|
|
263
|
+
return value.replace(/&(?:amp|lt|gt|quot|#39);/g, (entity) => entities[entity]);
|
|
264
|
+
}
|
|
265
|
+
function endsWithUnopenedBracket(url, open, close) {
|
|
266
|
+
return url.endsWith(close) && url.split(open).length < url.split(close).length;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Strip punctuation that prose attaches to a URL. A closing bracket stays when
|
|
270
|
+
* the URL opened it, so `wiki/Foo_(bar)` survives and `(see https://x/y)` does not.
|
|
271
|
+
*/
|
|
272
|
+
function normalizeCitationUrl(raw) {
|
|
273
|
+
let url = decodeHtmlEntities(raw).trim();
|
|
274
|
+
for (;;) {
|
|
275
|
+
let next = url.replace(/[;,.]+$/, "").replace(/\/+$/, "");
|
|
276
|
+
if (endsWithUnopenedBracket(next, "(", ")") || endsWithUnopenedBracket(next, "[", "]")) next = next.slice(0, -1);
|
|
277
|
+
if (next === url) return url;
|
|
278
|
+
url = next;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/** Models sometimes append a stray `/https` to a cited URL; try without it. */
|
|
282
|
+
function citationCandidates(raw) {
|
|
283
|
+
const normalized = normalizeCitationUrl(raw);
|
|
284
|
+
const stripped = normalizeCitationUrl(normalized.replace(/\/https?$/i, ""));
|
|
285
|
+
return stripped === normalized ? [normalized] : [normalized, stripped];
|
|
286
|
+
}
|
|
287
|
+
/** URL tokens in prose or Slack `<url|label>` links, normalized for matching. */
|
|
288
|
+
function normalizedUrlTokens(value) {
|
|
289
|
+
const tokens = decodeHtmlEntities(value).match(/https?:\/\/[^\s<>"'|]+/gi);
|
|
290
|
+
return new Set((tokens ?? []).map(normalizeCitationUrl).filter((url) => url.length > 0));
|
|
291
|
+
}
|
|
292
|
+
function buildEvidenceLinks(args) {
|
|
293
|
+
const deterministic = deterministicLinks(args.input);
|
|
294
|
+
const transcriptUrls = normalizedUrlTokens(args.entries.map((entry) => entry.text).join("\n"));
|
|
295
|
+
const priorUrls = new Set((args.previous?.links ?? []).map((link) => normalizeCitationUrl(link.url)));
|
|
296
|
+
const linkedUrls = new Set(deterministic.map((link) => normalizeCitationUrl(link.url)));
|
|
297
|
+
const supportedUrls = /* @__PURE__ */ new Set();
|
|
298
|
+
const droppedUrls = /* @__PURE__ */ new Map();
|
|
299
|
+
const cited = [];
|
|
300
|
+
for (const citation of args.model.urls) {
|
|
301
|
+
const candidates = citationCandidates(citation.url);
|
|
302
|
+
const normalized = candidates.find((candidate) => linkedUrls.has(candidate) || transcriptUrls.has(candidate) || priorUrls.has(candidate));
|
|
303
|
+
if (normalized === void 0) {
|
|
304
|
+
droppedUrls.set(`${citation.url}\0${candidates[0]}`, {
|
|
305
|
+
normalized: candidates[0],
|
|
306
|
+
raw: citation.url
|
|
307
|
+
});
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
supportedUrls.add(normalized);
|
|
311
|
+
if (linkedUrls.has(normalized)) continue;
|
|
312
|
+
linkedUrls.add(normalized);
|
|
313
|
+
cited.push({
|
|
314
|
+
raw: citation.url,
|
|
315
|
+
link: {
|
|
316
|
+
kind: "url",
|
|
317
|
+
label: truncate(citation.label, 400),
|
|
318
|
+
url: normalized
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
const citedLimit = Math.max(0, MAX_LINKS - deterministic.length);
|
|
323
|
+
for (const citation of cited.slice(citedLimit)) droppedUrls.set(`${citation.raw}\0${citation.link.url}`, {
|
|
324
|
+
normalized: citation.link.url,
|
|
325
|
+
raw: citation.raw
|
|
326
|
+
});
|
|
327
|
+
const links = [...deterministic, ...cited.slice(0, citedLimit).map((citation) => citation.link)].slice(0, MAX_LINKS);
|
|
328
|
+
const keptUrls = new Set(links.map((link) => normalizeCitationUrl(link.url)));
|
|
329
|
+
return {
|
|
330
|
+
droppedUrls: [...droppedUrls.values()],
|
|
331
|
+
keptUrlCount: [...supportedUrls].filter((url) => keptUrls.has(url)).length,
|
|
332
|
+
links
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
function hasMergedEvidence(input) {
|
|
336
|
+
return input.codeChanges.some((change) => change.state === "merged") || input.resources.some((resource) => resource.status?.trim().toLowerCase() === "merged");
|
|
337
|
+
}
|
|
338
|
+
/** Build the text indexed for a Brief without adding transcript content. */
|
|
339
|
+
function buildBriefSearchText(brief, title) {
|
|
340
|
+
return [
|
|
341
|
+
title,
|
|
342
|
+
brief.summary,
|
|
343
|
+
brief.intent,
|
|
344
|
+
brief.outcome.text,
|
|
345
|
+
...brief.decisions.map((decision) => decision.text),
|
|
346
|
+
...brief.openDecisions.map((decision) => decision.text),
|
|
347
|
+
...brief.facts,
|
|
348
|
+
...brief.keywords,
|
|
349
|
+
...brief.links.map((link) => link.label),
|
|
350
|
+
...brief.record.participants.map((participant) => participant.name),
|
|
351
|
+
...brief.record.codeChanges.map((change) => `${change.repository}#${change.number} ${change.state}`)
|
|
352
|
+
].filter((value) => Boolean(value?.trim())).join("\n");
|
|
353
|
+
}
|
|
354
|
+
/** Generate one bounded Brief and attach only deterministic evidence links. */
|
|
355
|
+
async function generateBrief(args) {
|
|
356
|
+
const input = parseBriefInput(args.input);
|
|
357
|
+
if (!Number.isSafeInteger(args.throughIndex) || args.throughIndex < 0) throw new Error("throughIndex must be a non-negative integer");
|
|
358
|
+
const entries = inputEntries(input, args.throughIndex);
|
|
359
|
+
const record = buildBriefRecord(input, entries);
|
|
360
|
+
const sizeClass = briefSizeClass(record);
|
|
361
|
+
const caps = SIZE_CAPS[sizeClass];
|
|
362
|
+
const result = await args.completeObject({
|
|
363
|
+
maxTokens: 2500,
|
|
364
|
+
prompt: promptInput({
|
|
365
|
+
caps,
|
|
366
|
+
entries,
|
|
367
|
+
input,
|
|
368
|
+
previous: args.previous,
|
|
369
|
+
record,
|
|
370
|
+
sizeClass
|
|
371
|
+
}),
|
|
372
|
+
schema: modelBriefSchema,
|
|
373
|
+
system: args.prompt,
|
|
374
|
+
temperature: 0
|
|
375
|
+
});
|
|
376
|
+
const model = modelBriefSchema.parse(result.object);
|
|
377
|
+
const { droppedUrls, keptUrlCount, links } = buildEvidenceLinks({
|
|
378
|
+
entries,
|
|
379
|
+
input,
|
|
380
|
+
model,
|
|
381
|
+
previous: args.previous
|
|
382
|
+
});
|
|
383
|
+
const normalized = normalizeModelBrief({
|
|
384
|
+
caps,
|
|
385
|
+
links,
|
|
386
|
+
model,
|
|
387
|
+
record
|
|
388
|
+
});
|
|
389
|
+
const mergedClaim = /\bmerged\b/i.test(`${normalized.brief.summary}\n${normalized.brief.outcome.text}`);
|
|
390
|
+
const hasLinkedEvidence = input.codeChanges.length > 0 || input.resources.length > 0;
|
|
391
|
+
return {
|
|
392
|
+
brief: normalized.brief,
|
|
393
|
+
...result.costUsd !== void 0 ? { costUsd: result.costUsd } : void 0,
|
|
394
|
+
evidence: {
|
|
395
|
+
citedUrlCount: model.urls.length,
|
|
396
|
+
codeChangeCount: input.codeChanges.length,
|
|
397
|
+
coercedDecisionKinds: normalized.coercedDecisionKinds,
|
|
398
|
+
droppedAttributionCount: normalized.droppedAttributionCount,
|
|
399
|
+
droppedRuntimeMarkerCount: normalized.droppedRuntimeMarkerCount,
|
|
400
|
+
droppedUrls,
|
|
401
|
+
keptUrlCount,
|
|
402
|
+
mergedClaimWithoutEvidence: mergedClaim && hasLinkedEvidence && !hasMergedEvidence(input),
|
|
403
|
+
resourceCount: input.resources.length
|
|
404
|
+
},
|
|
405
|
+
searchText: buildBriefSearchText(normalized.brief, input.title),
|
|
406
|
+
throughIndex: args.throughIndex
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
//#endregion
|
|
410
|
+
//#region src/chat/resource-events/actor.ts
|
|
411
|
+
/**
|
|
412
|
+
* System author ID stamped on resource-event mailbox messages.
|
|
413
|
+
*
|
|
414
|
+
* Keep this stable so conversation history can recognize the same system input
|
|
415
|
+
* for resource-event Turns.
|
|
416
|
+
*
|
|
417
|
+
* TODO(dcramer): Delete this ID after resumes read resource-event Source.kind
|
|
418
|
+
* from the Turn checkpoint instead of Message author data.
|
|
419
|
+
*/
|
|
420
|
+
const RESOURCE_EVENT_AUTHOR_ID = "UJRNEVENT";
|
|
421
|
+
/** System Message author for resource-event input. */
|
|
422
|
+
const RESOURCE_EVENT_MESSAGE_AUTHOR = {
|
|
423
|
+
fullName: "Junior event",
|
|
424
|
+
isBot: true,
|
|
425
|
+
userId: RESOURCE_EVENT_AUTHOR_ID,
|
|
426
|
+
userName: "junior-event"
|
|
427
|
+
};
|
|
428
|
+
/** System execution actor for every resource-event turn. */
|
|
429
|
+
const RESOURCE_EVENT_SYSTEM_ACTOR = {
|
|
430
|
+
platform: "system",
|
|
431
|
+
name: "resource-event"
|
|
432
|
+
};
|
|
433
|
+
/**
|
|
434
|
+
* Whether a saved Message started a resource-event Turn.
|
|
435
|
+
*
|
|
436
|
+
* TODO(dcramer): Delete this marker check after deployed Turn cursors all store
|
|
437
|
+
* Source and Actor. `eventType` only supports saved synthetic Slack input.
|
|
438
|
+
*/
|
|
439
|
+
function isResourceEventConversationMessage(message) {
|
|
440
|
+
return Boolean(message.meta?.eventType) || message.author?.userId === "UJRNEVENT";
|
|
441
|
+
}
|
|
442
|
+
//#endregion
|
|
443
|
+
//#region src/chat/briefs/event-entries.ts
|
|
444
|
+
const TOOL_TEXT_LIMIT = 1500;
|
|
445
|
+
function actorIdentityKey(actor) {
|
|
446
|
+
const slackUserId = actor?.slackUserId?.trim();
|
|
447
|
+
if (slackUserId) return `slack:${slackUserId}`;
|
|
448
|
+
const email = actor?.email?.trim();
|
|
449
|
+
return email ? `email:${email.toLowerCase()}` : void 0;
|
|
450
|
+
}
|
|
451
|
+
function bestActorName(actor) {
|
|
452
|
+
return actor?.fullName?.trim() || actor?.slackUserName?.trim() || actor?.email?.trim() || void 0;
|
|
453
|
+
}
|
|
454
|
+
function actorNames(events) {
|
|
455
|
+
const actors = /* @__PURE__ */ new Map();
|
|
456
|
+
for (const event of events) {
|
|
457
|
+
if (event.data.type !== "message") continue;
|
|
458
|
+
const actor = event.data.actorIdentity;
|
|
459
|
+
const key = actorIdentityKey(actor);
|
|
460
|
+
if (!key) continue;
|
|
461
|
+
const known = actors.get(key) ?? {};
|
|
462
|
+
actors.set(key, {
|
|
463
|
+
fullName: known.fullName ?? (actor?.fullName?.trim() || void 0),
|
|
464
|
+
slackUserName: known.slackUserName ?? (actor?.slackUserName?.trim() || void 0),
|
|
465
|
+
email: known.email ?? (actor?.email?.trim() || void 0)
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
const names = /* @__PURE__ */ new Map();
|
|
469
|
+
for (const [key, actor] of actors) {
|
|
470
|
+
const name = bestActorName(actor);
|
|
471
|
+
if (name) names.set(key, name);
|
|
472
|
+
}
|
|
473
|
+
return names;
|
|
474
|
+
}
|
|
475
|
+
function actorName(actor, resolvedNames) {
|
|
476
|
+
if (!actor) return void 0;
|
|
477
|
+
const key = actorIdentityKey(actor);
|
|
478
|
+
return (key ? resolvedNames.get(key) : void 0) ?? bestActorName(actor);
|
|
479
|
+
}
|
|
480
|
+
function toolText(output) {
|
|
481
|
+
if (output === void 0) return void 0;
|
|
482
|
+
const text = typeof output === "string" ? output : JSON.stringify(output, void 0, 2);
|
|
483
|
+
if (!text?.trim()) return void 0;
|
|
484
|
+
return text.length <= TOOL_TEXT_LIMIT ? text : `${text.slice(0, 1499)}…`;
|
|
485
|
+
}
|
|
486
|
+
/** Map reporting events to the entries used by every Brief input adapter. */
|
|
487
|
+
function briefEntriesFromReportEvents(events) {
|
|
488
|
+
const resolvedActorNames = actorNames(events);
|
|
489
|
+
const turnByMessageId = /* @__PURE__ */ new Map();
|
|
490
|
+
for (const event of events) if (event.data.type === "turn_lifecycle" && event.data.state === "started") for (const messageId of event.data.inputMessageIds ?? []) turnByMessageId.set(messageId, event.data.turnId);
|
|
491
|
+
const entries = [];
|
|
492
|
+
let activeTurnId;
|
|
493
|
+
for (const event of events) {
|
|
494
|
+
const data = event.data;
|
|
495
|
+
if (data.type === "turn_lifecycle") {
|
|
496
|
+
if (data.state === "started") activeTurnId = data.turnId;
|
|
497
|
+
else if (data.turnId === activeTurnId) activeTurnId = void 0;
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
if (data.type === "message" && data.text && data.role !== "system") {
|
|
501
|
+
const turnId = data.role === "user" ? turnByMessageId.get(data.messageId) : activeTurnId;
|
|
502
|
+
const author = actorName(data.actorIdentity, resolvedActorNames);
|
|
503
|
+
const role = data.role === "user" && (Boolean(data.eventType) || data.actorIdentity?.slackUserId === "UJRNEVENT") ? "event" : data.role;
|
|
504
|
+
entries.push({
|
|
505
|
+
index: event.seq,
|
|
506
|
+
role,
|
|
507
|
+
...author ? { author } : void 0,
|
|
508
|
+
text: data.text,
|
|
509
|
+
createdAtMs: Date.parse(event.createdAt),
|
|
510
|
+
...turnId ? { turnId } : void 0
|
|
511
|
+
});
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
if (data.type !== "tool_calls") continue;
|
|
515
|
+
for (const call of data.calls) {
|
|
516
|
+
const text = toolText(call.output);
|
|
517
|
+
if (!text) continue;
|
|
518
|
+
entries.push({
|
|
519
|
+
index: event.seq,
|
|
520
|
+
role: "tool",
|
|
521
|
+
author: call.name,
|
|
522
|
+
text,
|
|
523
|
+
createdAtMs: Date.parse(event.createdAt),
|
|
524
|
+
...activeTurnId ? { turnId: activeTurnId } : void 0
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return entries;
|
|
529
|
+
}
|
|
530
|
+
//#endregion
|
|
531
|
+
//#region src/chat/briefs/prompt.ts
|
|
532
|
+
const BRIEF_PROMPT = `You write one durable Brief for a Junior Conversation.
|
|
533
|
+
|
|
534
|
+
Use only facts supported by the supplied entries, evidence, or previous Brief. Do not invent names, ids, numbers, outcomes, choices, owners, or links.
|
|
535
|
+
|
|
536
|
+
Rules:
|
|
537
|
+
- Summarize the intent and current outcome in plain prose.
|
|
538
|
+
- Keep earlier decisions unless later entries reverse them. State a reversal as a new decision.
|
|
539
|
+
- A decision is a choice made by a listed participant in this Conversation. It is not a research finding, tool result, action, observation, or suggestion. Put findings in facts or the summary.
|
|
540
|
+
- Set each decision kind to stated when a human participant stated the choice themselves, confirmed when Junior proposed it and a human participant explicitly accepted it, or assumed when Junior chose while working and no participant confirmed or objected.
|
|
541
|
+
- An open decision is a choice the participants explicitly left open, asked about, or were asked to make. Phrase it as the choice and include its options when known. A next step such as merging or reviewing is not an open decision. Do not use worries, monitoring questions, or finished work.
|
|
542
|
+
- Facts are durable one-line facts that help after the transcript expires. Prefer ids, versions, owners, constraints, and numbers. Do not list fields, recap the transcript, or include transient progress.
|
|
543
|
+
- Ignore Junior runtime mechanics, context tags, and markers such as [[NO_REPLY]].
|
|
544
|
+
- Never say that a code change was merged, closed, deployed, or released unless a supplied code change or resource has that status. Say opened for an open code change.
|
|
545
|
+
- Attribute by or owner only to a participant in the supplied record. Junior is also valid.
|
|
546
|
+
- Choose the outcome status that best describes where the Conversation ended.
|
|
547
|
+
- Follow the supplied size class caps. Keep the first items that matter most.
|
|
548
|
+
- Cite a URL only when its exact full string occurs in an entry, evidence, or the previous Brief. Return cited URLs in urls. Core adds code changes and resources separately.
|
|
549
|
+
- Keep the summary under 600 characters, intent under 400 characters, outcome text under 600 characters, and each other string under 400 characters.
|
|
550
|
+
`;
|
|
551
|
+
//#endregion
|
|
552
|
+
export { RESOURCE_EVENT_SYSTEM_ACTOR as a, RESOURCE_EVENT_MESSAGE_AUTHOR as i, briefEntriesFromReportEvents as n, isResourceEventConversationMessage as o, RESOURCE_EVENT_AUTHOR_ID as r, generateBrief as s, BRIEF_PROMPT as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as setSpanAttributes, d as logInfo, f as logWarn } from "./logging-
|
|
1
|
+
import { _ as setSpanAttributes, d as logInfo, f as logWarn } from "./logging-D37bayDc.js";
|
|
2
2
|
import { i as credentialUserSubjectId } from "./context-B45_WqVX.js";
|
|
3
3
|
import { n as normalizePluginPackageNames, o as pluginRoots, t as discoverInstalledPluginPackageContent } from "./package-discovery-7a9YqLhE.js";
|
|
4
4
|
import path from "node:path";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as withSpan, _ as setSpanAttributes } from "./logging-
|
|
2
|
-
import { A as sleep, P as SANDBOX_WORKSPACE_ROOT, r as isStale, t as create, u as isSandboxUnavailableError, v as getVercelSandboxCredentials, w as getStateAdapter, x as runNonInteractiveCommand } from "./profile-
|
|
1
|
+
import { C as withSpan, _ as setSpanAttributes } from "./logging-D37bayDc.js";
|
|
2
|
+
import { A as sleep, P as SANDBOX_WORKSPACE_ROOT, r as isStale, t as create, u as isSandboxUnavailableError, v as getVercelSandboxCredentials, w as getStateAdapter, x as runNonInteractiveCommand } from "./profile-CSixooTi.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { FileSystem, Sandbox } from "@vercel/sandbox";
|
|
5
5
|
//#region src/chat/sandbox/resources.ts
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { p as authorizationId } from "./agent-
|
|
2
|
-
import { S as withLogContext, u as logException, y as setTags } from "./logging-
|
|
1
|
+
import { p as authorizationId } from "./agent-C9yuiZUF.js";
|
|
2
|
+
import { S as withLogContext, u as logException, y as setTags } from "./logging-D37bayDc.js";
|
|
3
3
|
import { t as credentialContextForActor } from "./context-B45_WqVX.js";
|
|
4
|
-
import {
|
|
5
|
-
import { $ as persistConversationMessages, P as commitAcceptedReply, Q as hydrateConversationMessages, R as loadProjection, W as recordAuthorizationCompleted, d as persistWithRetry, dt as recordDeliveredAssistantMessage, it as buildConversationContext, jt as stripRuntimeTurnContext, lt as markConversationMessage, pt as upsertConversationMessage, r as saveTurnCheckpoint, ut as normalizeConversationText } from "./checkpoint-
|
|
6
|
-
import { S as startActiveTurn, s as getAssistantReplyText, x as markTurnFailed } from "./pending-auth-
|
|
7
|
-
import { n as scheduleSessionCompletedPluginTasks, t as processPluginTask } from "./task-runner-
|
|
8
|
-
import { t as ConversationTurnLifecycleService } from "./turn-lifecycle-
|
|
9
|
-
import { n as getPersistedSandboxState, o as persistThreadStateById, r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-
|
|
10
|
-
import { d as buildDeliveredTurnStatePatch, r as finalizeFailedTurnReplyWithEvent, t as completeAuthPauseTurn } from "./auth-pause-state-
|
|
4
|
+
import { Ft as getConversationStore, Nt as getConversationEventStore } from "./agent-hooks-DNjlz2A-.js";
|
|
5
|
+
import { $ as persistConversationMessages, P as commitAcceptedReply, Q as hydrateConversationMessages, R as loadProjection, W as recordAuthorizationCompleted, d as persistWithRetry, dt as recordDeliveredAssistantMessage, it as buildConversationContext, jt as stripRuntimeTurnContext, lt as markConversationMessage, pt as upsertConversationMessage, r as saveTurnCheckpoint, ut as normalizeConversationText } from "./checkpoint-BY47jJCB.js";
|
|
6
|
+
import { S as startActiveTurn, s as getAssistantReplyText, x as markTurnFailed } from "./pending-auth-BPUzcs7n.js";
|
|
7
|
+
import { n as scheduleSessionCompletedPluginTasks, t as processPluginTask } from "./task-runner-alpjVSxQ.js";
|
|
8
|
+
import { t as ConversationTurnLifecycleService } from "./turn-lifecycle-VbwZLfBv.js";
|
|
9
|
+
import { n as getPersistedSandboxState, o as persistThreadStateById, r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-CRK7aA41.js";
|
|
10
|
+
import { d as buildDeliveredTurnStatePatch, r as finalizeFailedTurnReplyWithEvent, t as completeAuthPauseTurn } from "./auth-pause-state-MVZ6xvEK.js";
|
|
11
11
|
import { randomUUID } from "node:crypto";
|
|
12
12
|
import { createLocalSource, localDestinationSchema } from "@sentry/junior-plugin-api";
|
|
13
13
|
//#region src/chat/local/runner.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { J as sandboxEgressSignalsResponseSchema } from "./agent-C9yuiZUF.js";
|
|
2
2
|
//#region src/chat/local/sandbox-egress-signals.ts
|
|
3
3
|
const SANDBOX_EGRESS_SIGNALS_PATH = "/api/internal/sandbox-egress/signals";
|
|
4
4
|
const LOCAL_DEV_SERVER_TIMEOUT_MS = 1e3;
|