@sentry/junior 0.95.0 → 0.97.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 (144) hide show
  1. package/dist/{agent-hooks-FTQ5EGPO.js → agent-hooks-XSUIVRQS.js} +10 -8
  2. package/dist/{reporting → api}/conversations/activity.d.ts +1 -1
  3. package/dist/api/conversations/detail-projection.d.ts +10 -0
  4. package/dist/api/conversations/detail.d.ts +3 -0
  5. package/dist/api/conversations/detail.query.d.ts +3 -0
  6. package/dist/api/conversations/list.d.ts +3 -0
  7. package/dist/api/conversations/list.query.d.ts +94 -0
  8. package/dist/api/conversations/projection.d.ts +14 -0
  9. package/dist/api/conversations/schema.d.ts +683 -0
  10. package/dist/{reporting → api}/conversations/shared.d.ts +1 -5
  11. package/dist/api/conversations/stats.d.ts +3 -0
  12. package/dist/api/conversations/stats.query.d.ts +3 -0
  13. package/dist/api/conversations/subagent.d.ts +3 -0
  14. package/dist/{reporting → api}/conversations/transcript.d.ts +4 -4
  15. package/dist/api/people/list.d.ts +1 -2
  16. package/dist/api/people/list.query.d.ts +3 -4
  17. package/dist/api/people/profile.d.ts +1 -2
  18. package/dist/api/people/profile.query.d.ts +3 -4
  19. package/dist/api/people/schema.d.ts +193 -0
  20. package/dist/api/people/shared.d.ts +31 -9
  21. package/dist/api/schema.d.ts +20 -0
  22. package/dist/api/schema.js +50 -0
  23. package/dist/api-reference.d.ts +4 -3
  24. package/dist/api.d.ts +3 -0
  25. package/dist/api.js +156 -0
  26. package/dist/app.d.ts +1 -4
  27. package/dist/app.js +108 -370
  28. package/dist/build/virtual-config.d.ts +2 -2
  29. package/dist/{catalog-runtime-2D7IVKUT.js → catalog-runtime-PFHVMHDR.js} +5 -4
  30. package/dist/chat/agent/resume.d.ts +3 -1
  31. package/dist/chat/agent/tools.d.ts +2 -1
  32. package/dist/chat/config.d.ts +1 -8
  33. package/dist/chat/conversations/history.d.ts +241 -27
  34. package/dist/chat/conversations/legacy-advisor-session.d.ts +7 -0
  35. package/dist/chat/conversations/legacy-import.d.ts +2 -2
  36. package/dist/chat/conversations/projection.d.ts +19 -3
  37. package/dist/chat/conversations/sql/legacy-history-import.d.ts +1 -1
  38. package/dist/chat/conversations/sql/store.d.ts +15 -9
  39. package/dist/chat/conversations/store.d.ts +9 -4
  40. package/dist/chat/logging.d.ts +2 -2
  41. package/dist/chat/model-profile.d.ts +14 -0
  42. package/dist/chat/pi/transcript.d.ts +5 -3
  43. package/dist/chat/plugins/auth/oauth-request.d.ts +2 -0
  44. package/dist/chat/resource-events/notification.d.ts +2 -0
  45. package/dist/chat/resource-events/store.d.ts +2 -2
  46. package/dist/chat/runtime/agent-run-outcome.d.ts +3 -0
  47. package/dist/chat/sentry.d.ts +1 -1
  48. package/dist/chat/services/context-compaction.d.ts +15 -0
  49. package/dist/chat/services/turn-result.d.ts +1 -0
  50. package/dist/chat/services/turn-session-record.d.ts +6 -6
  51. package/dist/chat/state/turn-session.d.ts +18 -5
  52. package/dist/chat/task-execution/store.d.ts +2 -2
  53. package/dist/chat/tools/handoff/tool.d.ts +45 -0
  54. package/dist/chat/tools/types.d.ts +7 -2
  55. package/dist/chat/usage.d.ts +27 -13
  56. package/dist/{chunk-6ONME5IG.js → chunk-35BNLDOJ.js} +339 -578
  57. package/dist/{chunk-HDTO3A74.js → chunk-3ECW2K2O.js} +8 -3
  58. package/dist/{chunk-CRRBJ3JP.js → chunk-3F4OPKGF.js} +90 -56
  59. package/dist/chunk-3V7OFJZC.js +264 -0
  60. package/dist/chunk-4ZNGQH7C.js +25 -0
  61. package/dist/{chunk-NSCHU54X.js → chunk-5HLDPVYS.js} +15 -448
  62. package/dist/{chunk-3DA7X2U3.js → chunk-6GWA276C.js} +15 -0
  63. package/dist/{chunk-HZNJLKKI.js → chunk-6U26PYI5.js} +24 -14
  64. package/dist/chunk-AUUCAFDE.js +546 -0
  65. package/dist/{chunk-6APU57E6.js → chunk-B3IT5END.js} +41 -260
  66. package/dist/chunk-CEA3A3UA.js +86 -0
  67. package/dist/{chunk-AYP4ERVZ.js → chunk-D5KZYS4T.js} +1 -1
  68. package/dist/{chunk-GUE7HTNR.js → chunk-EEZCY5WX.js} +79 -29
  69. package/dist/{chunk-E3E2XNCN.js → chunk-EOWXRY5E.js} +2 -2
  70. package/dist/{chunk-5XMNOD3S.js → chunk-FBHZJFHI.js} +3 -3
  71. package/dist/{chunk-CZEX6KIQ.js → chunk-G3E7SCME.js} +0 -23
  72. package/dist/chunk-GUAP3H7O.js +319 -0
  73. package/dist/{chunk-WCMQJE4F.js → chunk-H66UCBPW.js} +7 -5
  74. package/dist/{chunk-WPOJ443W.js → chunk-I35CTGPO.js} +58 -6
  75. package/dist/{chunk-GVOUCI2O.js → chunk-LMJPRYHP.js} +16 -6
  76. package/dist/chunk-LUAE4MGW.js +199 -0
  77. package/dist/{chunk-GY34VGP5.js → chunk-LVA4I4H7.js} +3 -1
  78. package/dist/chunk-LVUKF5CE.js +63 -0
  79. package/dist/{chunk-EKNJM5IG.js → chunk-NG2AV7IB.js} +2 -72
  80. package/dist/chunk-ON6YKIQ7.js +260 -0
  81. package/dist/chunk-PDO5BLNM.js +22 -0
  82. package/dist/chunk-QNVHH5PU.js +20 -0
  83. package/dist/{chunk-XX4N4E6C.js → chunk-T533JLM3.js} +9 -11
  84. package/dist/chunk-UAMKJPDF.js +11 -0
  85. package/dist/chunk-VFH4ZKZT.js +66 -0
  86. package/dist/{chunk-KDPCGVIT.js → chunk-WGXMTFNU.js} +17 -75
  87. package/dist/{chunk-WAGQ377B.js → chunk-WJOSZOON.js} +288 -263
  88. package/dist/chunk-WTYEKPXU.js +451 -0
  89. package/dist/{chunk-HCXXB35T.js → chunk-XIRPQQVE.js} +613 -369
  90. package/dist/cli/chat.js +26 -23
  91. package/dist/cli/check.js +6 -5
  92. package/dist/cli/init.js +12 -0
  93. package/dist/cli/main.js +1 -1
  94. package/dist/cli/plugins.js +14 -11
  95. package/dist/cli/snapshot-warmup.js +10 -8
  96. package/dist/cli/upgrade.js +267 -139
  97. package/dist/db/db.d.ts +4 -0
  98. package/dist/db/schema/conversations.d.ts +128 -0
  99. package/dist/db/schema.d.ts +129 -60
  100. package/dist/{db-W55B74OP.js → db-BWXYIKFQ.js} +5 -5
  101. package/dist/detail-GA6NXWQJ.js +43 -0
  102. package/dist/discovery-RMHYSRL7.js +43 -0
  103. package/dist/handlers/health.d.ts +3 -0
  104. package/dist/instrumentation.js +2 -2
  105. package/dist/{legacy-import-ZPLGLAFD.js → legacy-import-3NQJVGQL.js} +6 -6
  106. package/dist/{api/people/list.js → list-A3WFRIYX.js} +26 -16
  107. package/dist/list-UXJI5Q5X.js +27 -0
  108. package/dist/nitro.d.ts +1 -1
  109. package/dist/nitro.js +2 -1
  110. package/dist/plugin-conversations-DDNGC35A.js +36 -0
  111. package/dist/{api/people/profile.js → profile-4RYDXWTF.js} +33 -18
  112. package/dist/reporting/plugin-conversations.d.ts +3 -0
  113. package/dist/reporting-schema.d.ts +140 -0
  114. package/dist/reporting.d.ts +12 -81
  115. package/dist/{runner-OOSBDYRK.js → runner-JALSYH2P.js} +19 -14
  116. package/dist/{sentry-EGFOV3DH.js → sentry-JMWTS32N.js} +11 -1
  117. package/dist/skills-MAZXZ2KE.js +26 -0
  118. package/dist/stats-XKXH3PTY.js +217 -0
  119. package/dist/subagent-TZ55XY37.js +48 -0
  120. package/dist/usage-schema.d.ts +25 -0
  121. package/dist/{validation-QCGRFRFZ.js → validation-FMEAEKCY.js} +6 -5
  122. package/migrations/0000_initial.sql +115 -0
  123. package/migrations/0001_conversation_metrics.sql +4 -0
  124. package/migrations/README.md +12 -30
  125. package/migrations/meta/0000_snapshot.json +1 -33
  126. package/migrations/meta/0001_snapshot.json +972 -0
  127. package/migrations/meta/_journal.json +9 -2
  128. package/package.json +14 -18
  129. package/dist/api/people/types.d.ts +0 -85
  130. package/dist/chat/advisor-request.d.ts +0 -4
  131. package/dist/chat/conversations/sql/migrations.d.ts +0 -22
  132. package/dist/chat/tools/advisor/session-store.d.ts +0 -9
  133. package/dist/chat/tools/advisor/tool.d.ts +0 -72
  134. package/dist/chunk-N4RNJ32C.js +0 -48
  135. package/dist/chunk-RAIB6WSG.js +0 -142
  136. package/dist/db/schema/migrations.d.ts +0 -58
  137. package/dist/reporting/conversations/context.d.ts +0 -8
  138. package/dist/reporting/conversations/details.d.ts +0 -6
  139. package/dist/reporting/conversations/index.d.ts +0 -4
  140. package/dist/reporting/conversations/statistics.d.ts +0 -4
  141. package/dist/reporting/conversations/summaries.d.ts +0 -17
  142. package/dist/reporting/conversations/types.d.ts +0 -178
  143. package/dist/reporting/conversations.d.ts +0 -1
  144. package/dist/reporting.js +0 -1065
@@ -1,274 +1,247 @@
1
1
  import {
2
- ensureLegacyConversationImport
3
- } from "./chunk-CRRBJ3JP.js";
2
+ commitMessages,
3
+ loadTurnProjection
4
+ } from "./chunk-GUAP3H7O.js";
5
+ import {
6
+ usageSchema
7
+ } from "./chunk-PDO5BLNM.js";
4
8
  import {
5
- contextProvenance,
6
- getAgentStepStore,
7
9
  getConversationStore,
8
10
  instructionActors,
9
11
  instructionProvenanceFor
10
- } from "./chunk-6ONME5IG.js";
12
+ } from "./chunk-35BNLDOJ.js";
11
13
  import {
12
14
  getStateAdapter
13
- } from "./chunk-GUE7HTNR.js";
15
+ } from "./chunk-EEZCY5WX.js";
14
16
  import {
15
17
  toStoredSlackActor
16
- } from "./chunk-WPOJ443W.js";
18
+ } from "./chunk-I35CTGPO.js";
19
+ import {
20
+ isRecord,
21
+ toOptionalNumber,
22
+ toOptionalString
23
+ } from "./chunk-OB42YVAE.js";
17
24
 
18
- // src/chat/conversations/projection.ts
19
- import { isDeepStrictEqual } from "util";
20
- function authorizationObservationMessage(entry, createdAtMs) {
21
- const label = entry.kind === "mcp" ? "MCP authorization" : "Authorization";
25
+ // src/chat/state/conversation.ts
26
+ function defaultConversationState() {
27
+ const nowMs = Date.now();
22
28
  return {
23
- role: "user",
24
- content: [
25
- {
26
- type: "text",
27
- text: `${label} completed for provider "${entry.provider}". Continue the blocked request and retry the provider operation if needed.`
28
- }
29
- ],
30
- timestamp: createdAtMs
29
+ schemaVersion: 1,
30
+ messages: [],
31
+ compactions: [],
32
+ backfill: {},
33
+ processing: {},
34
+ stats: {
35
+ estimatedContextTokens: 0,
36
+ totalMessageCount: 0,
37
+ compactedMessageCount: 0,
38
+ updatedAtMs: nowMs
39
+ },
40
+ vision: {
41
+ byFileId: {}
42
+ }
31
43
  };
32
44
  }
33
- function piEntryProvenance(entry) {
34
- return entry.provenance ?? contextProvenance;
35
- }
36
- function projectSteps(steps, opts) {
37
- const messages = [];
38
- const provenance = [];
39
- const seqs = [];
40
- for (const step of steps) {
41
- if (opts?.maxSeq !== void 0 && step.seq > opts.maxSeq) {
42
- break;
43
- }
44
- if (step.entry.type === "pi_message") {
45
- messages.push(step.entry.message);
46
- provenance.push(piEntryProvenance(step.entry));
47
- seqs.push(step.seq);
48
- continue;
49
- }
50
- if (step.entry.type === "authorization_completed") {
51
- messages.push(
52
- authorizationObservationMessage(step.entry, step.createdAtMs)
53
- );
54
- provenance.push(contextProvenance);
55
- seqs.push(step.seq);
56
- }
45
+ function coercePendingAuthState(value) {
46
+ if (!isRecord(value)) {
47
+ return void 0;
57
48
  }
58
- return { messages, provenance, seqs };
59
- }
60
- function connectedMcpProvidersFromSteps(steps) {
61
- const providers = /* @__PURE__ */ new Set();
62
- for (const step of steps) {
63
- if (step.entry.type === "mcp_provider_connected") {
64
- providers.add(step.entry.provider);
65
- }
49
+ const kind = value.kind;
50
+ const provider = toOptionalString(value.provider);
51
+ const actorId = toOptionalString(value.actorId);
52
+ const scope = toOptionalString(value.scope);
53
+ const sessionId = toOptionalString(value.sessionId);
54
+ const linkSentAtMs = toOptionalNumber(value.linkSentAtMs);
55
+ if (kind !== "mcp" && kind !== "plugin" || !provider || !actorId || !sessionId || typeof linkSentAtMs !== "number") {
56
+ return void 0;
66
57
  }
67
- return [...providers].sort((left, right) => left.localeCompare(right));
68
- }
69
- function isUserMessage(message) {
70
- return message.role === "user";
58
+ return {
59
+ kind,
60
+ provider,
61
+ actorId,
62
+ ...scope ? { scope } : {},
63
+ sessionId,
64
+ linkSentAtMs
65
+ };
71
66
  }
72
- function countMatchingPrefix(left, right) {
73
- const limit = Math.min(left.length, right.length);
74
- for (let index = 0; index < limit; index += 1) {
75
- if (!isDeepStrictEqual(left[index], right[index])) {
76
- return index;
77
- }
67
+ function coerceThreadConversationState(value) {
68
+ if (!isRecord(value)) {
69
+ return defaultConversationState();
78
70
  }
79
- return limit;
80
- }
81
- function resolveCommitProvenance(args) {
82
- if (args.explicitProvenance) {
83
- if (args.explicitProvenance.length !== args.nextMessages.length) {
84
- throw new Error("commit provenance must align one-to-one with messages");
85
- }
86
- return args.explicitProvenance;
71
+ const root = value;
72
+ const rawConversation = isRecord(root.conversation) ? root.conversation : {};
73
+ const base = defaultConversationState();
74
+ const messages = [];
75
+ const rawCompactions = Array.isArray(rawConversation.compactions) ? rawConversation.compactions : [];
76
+ const compactions = [];
77
+ for (const item of rawCompactions) {
78
+ if (!isRecord(item)) continue;
79
+ const id = toOptionalString(item.id);
80
+ const summary = toOptionalString(item.summary);
81
+ const createdAtMs = toOptionalNumber(item.createdAtMs);
82
+ if (!id || !summary || !createdAtMs) continue;
83
+ const coveredMessageIds = Array.isArray(item.coveredMessageIds) ? item.coveredMessageIds.filter(
84
+ (entry) => typeof entry === "string" && entry.length > 0
85
+ ) : [];
86
+ compactions.push({
87
+ id,
88
+ summary,
89
+ createdAtMs,
90
+ coveredMessageIds
91
+ });
87
92
  }
88
- const matchingPrefix = args.matchingPrefix;
89
- const provenance = args.nextMessages.map(
90
- (_, index) => index < matchingPrefix ? args.existing.provenance[index] ?? contextProvenance : contextProvenance
91
- );
92
- if (args.newMessageProvenance) {
93
- for (let index = args.nextMessages.length - 1; index >= matchingPrefix; index -= 1) {
94
- if (isUserMessage(args.nextMessages[index])) {
95
- provenance[index] = args.newMessageProvenance;
96
- break;
97
- }
98
- }
93
+ const rawBackfill = isRecord(rawConversation.backfill) ? rawConversation.backfill : {};
94
+ const backfill = {
95
+ completedAtMs: toOptionalNumber(rawBackfill.completedAtMs),
96
+ source: rawBackfill.source === "recent_messages" || rawBackfill.source === "thread_fetch" ? rawBackfill.source : void 0
97
+ };
98
+ const rawProcessing = isRecord(rawConversation.processing) ? rawConversation.processing : {};
99
+ const processing = {
100
+ activeTurnId: toOptionalString(rawProcessing.activeTurnId),
101
+ lastCompletedAtMs: toOptionalNumber(rawProcessing.lastCompletedAtMs),
102
+ pendingAuth: coercePendingAuthState(rawProcessing.pendingAuth)
103
+ };
104
+ const rawStats = isRecord(rawConversation.stats) ? rawConversation.stats : {};
105
+ const stats = {
106
+ estimatedContextTokens: toOptionalNumber(rawStats.estimatedContextTokens) ?? base.stats.estimatedContextTokens,
107
+ totalMessageCount: toOptionalNumber(rawStats.totalMessageCount) ?? messages.length,
108
+ compactedMessageCount: toOptionalNumber(rawStats.compactedMessageCount) ?? 0,
109
+ updatedAtMs: toOptionalNumber(rawStats.updatedAtMs) ?? base.stats.updatedAtMs
110
+ };
111
+ const rawVision = isRecord(rawConversation.vision) ? rawConversation.vision : {};
112
+ const rawVisionByFileId = isRecord(rawVision.byFileId) ? rawVision.byFileId : {};
113
+ const byFileId = {};
114
+ for (const [fileId, value2] of Object.entries(rawVisionByFileId)) {
115
+ if (typeof fileId !== "string" || fileId.trim().length === 0) continue;
116
+ if (!isRecord(value2)) continue;
117
+ const summary = toOptionalString(value2.summary);
118
+ const analyzedAtMs = toOptionalNumber(value2.analyzedAtMs);
119
+ if (!summary || !analyzedAtMs) continue;
120
+ byFileId[fileId] = {
121
+ summary,
122
+ analyzedAtMs
123
+ };
99
124
  }
100
- if (args.trailingMessageProvenance) {
101
- if (args.trailingMessageProvenance.length > provenance.length) {
102
- throw new Error(
103
- "trailing commit provenance cannot exceed committed messages"
104
- );
105
- }
106
- const newMessageCount = args.nextMessages.length - matchingPrefix;
107
- if (args.trailingMessageProvenance.length > newMessageCount) {
108
- throw new Error(
109
- "trailing commit provenance must align to newly committed messages"
110
- );
125
+ return {
126
+ schemaVersion: 1,
127
+ messages,
128
+ compactions,
129
+ backfill,
130
+ processing,
131
+ stats,
132
+ vision: {
133
+ backfillCompletedAtMs: toOptionalNumber(rawVision.backfillCompletedAtMs),
134
+ byFileId
111
135
  }
112
- const start = provenance.length - args.trailingMessageProvenance.length;
113
- args.trailingMessageProvenance.forEach((entry, offset) => {
114
- provenance[start + offset] = entry;
115
- });
116
- }
117
- return provenance;
118
- }
119
- async function importLegacyIfNeeded(args) {
120
- await ensureLegacyConversationImport({ conversationId: args.conversationId });
121
- }
122
- async function loadProjection(args) {
123
- await importLegacyIfNeeded(args);
124
- const steps = await getAgentStepStore().loadCurrentEpoch(args.conversationId);
125
- return projectSteps(steps).messages;
136
+ };
126
137
  }
127
- async function loadProjectionWithProvenance(args) {
128
- await importLegacyIfNeeded(args);
129
- const steps = await getAgentStepStore().loadCurrentEpoch(args.conversationId);
130
- const { messages, provenance } = projectSteps(steps);
131
- return { messages, provenance };
138
+ function buildConversationStatePatch(conversation) {
139
+ const {
140
+ compactions: _compactions,
141
+ messages: _messages,
142
+ ...scratch
143
+ } = conversation;
144
+ return {
145
+ conversation: {
146
+ ...scratch,
147
+ schemaVersion: 1,
148
+ stats: {
149
+ ...conversation.stats,
150
+ totalMessageCount: conversation.messages.length,
151
+ updatedAtMs: Date.now()
152
+ }
153
+ }
154
+ };
132
155
  }
133
- async function loadTurnProjection(args) {
134
- await importLegacyIfNeeded(args);
135
- const stepStore = getAgentStepStore();
136
- if (args.committedSeq < 0) {
137
- return projectSteps(await stepStore.loadCurrentEpoch(args.conversationId));
138
- }
139
- const history = await stepStore.loadHistory(args.conversationId);
140
- const committedStep = history.find((step) => step.seq === args.committedSeq);
141
- if (!committedStep) {
142
- return void 0;
143
- }
144
- const epochSteps = history.filter(
145
- (step) => step.contextEpoch === committedStep.contextEpoch
156
+
157
+ // src/chat/usage.ts
158
+ var agentTurnUsageSchema = usageSchema;
159
+ var COMPONENT_USAGE_FIELDS = [
160
+ "inputTokens",
161
+ "outputTokens",
162
+ "cachedInputTokens",
163
+ "cacheCreationTokens"
164
+ ];
165
+ var COST_COMPONENT_FIELDS = [
166
+ "input",
167
+ "output",
168
+ "cacheRead",
169
+ "cacheWrite"
170
+ ];
171
+ function hasAgentTurnUsage(usage) {
172
+ return Boolean(
173
+ usage && (Object.entries(usage).some(
174
+ ([field, value]) => field !== "cost" && typeof value === "number" && Number.isFinite(value)
175
+ ) || Object.values(usage.cost ?? {}).some(
176
+ (value) => typeof value === "number" && Number.isFinite(value)
177
+ ))
146
178
  );
147
- return args.includeTail ? projectSteps(epochSteps) : projectSteps(epochSteps, { maxSeq: args.committedSeq });
148
179
  }
149
- async function loadConnectedMcpProviders(args) {
150
- await importLegacyIfNeeded(args);
151
- const steps = await getAgentStepStore().loadCurrentEpoch(args.conversationId);
152
- return connectedMcpProvidersFromSteps(steps);
180
+ function getFiniteCount(value) {
181
+ return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : void 0;
153
182
  }
154
- function messageTimestamp(message) {
155
- const timestamp = message.timestamp;
156
- return typeof timestamp === "number" ? timestamp : Date.now();
183
+ function getFiniteCost(value) {
184
+ return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : void 0;
157
185
  }
158
- async function commitMessages(args) {
159
- const stepStore = getAgentStepStore();
160
- const existing = projectSteps(
161
- await stepStore.loadCurrentEpoch(args.conversationId)
162
- );
163
- const matchingPrefix = countMatchingPrefix(existing.messages, args.messages);
164
- const nextProvenance = resolveCommitProvenance({
165
- existing,
166
- nextMessages: args.messages,
167
- matchingPrefix,
168
- ...args.provenance ? { explicitProvenance: args.provenance } : {},
169
- ...args.trailingMessageProvenance ? { trailingMessageProvenance: args.trailingMessageProvenance } : {},
170
- ...args.newMessageProvenance ? { newMessageProvenance: args.newMessageProvenance } : {}
171
- });
172
- if (matchingPrefix === existing.messages.length) {
173
- const newMessages = args.messages.slice(matchingPrefix);
174
- await stepStore.append(
175
- args.conversationId,
176
- newMessages.map((message, index) => ({
177
- entry: {
178
- type: "pi_message",
179
- message,
180
- provenance: nextProvenance[matchingPrefix + index]
181
- },
182
- createdAtMs: messageTimestamp(message)
183
- }))
184
- );
185
- } else {
186
- await stepStore.startEpoch(args.conversationId, {
187
- reason: "rollback",
188
- messages: args.messages.map((message, index) => ({
189
- message,
190
- createdAtMs: messageTimestamp(message),
191
- provenance: nextProvenance[index]
192
- }))
193
- });
194
- }
195
- const committed = projectSteps(
196
- await stepStore.loadCurrentEpoch(args.conversationId)
197
- );
198
- return {
199
- committedSeq: committed.seqs.at(-1) ?? -1,
200
- messageSeqs: committed.seqs,
201
- provenance: nextProvenance
202
- };
186
+ function addCost(left, right) {
187
+ return Math.round(((left ?? 0) + right) * 1e12) / 1e12;
203
188
  }
204
- async function recordMcpProviderConnected(args) {
205
- const stepStore = getAgentStepStore();
206
- const steps = await stepStore.loadCurrentEpoch(args.conversationId);
207
- if (connectedMcpProvidersFromSteps(steps).includes(args.provider)) {
208
- return;
189
+ function getComponentTotal(usage) {
190
+ let total;
191
+ for (const field of COMPONENT_USAGE_FIELDS) {
192
+ const value = getFiniteCount(usage[field]);
193
+ if (value === void 0) continue;
194
+ total = (total ?? 0) + value;
209
195
  }
210
- await stepStore.append(args.conversationId, [
211
- {
212
- entry: { type: "mcp_provider_connected", provider: args.provider },
213
- createdAtMs: Date.now()
196
+ return total;
197
+ }
198
+ function addAgentTurnUsage(...usages) {
199
+ const components = {};
200
+ let componentTotal;
201
+ let totalOnlyTokens;
202
+ let reasoningTokens;
203
+ const cost = {};
204
+ for (const usage of usages) {
205
+ if (!usage) continue;
206
+ const reasoning = getFiniteCount(usage.reasoningTokens);
207
+ if (reasoning !== void 0) {
208
+ reasoningTokens = (reasoningTokens ?? 0) + reasoning;
214
209
  }
215
- ]);
216
- }
217
- async function recordAuthorizationRequested(args) {
218
- const stepStore = getAgentStepStore();
219
- const steps = await stepStore.loadCurrentEpoch(args.conversationId);
220
- if (steps.some(
221
- (step) => step.entry.type === "authorization_requested" && step.entry.authorizationId === args.authorizationId
222
- )) {
223
- return;
224
- }
225
- await stepStore.append(args.conversationId, [
226
- {
227
- entry: {
228
- type: "authorization_requested",
229
- kind: args.kind,
230
- provider: args.provider,
231
- actorId: args.actorId,
232
- authorizationId: args.authorizationId,
233
- delivery: args.delivery
234
- },
235
- createdAtMs: Date.now()
210
+ if (usage.cost) {
211
+ for (const field of [...COST_COMPONENT_FIELDS, "total"]) {
212
+ const value = getFiniteCost(usage.cost[field]);
213
+ if (value === void 0) continue;
214
+ cost[field] = addCost(cost[field], value);
215
+ }
236
216
  }
237
- ]);
238
- }
239
- async function recordAuthorizationCompleted(args) {
240
- const stepStore = getAgentStepStore();
241
- const steps = await stepStore.loadCurrentEpoch(args.conversationId);
242
- if (steps.some(
243
- (step) => step.entry.type === "authorization_completed" && step.entry.authorizationId === args.authorizationId
244
- )) {
245
- return;
246
- }
247
- await stepStore.append(args.conversationId, [
248
- {
249
- entry: {
250
- type: "authorization_completed",
251
- kind: args.kind,
252
- provider: args.provider,
253
- actorId: args.actorId,
254
- authorizationId: args.authorizationId
255
- },
256
- createdAtMs: Date.now()
217
+ const usageComponentTotal = getComponentTotal(usage);
218
+ if (usageComponentTotal !== void 0) {
219
+ componentTotal = (componentTotal ?? 0) + usageComponentTotal;
220
+ for (const field of COMPONENT_USAGE_FIELDS) {
221
+ const value = getFiniteCount(usage[field]);
222
+ if (value === void 0) continue;
223
+ components[field] = (components[field] ?? 0) + value;
224
+ }
225
+ continue;
257
226
  }
258
- ]);
259
- }
260
- async function recordToolExecutionStarted(args) {
261
- await getAgentStepStore().append(args.conversationId, [
262
- {
263
- entry: {
264
- type: "tool_execution_started",
265
- toolCallId: args.toolCallId,
266
- toolName: args.toolName,
267
- ...args.args !== void 0 ? { args: args.args } : {}
268
- },
269
- createdAtMs: args.createdAtMs ?? Date.now()
227
+ const totalTokens = getFiniteCount(usage.totalTokens);
228
+ if (totalTokens !== void 0) {
229
+ totalOnlyTokens = (totalOnlyTokens ?? 0) + totalTokens;
270
230
  }
271
- ]);
231
+ }
232
+ if (totalOnlyTokens !== void 0) {
233
+ return {
234
+ totalTokens: totalOnlyTokens + (componentTotal ?? 0),
235
+ ...reasoningTokens !== void 0 ? { reasoningTokens } : {},
236
+ ...Object.keys(cost).length > 0 ? { cost } : {}
237
+ };
238
+ }
239
+ const result = {
240
+ ...components,
241
+ ...reasoningTokens !== void 0 ? { reasoningTokens } : {},
242
+ ...Object.keys(cost).length > 0 ? { cost } : {}
243
+ };
244
+ return hasAgentTurnUsage(result) ? result : void 0;
272
245
  }
273
246
 
274
247
  // src/chat/state/turn-session.ts
@@ -282,6 +255,7 @@ import { z } from "zod";
282
255
  var AGENT_TURN_SESSION_PREFIX = "junior:agent_turn_session";
283
256
  var AGENT_TURN_SESSION_INDEX_KEY = `${AGENT_TURN_SESSION_PREFIX}:index`;
284
257
  var AGENT_TURN_SESSION_INDEX_MAX_LENGTH = 5e3;
258
+ var AGENT_TURN_SESSION_INDEX_READ_CONCURRENCY = 25;
285
259
  var AGENT_TURN_SESSION_TTL_MS = THREAD_STATE_TTL_MS;
286
260
  var agentTurnSessionStatusSchema = z.enum([
287
261
  "running",
@@ -303,13 +277,6 @@ var agentTurnResumeReasonSchema = z.enum([
303
277
  ]);
304
278
  var nonNegativeNumberSchema = z.number().finite().nonnegative();
305
279
  var seqCursorSchema = z.number().int().min(-1);
306
- var agentTurnUsageSchema = z.object({
307
- inputTokens: nonNegativeNumberSchema.optional(),
308
- outputTokens: nonNegativeNumberSchema.optional(),
309
- cachedInputTokens: nonNegativeNumberSchema.optional(),
310
- cacheCreationTokens: nonNegativeNumberSchema.optional(),
311
- totalTokens: nonNegativeNumberSchema.optional()
312
- }).strict();
313
280
  var agentTurnSessionSummarySchema = z.object({
314
281
  channelName: z.string().min(1).optional(),
315
282
  version: z.number().int().nonnegative(),
@@ -334,6 +301,7 @@ var agentTurnSessionSummarySchema = z.object({
334
301
  updatedAtMs: nonNegativeNumberSchema
335
302
  }).strict();
336
303
  var storedAgentTurnSessionRecordSchema = agentTurnSessionSummarySchema.extend({
304
+ actors: z.array(actorSchema).optional(),
337
305
  committedSeq: seqCursorSchema,
338
306
  errorMessage: z.string().optional(),
339
307
  turnStartSeq: seqCursorSchema.optional()
@@ -395,6 +363,10 @@ async function recordConversationActivityMetadata(args) {
395
363
  destination: args.summary.destination,
396
364
  execution: conversationExecutionFromSummary(args.summary),
397
365
  lastActivityAtMs: args.summary.updatedAtMs,
366
+ metrics: {
367
+ durationMs: args.summary.cumulativeDurationMs,
368
+ ...args.summary.cumulativeUsage ? { usage: args.summary.cumulativeUsage } : {}
369
+ },
398
370
  actor: sessionLogActor(args.summary.actor),
399
371
  source,
400
372
  updatedAtMs: args.nowMs,
@@ -414,7 +386,7 @@ function materializeAgentTurnSessionRecord(stored, piProjection, turnStartMessag
414
386
  updatedAtMs: stored.updatedAtMs,
415
387
  piMessages: piProjection.messages,
416
388
  piMessageProvenance: piProjection.provenance,
417
- actors: instructionActors(piProjection.provenance),
389
+ actors: stored.actors ?? instructionActors(piProjection.provenance),
418
390
  cumulativeDurationMs: stored.cumulativeDurationMs,
419
391
  ...stored.destination ? { destination: stored.destination } : {},
420
392
  ...stored.source ? { source: stored.source } : {},
@@ -483,6 +455,7 @@ function buildStoredRecord(args) {
483
455
  ...args.destination ? { destination: args.destination } : {},
484
456
  ...args.source ? { source: args.source } : {},
485
457
  ...args.actor ? { actor: args.actor } : {},
458
+ ...args.actors ? { actors: args.actors } : {},
486
459
  ...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
487
460
  ...args.modelId ? { modelId: args.modelId } : {},
488
461
  ...args.reasoningLevel ? { reasoningLevel: args.reasoningLevel } : {},
@@ -508,6 +481,7 @@ async function setStoredRecord(args) {
508
481
  args.ttlMs
509
482
  );
510
483
  const {
484
+ actors: _actors,
511
485
  committedSeq: _committedSeq,
512
486
  errorMessage: _errorMessage,
513
487
  turnStartSeq: _turnStartSeq,
@@ -555,6 +529,7 @@ async function updateAgentTurnSessionState(args) {
555
529
  ...args.existing.modelId ? { modelId: args.existing.modelId } : {},
556
530
  ...args.existing.reasoningLevel ? { reasoningLevel: args.existing.reasoningLevel } : {},
557
531
  ...args.existing.actor ? { actor: args.existing.actor } : {},
532
+ actors: args.existing.actors,
558
533
  ...args.existing.resumeReason ? { resumeReason: args.existing.resumeReason } : {},
559
534
  ...args.existing.resumedFromSliceId !== void 0 ? { resumedFromSliceId: args.existing.resumedFromSliceId } : {},
560
535
  ...args.existing.surface ? { surface: args.existing.surface } : {},
@@ -572,6 +547,7 @@ async function upsertAgentTurnSessionRecord(args) {
572
547
  const instructionActor = args.actor ?? existingRecord?.actor;
573
548
  const commit = await commitMessages({
574
549
  conversationId: args.conversationId,
550
+ modelId: args.modelId,
575
551
  messages: args.piMessages,
576
552
  ...instructionActor ? { newMessageProvenance: instructionProvenanceFor(instructionActor) } : {},
577
553
  ...args.trailingMessageProvenance ? { trailingMessageProvenance: args.trailingMessageProvenance } : {}
@@ -601,11 +577,16 @@ async function upsertAgentTurnSessionRecord(args) {
601
577
  ...args.destination ?? existingRecord?.destination ? { destination: args.destination ?? existingRecord?.destination } : {},
602
578
  ...args.source ?? existingRecord?.source ? { source: args.source ?? existingRecord?.source } : {},
603
579
  ...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
604
- ...existingRecord?.modelId ?? args.modelId ? { modelId: existingRecord?.modelId ?? args.modelId } : {},
580
+ modelId: args.modelId,
605
581
  ...args.reasoningLevel ?? existingRecord?.reasoningLevel ? {
606
582
  reasoningLevel: args.reasoningLevel ?? existingRecord?.reasoningLevel
607
583
  } : {},
608
584
  ...args.actor ?? existingRecord?.actor ? { actor: args.actor ?? existingRecord?.actor } : {},
585
+ actors: instructionActors([
586
+ ...(existingRecord?.actors ?? []).map(instructionProvenanceFor),
587
+ ...(args.actors ?? []).map(instructionProvenanceFor),
588
+ ...commit.provenance
589
+ ]),
609
590
  ...args.resumeReason ? { resumeReason: args.resumeReason } : {},
610
591
  ...args.errorMessage ? { errorMessage: args.errorMessage } : {},
611
592
  ...args.resumedFromSliceId !== void 0 ? { resumedFromSliceId: args.resumedFromSliceId } : {},
@@ -637,7 +618,7 @@ async function recordAgentTurnSessionSummary(args) {
637
618
  ...args.source ?? existing?.source ? { source: args.source ?? existing?.source } : {},
638
619
  ...args.actor ?? existing?.actor ? { actor: args.actor ?? existing?.actor } : {},
639
620
  ...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : existing?.loadedSkillNames ? { loadedSkillNames: existing.loadedSkillNames } : {},
640
- ...existing?.modelId ?? args.modelId ? { modelId: existing?.modelId ?? args.modelId } : {},
621
+ ...args.modelId ?? existing?.modelId ? { modelId: args.modelId ?? existing?.modelId } : {},
641
622
  ...args.reasoningLevel ?? existing?.reasoningLevel ? { reasoningLevel: args.reasoningLevel ?? existing?.reasoningLevel } : {},
642
623
  ...args.resumeReason ? { resumeReason: args.resumeReason } : {},
643
624
  ...args.surface ?? existing?.surface ? { surface: args.surface ?? existing?.surface } : {},
@@ -651,8 +632,7 @@ async function recordAgentTurnSessionSummary(args) {
651
632
  });
652
633
  await appendAgentTurnSessionSummary(summary, ttlMs);
653
634
  }
654
- async function readAgentTurnSessionSummariesFromIndex(key) {
655
- const stateAdapter = getStateAdapter();
635
+ async function readAgentTurnSessionSummariesFromIndex(key, stateAdapter) {
656
636
  await stateAdapter.connect();
657
637
  const values = await stateAdapter.getList(key);
658
638
  const summaries = /* @__PURE__ */ new Map();
@@ -668,13 +648,58 @@ async function readAgentTurnSessionSummariesFromIndex(key) {
668
648
  );
669
649
  }
670
650
  async function listAgentTurnSessionSummariesForConversation(conversationId) {
651
+ const stateAdapter = getStateAdapter();
671
652
  const summaries = await readAgentTurnSessionSummariesFromIndex(
672
- agentTurnSessionConversationIndexKey(conversationId)
653
+ agentTurnSessionConversationIndexKey(conversationId),
654
+ stateAdapter
673
655
  );
674
656
  if (summaries.length > 0) {
675
657
  return summaries;
676
658
  }
677
- return (await readAgentTurnSessionSummariesFromIndex(AGENT_TURN_SESSION_INDEX_KEY)).filter((summary) => summary.conversationId === conversationId);
659
+ return (await readAgentTurnSessionSummariesFromIndex(
660
+ AGENT_TURN_SESSION_INDEX_KEY,
661
+ stateAdapter
662
+ )).filter((summary) => summary.conversationId === conversationId);
663
+ }
664
+ async function listAgentTurnSessionSummariesForConversations(stateAdapter, conversationIds) {
665
+ const ids = [...new Set(conversationIds)];
666
+ const globalSummaries = await readAgentTurnSessionSummariesFromIndex(
667
+ AGENT_TURN_SESSION_INDEX_KEY,
668
+ stateAdapter
669
+ );
670
+ const globalByConversation = /* @__PURE__ */ new Map();
671
+ for (const summary of globalSummaries) {
672
+ globalByConversation.set(summary.conversationId, [
673
+ ...globalByConversation.get(summary.conversationId) ?? [],
674
+ summary
675
+ ]);
676
+ }
677
+ const summariesByConversation = /* @__PURE__ */ new Map();
678
+ let nextIndex = 0;
679
+ const readNext = async () => {
680
+ while (nextIndex < ids.length) {
681
+ const conversationId = ids[nextIndex];
682
+ nextIndex += 1;
683
+ if (!conversationId) continue;
684
+ const summaries = await readAgentTurnSessionSummariesFromIndex(
685
+ agentTurnSessionConversationIndexKey(conversationId),
686
+ stateAdapter
687
+ );
688
+ summariesByConversation.set(
689
+ conversationId,
690
+ summaries.length > 0 ? summaries : globalByConversation.get(conversationId) ?? []
691
+ );
692
+ }
693
+ };
694
+ await Promise.all(
695
+ Array.from(
696
+ {
697
+ length: Math.min(AGENT_TURN_SESSION_INDEX_READ_CONCURRENCY, ids.length)
698
+ },
699
+ readNext
700
+ )
701
+ );
702
+ return summariesByConversation;
678
703
  }
679
704
  async function abandonAgentTurnSessionRecord(args) {
680
705
  const existing = await getAgentTurnSessionRecord(
@@ -705,20 +730,20 @@ async function failAgentTurnSessionRecord(args) {
705
730
  });
706
731
  }
707
732
 
733
+ // src/chat/state/ttl.ts
734
+ var JUNIOR_THREAD_STATE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
735
+
708
736
  export {
709
- projectSteps,
710
- loadProjection,
711
- loadProjectionWithProvenance,
712
- loadConnectedMcpProviders,
713
- commitMessages,
714
- recordMcpProviderConnected,
715
- recordAuthorizationRequested,
716
- recordAuthorizationCompleted,
717
- recordToolExecutionStarted,
737
+ hasAgentTurnUsage,
738
+ addAgentTurnUsage,
718
739
  getAgentTurnSessionRecord,
719
740
  upsertAgentTurnSessionRecord,
720
741
  recordAgentTurnSessionSummary,
721
742
  listAgentTurnSessionSummariesForConversation,
743
+ listAgentTurnSessionSummariesForConversations,
722
744
  abandonAgentTurnSessionRecord,
723
- failAgentTurnSessionRecord
745
+ failAgentTurnSessionRecord,
746
+ JUNIOR_THREAD_STATE_TTL_MS,
747
+ coerceThreadConversationState,
748
+ buildConversationStatePatch
724
749
  };