@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.
Files changed (66) hide show
  1. package/dist/{agent-D4nu8MQV.js → agent-C9yuiZUF.js} +247 -67
  2. package/dist/{agent-hooks-DXHpTJ4m.js → agent-hooks-DNjlz2A-.js} +1078 -12
  3. package/dist/{agent-runner-C04colea.js → agent-runner-E67lFrDG.js} +5 -5
  4. package/dist/api/schema.d.ts +77 -0
  5. package/dist/api/schema.js +2 -2
  6. package/dist/api.js +44 -511
  7. package/dist/{app-Cb776lL8.d.ts → app-AxGmJHwK.d.ts} +7 -0
  8. package/dist/app.d.ts +1 -1
  9. package/dist/app.js +50 -80
  10. package/dist/{auth-pause-state-C2P3guS1.js → auth-pause-state-MVZ6xvEK.js} +6 -6
  11. package/dist/briefs-CvJFQvhJ.js +398 -0
  12. package/dist/{catalog-runtime-CXw3asJD.js → catalog-runtime-TBR8n4e1.js} +1 -1
  13. package/dist/{checkpoint-CrViJ8Ps.js → checkpoint-BY47jJCB.js} +7 -6
  14. package/dist/cli/chat.js +14 -12
  15. package/dist/cli/check.js +3 -3
  16. package/dist/cli/init.js +1 -0
  17. package/dist/cli/main.js +5 -1
  18. package/dist/cli/plugins.js +3 -3
  19. package/dist/cli/run.d.ts +2 -1
  20. package/dist/cli/run.js +2 -1
  21. package/dist/cli/snapshot-create.js +3 -3
  22. package/dist/cli/upgrade.js +3 -3
  23. package/dist/{config-BOtJQ7Pc.js → client-ceE7NtvC.js} +4 -367
  24. package/dist/config-CIE83sR2.js +386 -0
  25. package/dist/{schema-Rn8-XEaN.js → conversation-AA4R1VUn.js} +580 -558
  26. package/dist/{conversation-privacy-BZRA6Pes.js → conversation-privacy-Auyq26XQ.js} +1 -1
  27. package/dist/{credential-sync-Bep34IB_.js → credential-sync-BO33aNQd.js} +20 -19
  28. package/dist/{execution-stats-B6lgWxCK.js → execution-stats-SiS2FLcv.js} +4 -4
  29. package/dist/{executor-BlUhFL7_.js → executor-BfbYPW63.js} +31 -3
  30. package/dist/instrumentation.js +2 -2
  31. package/dist/{job-queue-DLMqIK74.js → job-queue-CyTc24Es.js} +1 -1
  32. package/dist/{logging-BMJzdDlP.js → logging-D37bayDc.js} +1 -1
  33. package/dist/nitro.d.ts +1 -1
  34. package/dist/nitro.js +3 -3
  35. package/dist/{oauth-callback-server-WwG5_Zgm.js → oauth-callback-server-BtfOeHTZ.js} +2 -2
  36. package/dist/{pending-auth-CUbXq06G.js → pending-auth-BPUzcs7n.js} +2 -2
  37. package/dist/{profile-T0tXE4zu.js → profile-CSixooTi.js} +4 -3
  38. package/dist/prompt-3gqNY1Ka.js +552 -0
  39. package/dist/{registry-BA0__8ai.js → registry-DiR0-xRg.js} +1 -1
  40. package/dist/{resolve-bvD1ORdv.js → resolve-Bn5q__9b.js} +2 -2
  41. package/dist/{runner-BXYxfIUL.js → runner-DFx3dIJH.js} +9 -9
  42. package/dist/{sandbox-egress-signals-ngEPjSEW.js → sandbox-egress-signals-bwvjihxA.js} +1 -1
  43. package/dist/schema-8GXFfclK.js +559 -0
  44. package/dist/{skills-DJOlYdBs.js → skills-BhM5qyCF.js} +2 -2
  45. package/dist/{spawn-h0R1bTEo.js → spawn-Dk0PqdH6.js} +2 -2
  46. package/dist/{stats-3YZn-Swi.js → stats-DKRP4rsY.js} +6 -6
  47. package/dist/{task-queue-CoXwd2eq.js → task-queue-CdpTvu4R.js} +3 -3
  48. package/dist/{task-runner-B2_mJZj0.js → task-runner-alpjVSxQ.js} +10 -10
  49. package/dist/{thread-state-CqZ1m-es.js → thread-state-CRK7aA41.js} +5 -5
  50. package/dist/{turn-execution-fI6PZ0iI.js → turn-execution-XtyzIHog.js} +3 -3
  51. package/dist/{turn-lifecycle-CcnwrOcp.js → turn-lifecycle-VbwZLfBv.js} +1 -1
  52. package/dist/{usage-B5SfMb3H.js → usage-D4A48Bdl.js} +4 -125
  53. package/dist/{validation-DfWcvhuS.js → validation-jn_46o4U.js} +1 -1
  54. package/dist/{version-oD-oPrbO.js → version-DcfJxB4z.js} +1 -1
  55. package/dist/version.js +1 -1
  56. package/dist/web-authorization-CsfLSfEs.js +38 -0
  57. package/dist/{web-input-BOuq3oKK.js → web-input-BPp_sc7t.js} +6 -6
  58. package/dist/{work-B91LQKHr.js → work-Di-ubSGg.js} +9 -9
  59. package/dist/{worker-BcRRTe6k.js → worker-Cu_D5Mr8.js} +5 -4
  60. package/migrations/0040_conversation_briefs.sql +17 -0
  61. package/migrations/meta/0040_snapshot.json +3516 -0
  62. package/migrations/meta/_journal.json +7 -0
  63. package/package.json +5 -5
  64. package/dist/history-CqTuSAdI.js +0 -441
  65. package/dist/usage-schema-CKaDkH_j.js +0 -20
  66. 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
+ "&#39;": "'",
258
+ "&amp;": "&",
259
+ "&gt;": ">",
260
+ "&lt;": "<",
261
+ "&quot;": "\""
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-BMJzdDlP.js";
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-BMJzdDlP.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-T0tXE4zu.js";
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-D4nu8MQV.js";
2
- import { S as withLogContext, u as logException, y as setTags } from "./logging-BMJzdDlP.js";
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 { Ct as getConversationEventStore, Tt as getConversationStore } from "./agent-hooks-DXHpTJ4m.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-CrViJ8Ps.js";
6
- import { S as startActiveTurn, s as getAssistantReplyText, x as markTurnFailed } from "./pending-auth-CUbXq06G.js";
7
- import { n as scheduleSessionCompletedPluginTasks, t as processPluginTask } from "./task-runner-B2_mJZj0.js";
8
- import { t as ConversationTurnLifecycleService } from "./turn-lifecycle-CcnwrOcp.js";
9
- import { n as getPersistedSandboxState, o as persistThreadStateById, r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-CqZ1m-es.js";
10
- import { d as buildDeliveredTurnStatePatch, r as finalizeFailedTurnReplyWithEvent, t as completeAuthPauseTurn } from "./auth-pause-state-C2P3guS1.js";
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 { Z as sandboxEgressSignalsResponseSchema } from "./agent-D4nu8MQV.js";
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;