@sentry/junior 0.112.0 → 0.113.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 (49) hide show
  1. package/dist/{agent-hooks-6MVTPKP4.js → agent-hooks-BKEMWR3Z.js} +8 -8
  2. package/dist/api/conversations/cursor.d.ts +17 -0
  3. package/dist/api/conversations/detail.d.ts +16 -18
  4. package/dist/api/conversations/event-list.d.ts +91 -0
  5. package/dist/api/conversations/event-page.d.ts +18 -0
  6. package/dist/api/conversations/events.d.ts +5 -1
  7. package/dist/api/conversations/projection.d.ts +6 -1
  8. package/dist/api/schema/conversation.d.ts +132 -51
  9. package/dist/api/schema.d.ts +2 -2
  10. package/dist/api/schema.js +7 -1
  11. package/dist/api.js +444 -138
  12. package/dist/app.js +20 -20
  13. package/dist/{catalog-runtime-UEO25P2J.js → catalog-runtime-6XYOUVRZ.js} +5 -5
  14. package/dist/chat/pi/transcript.d.ts +2 -0
  15. package/dist/chat/services/provider-error.d.ts +39 -0
  16. package/dist/chat/services/provider-retry.d.ts +3 -6
  17. package/dist/chat/task-execution/state.d.ts +2 -2
  18. package/dist/chat/task-execution/store.d.ts +1 -1
  19. package/dist/{chunk-CSS7JHEM.js → chunk-36UJA3OD.js} +1 -1
  20. package/dist/{chunk-4EJO4HIC.js → chunk-3CTIAKSS.js} +1 -1
  21. package/dist/{chunk-66IP6WED.js → chunk-4VKFQPCW.js} +4 -4
  22. package/dist/{chunk-GJG67YFS.js → chunk-4WZ7YKDF.js} +2 -2
  23. package/dist/{chunk-SKEMI4IF.js → chunk-DXRPHBOU.js} +132 -23
  24. package/dist/{chunk-GR4IB6OQ.js → chunk-H5ON7XTE.js} +73 -28
  25. package/dist/{chunk-X3SP5RDW.js → chunk-HMRVILWZ.js} +1 -1
  26. package/dist/{chunk-2FJ6WEYV.js → chunk-K5BXPM74.js} +1 -1
  27. package/dist/{chunk-QIALGHRN.js → chunk-LLKDNPYV.js} +2 -2
  28. package/dist/{chunk-ZHP4KNF7.js → chunk-LR2SKL5Y.js} +1 -1
  29. package/dist/{chunk-AU2JVUDF.js → chunk-NP7TYFYR.js} +1 -1
  30. package/dist/{chunk-TZU3R5OC.js → chunk-OXE5ZUTS.js} +1 -1
  31. package/dist/{chunk-NDQL4ESR.js → chunk-QTS66GMR.js} +2 -2
  32. package/dist/{chunk-PK4ETCUQ.js → chunk-RCBUZGIT.js} +217 -199
  33. package/dist/{chunk-JE23YR3X.js → chunk-UKGNSJQQ.js} +50 -42
  34. package/dist/{chunk-AWNDKI5K.js → chunk-WBB22ANL.js} +20 -15
  35. package/dist/{chunk-VBSVNOLA.js → chunk-YJ44UT7D.js} +5 -5
  36. package/dist/{chunk-IV2O4HQJ.js → chunk-ZRSUCJGA.js} +1 -1
  37. package/dist/cli/chat.js +19 -19
  38. package/dist/cli/check.js +6 -6
  39. package/dist/cli/plugins.js +11 -11
  40. package/dist/cli/snapshot-warmup.js +8 -8
  41. package/dist/cli/upgrade.js +5 -5
  42. package/dist/{db-P5H5RVQV.js → db-2ASK6MV6.js} +5 -5
  43. package/dist/instrumentation.js +2 -2
  44. package/dist/nitro.js +2 -2
  45. package/dist/{runner-B4ZDUO3R.js → runner-L363K62Z.js} +12 -13
  46. package/dist/{skills-GWTOZ3XV.js → skills-VCPG3NOO.js} +6 -6
  47. package/dist/{validation-QFIC6XH6.js → validation-GY7AJBLC.js} +6 -6
  48. package/dist/version.js +1 -1
  49. package/package.json +6 -6
package/dist/app.js CHANGED
@@ -6,14 +6,14 @@ import {
6
6
  processPluginTask,
7
7
  requireTurnFailureEventId,
8
8
  scheduleSessionCompletedPluginTasks
9
- } from "./chunk-AWNDKI5K.js";
9
+ } from "./chunk-WBB22ANL.js";
10
10
  import {
11
11
  GET,
12
12
  buildSentryConversationUrl,
13
13
  conversationVisibilityFromSlackChannelType,
14
14
  resolveSlackChannelTypeFromMessage,
15
15
  resolveSlackConversationContext
16
- } from "./chunk-2FJ6WEYV.js";
16
+ } from "./chunk-K5BXPM74.js";
17
17
  import "./chunk-2ZTTXTSZ.js";
18
18
  import {
19
19
  ConversationQueueMessageRejectedError,
@@ -31,7 +31,7 @@ import {
31
31
  import {
32
32
  validatePluginEgressCredentialHooks,
33
33
  validatePluginRegistrations
34
- } from "./chunk-4EJO4HIC.js";
34
+ } from "./chunk-3CTIAKSS.js";
35
35
  import {
36
36
  defineJuniorPlugins,
37
37
  pluginCatalogConfigFromEnv,
@@ -84,7 +84,7 @@ import {
84
84
  splitSlackReplyText,
85
85
  startOAuthFlow,
86
86
  truncateStatusText
87
- } from "./chunk-GR4IB6OQ.js";
87
+ } from "./chunk-H5ON7XTE.js";
88
88
  import {
89
89
  ConversationTurnBoundaryError,
90
90
  CooperativeTurnYieldError,
@@ -140,11 +140,13 @@ import {
140
140
  recordAuthorizationCompleted,
141
141
  recordDeliveredAssistantMessage,
142
142
  startActiveTurn,
143
+ stripRuntimeTurnContext,
144
+ trimTrailingAssistantMessages,
143
145
  turnHasReply,
144
146
  updateConversationStats,
145
147
  upsertConversationMessage
146
- } from "./chunk-SKEMI4IF.js";
147
- import "./chunk-66IP6WED.js";
148
+ } from "./chunk-DXRPHBOU.js";
149
+ import "./chunk-4VKFQPCW.js";
148
150
  import "./chunk-MU6HHZEN.js";
149
151
  import "./chunk-PDO5BLNM.js";
150
152
  import "./chunk-UIE3R5XU.js";
@@ -163,12 +165,12 @@ import {
163
165
  validatePlugins,
164
166
  verifyScheduledTaskCredentialSubject,
165
167
  verifySlackDirectCredentialSubject
166
- } from "./chunk-VBSVNOLA.js";
168
+ } from "./chunk-YJ44UT7D.js";
167
169
  import {
168
170
  acquireActiveLock,
169
171
  getDefaultRedisStateAdapterFor,
170
172
  getStateAdapter
171
- } from "./chunk-X3SP5RDW.js";
173
+ } from "./chunk-HMRVILWZ.js";
172
174
  import {
173
175
  SlackActionError,
174
176
  downloadPrivateSlackFile,
@@ -179,7 +181,7 @@ import {
179
181
  normalizeSlackConversationId,
180
182
  runWithSlackInstallationToken,
181
183
  runWithWorkspaceTeamId
182
- } from "./chunk-NDQL4ESR.js";
184
+ } from "./chunk-QTS66GMR.js";
183
185
  import {
184
186
  contextProvenance,
185
187
  createSlackDestination,
@@ -193,7 +195,7 @@ import {
193
195
  requireSlackDestination,
194
196
  sameDestination,
195
197
  withConversationEventLock
196
- } from "./chunk-QIALGHRN.js";
198
+ } from "./chunk-LLKDNPYV.js";
197
199
  import {
198
200
  juniorConversationEvents,
199
201
  juniorConversations
@@ -219,10 +221,8 @@ import {
219
221
  modelIdForProfile,
220
222
  normalizeSlackEmojiName,
221
223
  setSlackReactionConfig,
222
- standardModelId,
223
- stripRuntimeTurnContext,
224
- trimTrailingAssistantMessages
225
- } from "./chunk-PK4ETCUQ.js";
224
+ standardModelId
225
+ } from "./chunk-RCBUZGIT.js";
226
226
  import {
227
227
  juniorDestinations
228
228
  } from "./chunk-VFUK3X5B.js";
@@ -234,16 +234,16 @@ import {
234
234
  } from "./chunk-6Y5L4BOM.js";
235
235
  import {
236
236
  discoverSkills
237
- } from "./chunk-GJG67YFS.js";
237
+ } from "./chunk-4WZ7YKDF.js";
238
238
  import {
239
239
  pluginCatalogRuntime
240
- } from "./chunk-ZHP4KNF7.js";
240
+ } from "./chunk-LR2SKL5Y.js";
241
241
  import {
242
242
  buildOAuthTokenRequest,
243
243
  credentialSubjectSchema,
244
244
  hasRequiredOAuthScope,
245
245
  parseOAuthTokenResponse
246
- } from "./chunk-TZU3R5OC.js";
246
+ } from "./chunk-OXE5ZUTS.js";
247
247
  import {
248
248
  createActor,
249
249
  createSlackActor,
@@ -268,7 +268,7 @@ import {
268
268
  setTags,
269
269
  withContext,
270
270
  withSpan
271
- } from "./chunk-CSS7JHEM.js";
271
+ } from "./chunk-36UJA3OD.js";
272
272
  import {
273
273
  isRecord,
274
274
  parseSlackChannelId,
@@ -285,10 +285,10 @@ import {
285
285
  } from "./chunk-3V7OFJZC.js";
286
286
  import {
287
287
  JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE
288
- } from "./chunk-AU2JVUDF.js";
288
+ } from "./chunk-NP7TYFYR.js";
289
289
  import {
290
290
  JUNIOR_VERSION
291
- } from "./chunk-IV2O4HQJ.js";
291
+ } from "./chunk-ZRSUCJGA.js";
292
292
  import "./chunk-MLKGABMK.js";
293
293
 
294
294
  // src/app.ts
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  pluginCatalogRuntime
3
- } from "./chunk-ZHP4KNF7.js";
4
- import "./chunk-TZU3R5OC.js";
3
+ } from "./chunk-LR2SKL5Y.js";
4
+ import "./chunk-OXE5ZUTS.js";
5
5
  import "./chunk-C4MZ3GP5.js";
6
- import "./chunk-CSS7JHEM.js";
6
+ import "./chunk-36UJA3OD.js";
7
7
  import "./chunk-42WRQLJ6.js";
8
8
  import "./chunk-6GWA276C.js";
9
9
  import "./chunk-B3IT5END.js";
10
10
  import "./chunk-3V7OFJZC.js";
11
- import "./chunk-AU2JVUDF.js";
12
- import "./chunk-IV2O4HQJ.js";
11
+ import "./chunk-NP7TYFYR.js";
12
+ import "./chunk-ZRSUCJGA.js";
13
13
  import "./chunk-MLKGABMK.js";
14
14
  export {
15
15
  pluginCatalogRuntime
@@ -19,6 +19,8 @@ export declare function isToolResultError(result: unknown): boolean;
19
19
  export declare function normalizeToolNameFromResult(result: unknown): string | undefined;
20
20
  /** Extract role string from a raw Pi message. */
21
21
  export declare function getPiMessageRole(value: unknown): string | undefined;
22
+ /** Return whether Pi can continue directly from the current history tail. */
23
+ export declare function isContinuablePiBoundary(messages: readonly PiMessage[]): boolean;
22
24
  /** Concatenate text content parts from an assistant message. */
23
25
  export declare function extractAssistantText(message: AssistantMessage): string;
24
26
  /** Return assistant messages that belong to the terminal post-tool reply phase. */
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Normalize failures at the model-provider boundary.
3
+ *
4
+ * Junior owns stable failure kinds, safe user copy, and telemetry fields. Pi
5
+ * remains the source of truth for retrying assistant error messages.
6
+ */
7
+ export type ProviderErrorKind = "auth" | "permission" | "rate_limit" | "capacity" | "timeout" | "network" | "server" | "invalid_request" | "invalid_response" | "quota" | "content_policy" | "unknown";
8
+ interface ProviderErrorOptions {
9
+ kind?: ProviderErrorKind;
10
+ modelId?: string;
11
+ /** Pi's decision for an assistant error; explicit terminal kinds still win. */
12
+ retryable?: boolean;
13
+ }
14
+ interface ProviderErrorDetails {
15
+ kind: ProviderErrorKind;
16
+ modelId?: string;
17
+ retryable: boolean;
18
+ retryAfterMs?: number;
19
+ status?: number;
20
+ }
21
+ /** Structured failure produced at an AI provider boundary. */
22
+ export declare class ProviderError extends Error {
23
+ readonly code = "provider_error";
24
+ readonly kind: ProviderErrorKind;
25
+ readonly modelId?: string;
26
+ readonly retryable: boolean;
27
+ readonly retryAfterMs?: number;
28
+ readonly status?: number;
29
+ constructor(details: ProviderErrorDetails);
30
+ }
31
+ /** Normalize SDK, Gateway, and Pi failures into one provider error contract. */
32
+ export declare function createProviderError(error: unknown, options?: ProviderErrorOptions): ProviderError;
33
+ /** Return whether a provider-boundary error should be retried. */
34
+ export declare function isProviderRetryError(error: unknown): error is ProviderError;
35
+ /** Return stable, sanitized copy suitable for a terminal user response. */
36
+ export declare function getProviderErrorUserMessage(error: ProviderError): string;
37
+ /** Return safe structured fields for terminal failure telemetry. */
38
+ export declare function getProviderErrorAttributes(error: ProviderError): Record<string, unknown>;
39
+ export {};
@@ -1,13 +1,10 @@
1
+ import { type AssistantMessage } from "@earendil-works/pi-ai";
1
2
  import type { PiMessage } from "@/chat/pi/messages";
2
- /** Mark failures that cross the AI provider boundary for shared retry handling. */
3
- export declare function createProviderError(error: unknown): Error;
4
- /** Return whether a provider-boundary error should be retried by the worker. */
5
- export declare function isProviderRetryError(error: unknown): boolean;
6
- /** Build the next provider retry step from Pi history, if the turn can resume. */
3
+ /** Apply Junior's retry budget to a retryable Pi assistant failure. */
7
4
  export declare function nextProviderRetry(args: {
8
5
  attempt: number;
6
+ failure?: AssistantMessage;
9
7
  messages: PiMessage[];
10
- retryableFailure: boolean;
11
8
  }): {
12
9
  delayMs: number;
13
10
  messages: PiMessage[];
@@ -16,8 +16,8 @@ export declare const CONVERSATION_WORK_MAX_DELIVERY_ATTEMPTS = 5;
16
16
  declare const inboundMessageSourceSchema: z.ZodEnum<{
17
17
  slack: "slack";
18
18
  plugin: "plugin";
19
- api: "api";
20
19
  local: "local";
20
+ api: "api";
21
21
  internal: "internal";
22
22
  resource_event: "resource_event";
23
23
  scheduler: "scheduler";
@@ -66,8 +66,8 @@ export declare const inboundMessageSchema: z.ZodObject<{
66
66
  source: z.ZodEnum<{
67
67
  slack: "slack";
68
68
  plugin: "plugin";
69
- api: "api";
70
69
  local: "local";
70
+ api: "api";
71
71
  internal: "internal";
72
72
  resource_event: "resource_event";
73
73
  scheduler: "scheduler";
@@ -114,7 +114,7 @@ export declare function drainConversationMailbox(args: Parameters<typeof workSta
114
114
  metadata?: Record<string, unknown> | undefined;
115
115
  };
116
116
  receivedAtMs: number;
117
- source: "slack" | "plugin" | "api" | "local" | "internal" | "resource_event" | "scheduler";
117
+ source: "slack" | "plugin" | "local" | "api" | "internal" | "resource_event" | "scheduler";
118
118
  attemptCount?: number | undefined;
119
119
  injectedAtMs?: number | undefined;
120
120
  }[]>;
@@ -13,7 +13,7 @@ import {
13
13
  } from "./chunk-6GWA276C.js";
14
14
  import {
15
15
  getDeploymentTelemetryAttributes
16
- } from "./chunk-AU2JVUDF.js";
16
+ } from "./chunk-NP7TYFYR.js";
17
17
 
18
18
  // src/chat/logging.ts
19
19
  import path from "path";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pluginCatalogRuntime
3
- } from "./chunk-ZHP4KNF7.js";
3
+ } from "./chunk-LR2SKL5Y.js";
4
4
 
5
5
  // src/chat/plugins/validation.ts
6
6
  import { isDeepStrictEqual } from "util";
@@ -1,21 +1,21 @@
1
1
  import {
2
2
  SANDBOX_WORKSPACE_ROOT,
3
3
  getStateAdapter
4
- } from "./chunk-X3SP5RDW.js";
4
+ } from "./chunk-HMRVILWZ.js";
5
5
  import {
6
6
  sleep
7
7
  } from "./chunk-4ZNGQH7C.js";
8
8
  import {
9
9
  toOptionalTrimmed
10
- } from "./chunk-PK4ETCUQ.js";
10
+ } from "./chunk-RCBUZGIT.js";
11
11
  import {
12
12
  pluginCatalogRuntime
13
- } from "./chunk-ZHP4KNF7.js";
13
+ } from "./chunk-LR2SKL5Y.js";
14
14
  import {
15
15
  setSpanAttributes,
16
16
  setSpanStatus,
17
17
  withSpan
18
- } from "./chunk-CSS7JHEM.js";
18
+ } from "./chunk-36UJA3OD.js";
19
19
 
20
20
  // src/chat/sandbox/runtime-dependencies.ts
21
21
  var GLOBAL_RUNTIME_DEPENDENCIES = [
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  pluginCatalogRuntime
3
- } from "./chunk-ZHP4KNF7.js";
3
+ } from "./chunk-LR2SKL5Y.js";
4
4
  import {
5
5
  logWarn
6
- } from "./chunk-CSS7JHEM.js";
6
+ } from "./chunk-36UJA3OD.js";
7
7
  import {
8
8
  skillRoots
9
9
  } from "./chunk-3V7OFJZC.js";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-MU6HHZEN.js";
5
5
  import {
6
6
  getStateAdapter
7
- } from "./chunk-X3SP5RDW.js";
7
+ } from "./chunk-HMRVILWZ.js";
8
8
  import {
9
9
  contextProvenance,
10
10
  createSqlConversationEventStore,
@@ -15,20 +15,17 @@ import {
15
15
  instructionProvenanceFor,
16
16
  sanitizePostgresJson,
17
17
  withConversationEventLock
18
- } from "./chunk-QIALGHRN.js";
18
+ } from "./chunk-LLKDNPYV.js";
19
19
  import {
20
20
  sleep
21
21
  } from "./chunk-4ZNGQH7C.js";
22
22
  import {
23
23
  botConfig,
24
24
  escapeXml,
25
- getPiMessageRole,
26
25
  resolveGatewayModel,
27
- retainRuntimeTurnContext,
28
26
  standardModelId,
29
- stripRuntimeTurnContext,
30
- trimTrailingAssistantMessages
31
- } from "./chunk-PK4ETCUQ.js";
27
+ unwrapCurrentInstruction
28
+ } from "./chunk-RCBUZGIT.js";
32
29
  import {
33
30
  toStoredSlackActor
34
31
  } from "./chunk-C4MZ3GP5.js";
@@ -37,7 +34,7 @@ import {
37
34
  logException,
38
35
  logWarn,
39
36
  setSpanAttributes
40
- } from "./chunk-CSS7JHEM.js";
37
+ } from "./chunk-36UJA3OD.js";
41
38
  import {
42
39
  isRecord,
43
40
  parseSlackMessageTs,
@@ -45,6 +42,111 @@ import {
45
42
  toOptionalString
46
43
  } from "./chunk-42WRQLJ6.js";
47
44
 
45
+ // src/chat/turn-context-tag.ts
46
+ var TURN_CONTEXT_TAG = "runtime-turn-context";
47
+
48
+ // src/chat/pi/transcript.ts
49
+ var RUNTIME_TURN_CONTEXT_START = `<${TURN_CONTEXT_TAG}>`;
50
+ function userMessageContent(message) {
51
+ const record = message;
52
+ return record.role === "user" && Array.isArray(record.content) ? record.content : void 0;
53
+ }
54
+ function isRuntimeTurnContextPart(part) {
55
+ return part !== null && typeof part === "object" && part.type === "text" && typeof part.text === "string" && part.text.startsWith(RUNTIME_TURN_CONTEXT_START);
56
+ }
57
+ var EMBEDDED_THREAD_CONTEXT_TAGS = [
58
+ "recent-thread-messages",
59
+ "thread-compactions",
60
+ "thread-transcript",
61
+ "thread-background"
62
+ ];
63
+ var EMBEDDED_THREAD_CONTEXT_PATTERN = new RegExp(
64
+ `<(${EMBEDDED_THREAD_CONTEXT_TAGS.join("|")})(?:\\s[^>]*)?>[\\s\\S]*?</\\1>`,
65
+ "g"
66
+ );
67
+ function isAssistantMessage(value) {
68
+ return typeof value === "object" && value !== null && value.role === "assistant";
69
+ }
70
+ function isToolResultMessage(value) {
71
+ return typeof value === "object" && value !== null && value.role === "toolResult";
72
+ }
73
+ function isToolResultError(result) {
74
+ if (!result || typeof result !== "object") return false;
75
+ return Boolean(result.isError);
76
+ }
77
+ function normalizeToolNameFromResult(result) {
78
+ if (!result || typeof result !== "object") return void 0;
79
+ const record = result;
80
+ if (typeof record.toolName === "string" && record.toolName.length > 0) {
81
+ return record.toolName;
82
+ }
83
+ if (typeof record.name === "string" && record.name.length > 0) {
84
+ return record.name;
85
+ }
86
+ return void 0;
87
+ }
88
+ function getPiMessageRole(value) {
89
+ if (!value || typeof value !== "object") {
90
+ return void 0;
91
+ }
92
+ const role = value.role;
93
+ return typeof role === "string" ? role : void 0;
94
+ }
95
+ function isContinuablePiBoundary(messages) {
96
+ const lastRole = getPiMessageRole(messages.at(-1));
97
+ return lastRole === "user" || lastRole === "toolResult";
98
+ }
99
+ function extractAssistantText(message) {
100
+ const content = message.content ?? [];
101
+ return content.filter(
102
+ (part) => part.type === "text" && typeof part.text === "string"
103
+ ).map((part) => part.text).join("\n");
104
+ }
105
+ function getTerminalAssistantMessages(messages) {
106
+ let lastNonAssistantIndex = -1;
107
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
108
+ if (!isAssistantMessage(messages[index])) {
109
+ lastNonAssistantIndex = index;
110
+ break;
111
+ }
112
+ }
113
+ return messages.slice(lastNonAssistantIndex + 1).filter(isAssistantMessage);
114
+ }
115
+ function trimTrailingAssistantMessages(messages) {
116
+ let end = messages.length;
117
+ while (end > 0 && getPiMessageRole(messages[end - 1]) === "assistant") {
118
+ end -= 1;
119
+ }
120
+ return end === messages.length ? [...messages] : messages.slice(0, end);
121
+ }
122
+ function hasRuntimeTurnContext(messages) {
123
+ return messages.some(
124
+ (message) => userMessageContent(message)?.some(isRuntimeTurnContextPart)
125
+ );
126
+ }
127
+ function retainRuntimeTurnContext(messages) {
128
+ return messages.flatMap((message) => {
129
+ const runtimeContent = userMessageContent(message)?.filter(isRuntimeTurnContextPart) ?? [];
130
+ return runtimeContent.length > 0 ? [{ ...message, content: runtimeContent }] : [];
131
+ });
132
+ }
133
+ function instructionTextForProjection(text) {
134
+ const withoutContext = text.replace(EMBEDDED_THREAD_CONTEXT_PATTERN, "").trim();
135
+ return unwrapCurrentInstruction(withoutContext) ?? withoutContext;
136
+ }
137
+ function stripRuntimeTurnContext(messages) {
138
+ return messages.flatMap((message) => {
139
+ const content = userMessageContent(message);
140
+ if (!content) return [message];
141
+ const nextContent = content.filter(
142
+ (part) => !isRuntimeTurnContextPart(part)
143
+ );
144
+ if (nextContent.length === content.length) return [message];
145
+ if (nextContent.length === 0) return [];
146
+ return [{ ...message, content: nextContent }];
147
+ });
148
+ }
149
+
48
150
  // src/chat/conversations/projection.ts
49
151
  import { isDeepStrictEqual } from "util";
50
152
 
@@ -1033,13 +1135,9 @@ function addDurationMs(prior, current) {
1033
1135
  }, void 0);
1034
1136
  return total;
1035
1137
  }
1036
- function isContinuableBoundary(messages) {
1037
- const lastRole = getPiMessageRole(messages.at(-1));
1038
- return lastRole === "user" || lastRole === "toolResult";
1039
- }
1040
1138
  function resumableBoundary(messages, fallbackMessages) {
1041
1139
  const current = trimTrailingAssistantMessages(messages);
1042
- if (current.length > 0 && isContinuableBoundary(current)) {
1140
+ if (current.length > 0 && isContinuablePiBoundary(current)) {
1043
1141
  return current;
1044
1142
  }
1045
1143
  return trimTrailingAssistantMessages(fallbackMessages ?? []);
@@ -1059,7 +1157,7 @@ async function loadTurnSessionRecord(ctx) {
1059
1157
  };
1060
1158
  }
1061
1159
  async function persistRunningSessionRecord(args) {
1062
- if (args.messages.length === 0 || !isContinuableBoundary(args.messages)) {
1160
+ if (args.messages.length === 0 || !isContinuablePiBoundary(args.messages)) {
1063
1161
  return false;
1064
1162
  }
1065
1163
  try {
@@ -1184,7 +1282,7 @@ async function persistAuthPauseSessionRecord(args) {
1184
1282
  args.messages,
1185
1283
  latestSessionRecord?.piMessages
1186
1284
  );
1187
- if (piMessages.length > 0 && !isContinuableBoundary(piMessages)) {
1285
+ if (piMessages.length > 0 && !isContinuablePiBoundary(piMessages)) {
1188
1286
  return void 0;
1189
1287
  }
1190
1288
  return await upsertAgentTurnSessionRecord({
@@ -1241,7 +1339,7 @@ async function persistContinuationSessionRecord(args) {
1241
1339
  args.messages,
1242
1340
  latestSessionRecord?.piMessages
1243
1341
  );
1244
- if (piMessages.length === 0 || !isContinuableBoundary(piMessages)) {
1342
+ if (piMessages.length === 0 || !isContinuablePiBoundary(piMessages)) {
1245
1343
  return void 0;
1246
1344
  }
1247
1345
  const cumulativeDurationMs = addDurationMs(
@@ -1322,17 +1420,14 @@ async function persistContinuationSessionRecord(args) {
1322
1420
  }
1323
1421
  async function persistYieldSessionRecord(args) {
1324
1422
  try {
1423
+ const piMessages = [...args.messages];
1424
+ if (piMessages.length === 0 || !isContinuablePiBoundary(piMessages)) {
1425
+ return void 0;
1426
+ }
1325
1427
  const latestSessionRecord = await getAgentTurnSessionRecord(
1326
1428
  args.conversationId,
1327
1429
  args.sessionId
1328
1430
  );
1329
- const piMessages = resumableBoundary(
1330
- args.messages,
1331
- latestSessionRecord?.piMessages
1332
- );
1333
- if (piMessages.length === 0 || !isContinuableBoundary(piMessages)) {
1334
- return void 0;
1335
- }
1336
1431
  return await upsertAgentTurnSessionRecord({
1337
1432
  ...args.channelName ?? latestSessionRecord?.channelName ? { channelName: args.channelName ?? latestSessionRecord?.channelName } : {},
1338
1433
  conversationId: args.conversationId,
@@ -2533,6 +2628,20 @@ function isPendingAuthLatestRequest(conversation, pendingAuth) {
2533
2628
  }
2534
2629
 
2535
2630
  export {
2631
+ TURN_CONTEXT_TAG,
2632
+ isAssistantMessage,
2633
+ isToolResultMessage,
2634
+ isToolResultError,
2635
+ normalizeToolNameFromResult,
2636
+ getPiMessageRole,
2637
+ isContinuablePiBoundary,
2638
+ extractAssistantText,
2639
+ getTerminalAssistantMessages,
2640
+ trimTrailingAssistantMessages,
2641
+ hasRuntimeTurnContext,
2642
+ retainRuntimeTurnContext,
2643
+ instructionTextForProjection,
2644
+ stripRuntimeTurnContext,
2536
2645
  loadProjection,
2537
2646
  loadConversationProjection,
2538
2647
  openConversationProjection,