@sentry/junior 0.210.1 → 0.211.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-CbAaXPT7.js → agent-XXC-9m0B.js} +356 -31
  2. package/dist/{agent-hooks-CbFeRDRP.js → agent-hooks-7vR8uZw2.js} +6 -6
  3. package/dist/{agent-runner-CcPlgDIx.js → agent-runner-CqXaUvvQ.js} +5 -5
  4. package/dist/api.js +15 -15
  5. package/dist/app.js +30 -48
  6. package/dist/{auth-pause-state-BImcZ0Wo.js → auth-pause-state-r-1vEoSX.js} +6 -6
  7. package/dist/{briefs-D1rYGjCn.js → briefs-DIj_YgLE.js} +2 -2
  8. package/dist/{catalog-runtime-Dj_tC_Hx.js → catalog-runtime-BMMLIpxX.js} +1 -1
  9. package/dist/{checkpoint-CTBVbFYq.js → checkpoint-B6jEtGHN.js} +150 -527
  10. package/dist/cli/chat.js +13 -13
  11. package/dist/cli/check.js +3 -3
  12. package/dist/cli/main.js +1 -1
  13. package/dist/cli/plugins.js +3 -3
  14. package/dist/cli/snapshot-create.js +3 -3
  15. package/dist/cli/upgrade.js +3 -3
  16. package/dist/{client-BvhLSDdj.js → client-BBPo9KiG.js} +2 -2
  17. package/dist/{config-DfGsDs-7.js → config-DMq-uJAV.js} +2 -2
  18. package/dist/{conversation-privacy-Cj51CVTB.js → conversation-privacy-D99FrYrV.js} +1 -1
  19. package/dist/{credential-sync-DfJK1MWh.js → credential-sync-Cz1T9Ws-.js} +23 -32
  20. package/dist/{execution-stats--y9Jggoy.js → execution-stats-BkCnuVBv.js} +4 -4
  21. package/dist/{executor-DotJl2jl.js → executor-B5VURdwg.js} +1 -1
  22. package/dist/instrumentation.js +2 -2
  23. package/dist/{job-queue-pdf1uj5X.js → job-queue-DUs_jnBa.js} +1 -1
  24. package/dist/{logging-Bb_U9Vff.js → logging-CXQcqM-W.js} +1 -1
  25. package/dist/nitro.js +3 -3
  26. package/dist/{oauth-callback-server-DyJkq4qL.js → oauth-callback-server-B0KXcd24.js} +2 -2
  27. package/dist/{pending-auth-D0_QYjpu.js → pending-auth-CxZjizu_.js} +2 -2
  28. package/dist/{profile-BGU13Ftr.js → profile-8K6D5LHD.js} +4 -4
  29. package/dist/{registry-DmJtt89-.js → registry-DUa5NdZP.js} +1 -1
  30. package/dist/{resolve-DOmkFjRY.js → resolve-E_-QQ8pM.js} +2 -2
  31. package/dist/{runner-Bw_iSMQT.js → runner-Bf0HnWqY.js} +10 -10
  32. package/dist/{sandbox-egress-signals-BcD4xbJ5.js → sandbox-egress-signals-BW9FzWoQ.js} +1 -1
  33. package/dist/{skills-BosqkO9V.js → skills-B1f5Ku_s.js} +2 -2
  34. package/dist/{spawn-DZQ3VVgQ.js → spawn-BsOCLy8T.js} +2 -2
  35. package/dist/{stats-C9s5HTPZ.js → stats-CrITkCy5.js} +15 -7
  36. package/dist/{task-queue-CpgBemXY.js → task-queue-CcqJCAUd.js} +3 -3
  37. package/dist/{task-runner-DZa0IGwl.js → task-runner-7IOXx_Uo.js} +7 -7
  38. package/dist/{thread-state-BnTTDCWs.js → thread-state-Y3PGLZQa.js} +5 -5
  39. package/dist/{turn-execution-Ba_SruNW.js → turn-execution-Bm6Xbaoi.js} +3 -3
  40. package/dist/{turn-lifecycle-BQtgEQo-.js → turn-lifecycle-Czzk8ila.js} +1 -1
  41. package/dist/{usage-D0KzRV9P.js → usage-DHJ3ng2s.js} +3 -12
  42. package/dist/{validation-DCl2ucHB.js → validation-A2SMrAkJ.js} +1 -1
  43. package/dist/{version-w5qqIpsQ.js → version-CfRx6f_C.js} +1 -1
  44. package/dist/version.js +1 -1
  45. package/dist/{web-authorization-KnR0oZcQ.js → web-authorization-DN3cJuLB.js} +1 -1
  46. package/dist/{web-input-BiDGtDS0.js → web-input-lJH42B_R.js} +4 -4
  47. package/dist/{work-CaIyEe2C.js → work-CRHJu3hb.js} +9 -9
  48. package/dist/{worker-BQWJdxvm.js → worker-D6oTNemM.js} +5 -5
  49. package/package.json +4 -4
@@ -1,12 +1,11 @@
1
- import { i as completeText, m as escapeXml, o as resolveGatewayModel, p as unwrapCurrentInstruction } from "./client-BvhLSDdj.js";
2
- import { _ as setSpanAttributes, f as logWarn, o as getActiveTraceId, t as buildTurnFailureResponse, u as logException } from "./logging-Bb_U9Vff.js";
3
- import { n as botConfig } from "./config-DfGsDs-7.js";
1
+ import { i as completeText, m as escapeXml, o as resolveGatewayModel, p as unwrapCurrentInstruction } from "./client-BBPo9KiG.js";
2
+ import { _ as setSpanAttributes, f as logWarn, o as getActiveTraceId, t as buildTurnFailureResponse, u as logException } from "./logging-CXQcqM-W.js";
3
+ import { n as botConfig } from "./config-DMq-uJAV.js";
4
4
  import { _ as parseSlackMessageTs, f as toStoredSlackActor } from "./context-Bplcgypl.js";
5
- import { Bt as authenticationUnlinkedEvent, Ht as getConversationEventStore, Jt as withConversationEventLock, Kt as getSqlExecutor, Lt as JUNIOR_NATIVE_EVENT_NAMESPACE, Qt as bindProviderConversation, Rt as agentsInstructionsUpdatedEvent, Wt as getConversationStore, Yt as sanitizePostgresJson, qt as createSqlConversationEventStore, zt as authenticationLinkedEvent } from "./agent-hooks-CbFeRDRP.js";
5
+ import { Bt as authenticationUnlinkedEvent, Ht as getConversationEventStore, Jt as withConversationEventLock, Kt as getSqlExecutor, Lt as JUNIOR_NATIVE_EVENT_NAMESPACE, Qt as bindProviderConversation, Rt as agentsInstructionsUpdatedEvent, Wt as getConversationStore, Yt as sanitizePostgresJson, qt as createSqlConversationEventStore, zt as authenticationLinkedEvent } from "./agent-hooks-7vR8uZw2.js";
6
6
  import { G as instructionProvenanceFor, I as agentHistoryItemSchema, U as contextProvenance, W as instructionActors } from "./conversation-AA4R1VUn.js";
7
- import { c as generateShortTitle, o as ToolInputError, s as fallbackShortTitle, t as addAgentTurnUsage } from "./usage-D0KzRV9P.js";
8
- import { A as sleep, D as fenceLock, M as SANDBOX_REPOS_ROOT, P as SANDBOX_WORKSPACE_ROOT, T as MUTATION_LOCK_TTL_MS, k as withLock, w as getStateAdapter } from "./profile-BGU13Ftr.js";
9
- import path from "node:path";
7
+ import { o as fallbackShortTitle, s as generateShortTitle, t as addAgentTurnUsage } from "./usage-DHJ3ng2s.js";
8
+ import { A as sleep, D as fenceLock, T as MUTATION_LOCK_TTL_MS, k as withLock, w as getStateAdapter } from "./profile-8K6D5LHD.js";
10
9
  import { isDeepStrictEqual } from "node:util";
11
10
  import { z } from "zod";
12
11
  import { createHash } from "node:crypto";
@@ -19,147 +18,6 @@ z.object({
19
18
  role: z.string().min(1)
20
19
  }).passthrough().transform((value) => value);
21
20
  //#endregion
22
- //#region src/chat/turn-context-tag.ts
23
- /**
24
- * Canonical tag name for the runtime turn context block injected into Pi
25
- * messages. Shared between prompt assembly and turn context stripping to keep
26
- * both in sync without requiring the full prompt module.
27
- */
28
- const TURN_CONTEXT_TAG = "runtime-turn-context";
29
- //#endregion
30
- //#region src/chat/pi/transcript.ts
31
- const RUNTIME_TURN_CONTEXT_START = `<${TURN_CONTEXT_TAG}>`;
32
- const AGENTS_INSTRUCTIONS_START = "# AGENTS.md instructions";
33
- function userMessageContent(message) {
34
- const record = message;
35
- return record.role === "user" && Array.isArray(record.content) ? record.content : void 0;
36
- }
37
- function isRuntimeTurnContextPart(part) {
38
- return part !== null && typeof part === "object" && part.type === "text" && typeof part.text === "string" && (part.text.startsWith(RUNTIME_TURN_CONTEXT_START) || part.text.startsWith(AGENTS_INSTRUCTIONS_START));
39
- }
40
- function hasCurrentInstruction(message) {
41
- return userMessageContent(message)?.some((part) => part !== null && typeof part === "object" && part.type === "text" && typeof part.text === "string" && part.text.includes("<current-instruction")) ?? false;
42
- }
43
- function messageTimestamp$1(message) {
44
- return message.timestamp;
45
- }
46
- function isStandaloneRuntimeContextMessage(messages, index) {
47
- const message = messages[index];
48
- const next = messages[index + 1];
49
- return Boolean(message && next && userMessageContent(message) && userMessageContent(next) && messageTimestamp$1(message) === messageTimestamp$1(next) && !hasCurrentInstruction(message) && hasCurrentInstruction(next));
50
- }
51
- const EMBEDDED_THREAD_CONTEXT_PATTERN = new RegExp(`<(${[
52
- "thread-context",
53
- "recent-thread-messages",
54
- "thread-compactions",
55
- "thread-transcript",
56
- "thread-background"
57
- ].join("|")})(?:\\s[^>]*)?>[\\s\\S]*</\\1>`, "g");
58
- /** Type guard for Pi SDK assistant messages. */
59
- function isAssistantMessage(value) {
60
- return typeof value === "object" && value !== null && value.role === "assistant";
61
- }
62
- /** Type guard for Pi SDK tool result messages. */
63
- function isToolResultMessage(value) {
64
- return typeof value === "object" && value !== null && value.role === "toolResult";
65
- }
66
- /** Check whether a tool result carries an error flag. */
67
- function isToolResultError(result) {
68
- if (!result || typeof result !== "object") return false;
69
- return Boolean(result.isError);
70
- }
71
- /** Extract the tool name from a raw tool result message. */
72
- function normalizeToolNameFromResult(result) {
73
- if (!result || typeof result !== "object") return void 0;
74
- const record = result;
75
- if (typeof record.toolName === "string" && record.toolName.length > 0) return record.toolName;
76
- if (typeof record.name === "string" && record.name.length > 0) return record.name;
77
- }
78
- /** Extract role string from a raw Pi message. */
79
- function getPiMessageRole(value) {
80
- if (!value || typeof value !== "object") return;
81
- const role = value.role;
82
- return typeof role === "string" ? role : void 0;
83
- }
84
- /** Return whether Pi can continue directly from the current history tail. */
85
- function isContinuablePiBoundary(messages) {
86
- const lastRole = getPiMessageRole(messages.at(-1));
87
- return lastRole === "user" || lastRole === "toolResult";
88
- }
89
- /** Concatenate text content parts from an assistant message. */
90
- function extractAssistantText(message) {
91
- return (message.content ?? []).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text).join("\n");
92
- }
93
- /** Return assistant messages that belong to the terminal post-tool reply phase. */
94
- function getTerminalAssistantMessages(messages) {
95
- let lastNonAssistantIndex = -1;
96
- for (let index = messages.length - 1; index >= 0; index -= 1) if (!isAssistantMessage(messages[index])) {
97
- lastNonAssistantIndex = index;
98
- break;
99
- }
100
- return messages.slice(lastNonAssistantIndex + 1).filter(isAssistantMessage);
101
- }
102
- /** Remove trailing assistant messages before committing a resumable boundary. */
103
- function trimTrailingAssistantMessages(messages) {
104
- let end = messages.length;
105
- while (end > 0 && getPiMessageRole(messages[end - 1]) === "assistant") end -= 1;
106
- return end === messages.length ? [...messages] : messages.slice(0, end);
107
- }
108
- /** Return whether Pi history already carries session bootstrap context. */
109
- function hasRuntimeTurnContext(messages) {
110
- return messages.some((message, index) => isStandaloneRuntimeContextMessage(messages, index) || userMessageContent(message)?.some(isRuntimeTurnContextPart));
111
- }
112
- /** Keep only volatile bootstrap messages needed by an in-turn context replacement. */
113
- function retainRuntimeTurnContext(messages) {
114
- return messages.flatMap((message, index) => {
115
- if (isStandaloneRuntimeContextMessage(messages, index)) return [message];
116
- const runtimeContent = userMessageContent(message)?.filter(isRuntimeTurnContextPart) ?? [];
117
- return runtimeContent.length > 0 ? [{
118
- ...message,
119
- content: runtimeContent
120
- }] : [];
121
- });
122
- }
123
- /**
124
- * Reduce a runtime user-turn prompt to only the current turn's instruction.
125
- *
126
- * Live user prompts embed prior-thread context as `<thread-context
127
- * authority="evidence-only">` (or legacy transcript/background blocks) beside
128
- * the `<current-instruction>` block. Those blocks hold other participants'
129
- * verbatim messages, so completed-run projections consumed by plugins must
130
- * expose only the instruction authored by this turn's actor — otherwise
131
- * per-entry provenance can be defeated by reading another user's text out of an
132
- * instruction-authority entry. Prior thread context is projected separately as
133
- * per-author context-authority entries, so dropping it here is non-lossy for
134
- * plugins. This is projection-only; it never touches what the model sees during
135
- * a live run.
136
- */
137
- function instructionTextForProjection(text) {
138
- const withoutContext = text.replace(EMBEDDED_THREAD_CONTEXT_PATTERN, "").trim();
139
- return unwrapCurrentInstruction(withoutContext) ?? withoutContext;
140
- }
141
- /** Return the projected instruction text carried by one user message. */
142
- function getUserMessageInstructionText(message) {
143
- if (getPiMessageRole(message) !== "user") return "";
144
- const content = message.content;
145
- return instructionTextForProjection(typeof content === "string" ? content : Array.isArray(content) ? content.flatMap((part) => part && typeof part === "object" && part.type === "text" && typeof part.text === "string" ? [part.text] : []).join("\n") : "").trim();
146
- }
147
- /** Remove volatile runtime context before reusing messages as history. */
148
- function stripRuntimeTurnContext(messages) {
149
- return messages.flatMap((message, index) => {
150
- if (isStandaloneRuntimeContextMessage(messages, index)) return [];
151
- const content = userMessageContent(message);
152
- if (!content) return [message];
153
- const nextContent = content.filter((part) => !isRuntimeTurnContextPart(part));
154
- if (nextContent.length === content.length) return [message];
155
- if (nextContent.length === 0) return [];
156
- return [{
157
- ...message,
158
- content: nextContent
159
- }];
160
- });
161
- }
162
- //#endregion
163
21
  //#region src/chat/pi/conversation-events.ts
164
22
  function authorizationObservationMessage(data, createdAtMs) {
165
23
  const label = data.kind === "mcp" ? "MCP authorization" : "Authorization";
@@ -172,9 +30,6 @@ function authorizationObservationMessage(data, createdAtMs) {
172
30
  timestamp: createdAtMs
173
31
  });
174
32
  }
175
- function durableMessages(message) {
176
- return stripRuntimeTurnContext([message]);
177
- }
178
33
  /** Translate one Pi message into Junior's native durable history item. */
179
34
  function historyItemFromPiMessage(message, provenance) {
180
35
  const { role, ...fields } = piMessageSchema.parse(message);
@@ -239,7 +94,8 @@ function projectConversationEvents(events, options) {
239
94
  if (event.data.type === "compaction" || event.data.type === "handoff") {
240
95
  modelProfile = event.data.modelProfile;
241
96
  replacementSeq = event.seq;
242
- for (const replacement of event.data.replacementHistory) for (const message of durableMessages(piMessageFromHistoryItem(replacement.item))) {
97
+ for (const replacement of event.data.replacementHistory) {
98
+ const message = piMessageFromHistoryItem(replacement.item);
243
99
  messages.push(message);
244
100
  provenance.push(historyItemProvenance(replacement.item));
245
101
  seqs.push(replacement.sourceEventSeq ?? event.seq);
@@ -247,11 +103,10 @@ function projectConversationEvents(events, options) {
247
103
  continue;
248
104
  }
249
105
  if (event.data.type === "user_message" || event.data.type === "assistant_message" || event.data.type === "tool_result") {
250
- for (const message of durableMessages(piMessageFromHistoryItem(event.data))) {
251
- messages.push(message);
252
- provenance.push(historyItemProvenance(event.data));
253
- seqs.push(event.seq);
254
- }
106
+ const message = piMessageFromHistoryItem(event.data);
107
+ messages.push(message);
108
+ provenance.push(historyItemProvenance(event.data));
109
+ seqs.push(event.seq);
255
110
  continue;
256
111
  }
257
112
  if (event.data.type === "authorization_completed") {
@@ -274,7 +129,7 @@ const COMPACTION_TRIGGER_RATIO = .9;
274
129
  const CONTEXT_INPUT_LIMIT_RATIO = .95;
275
130
  const COMPACTION_TARGET_RATIO = .8;
276
131
  const FALLBACK_CONTEXT_WINDOW_TOKENS = 4e5;
277
- function positiveInteger$1(value, fallback) {
132
+ function positiveInteger(value, fallback) {
278
133
  return Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
279
134
  }
280
135
  /** Estimate text tokens with the shared coarse heuristic used for local budgets. */
@@ -283,12 +138,12 @@ function estimateTextTokens(text) {
283
138
  }
284
139
  /** Derive the automatic compaction threshold from model context capacity. */
285
140
  function calculateContextCompactionTriggerTokens(model) {
286
- const contextWindow = positiveInteger$1(model.contextWindow, FALLBACK_CONTEXT_WINDOW_TOKENS);
141
+ const contextWindow = positiveInteger(model.contextWindow, FALLBACK_CONTEXT_WINDOW_TOKENS);
287
142
  return Math.max(1, Math.floor(contextWindow * COMPACTION_TRIGGER_RATIO));
288
143
  }
289
144
  /** Derive the maximum estimated input size with room for uncounted request overhead. */
290
145
  function calculateContextInputLimitTokens(model) {
291
- const contextWindow = positiveInteger$1(model.contextWindow, FALLBACK_CONTEXT_WINDOW_TOKENS);
146
+ const contextWindow = positiveInteger(model.contextWindow, FALLBACK_CONTEXT_WINDOW_TOKENS);
292
147
  return Math.max(1, Math.floor(contextWindow * CONTEXT_INPUT_LIMIT_RATIO));
293
148
  }
294
149
  /** Derive the post-compaction target from the automatic trigger threshold. */
@@ -297,7 +152,7 @@ function calculateContextCompactionTargetTokens(triggerTokens) {
297
152
  }
298
153
  /** Cap one model's advertised context capacity with the host bot configuration. */
299
154
  function getModelContextBudget(modelId) {
300
- const advertisedContextWindow = positiveInteger$1(resolveGatewayModel(modelId).contextWindow, FALLBACK_CONTEXT_WINDOW_TOKENS);
155
+ const advertisedContextWindow = positiveInteger(resolveGatewayModel(modelId).contextWindow, FALLBACK_CONTEXT_WINDOW_TOKENS);
301
156
  return { contextWindow: Math.min(botConfig.contextWindowTokens, advertisedContextWindow) };
302
157
  }
303
158
  /** Resolve the automatic compaction threshold for the active agent model. */
@@ -987,7 +842,7 @@ async function loadTurnProjection(args) {
987
842
  async function loadConnectedMcpProviders(args) {
988
843
  return connectedMcpProvidersFromEvents(await getConversationEventStore().loadHistory(args.conversationId), args.credentialSubjectId);
989
844
  }
990
- function messageTimestamp(message) {
845
+ function messageTimestamp$1(message) {
991
846
  const timestamp = message.timestamp;
992
847
  return typeof timestamp === "number" ? timestamp : Date.now();
993
848
  }
@@ -1019,7 +874,7 @@ async function commitAcceptedReply(args) {
1019
874
  await createSqlConversationEventStore(executor).append(args.conversationId, [{
1020
875
  idempotencyKey: `message:${args.conversationMessageId}:agent`,
1021
876
  data: historyItemFromPiMessage(agentMessage, contextProvenance),
1022
- createdAtMs: messageTimestamp(agentMessage)
877
+ createdAtMs: messageTimestamp$1(agentMessage)
1023
878
  }]);
1024
879
  }
1025
880
  await appendConversationMessages(createSqlConversationEventStore(executor), {
@@ -1037,7 +892,7 @@ async function commitMessagesLocked(args, executor) {
1037
892
  const eventStore = createSqlConversationEventStore(executor);
1038
893
  const currentEvents = await eventStore.loadCurrentHistory(args.conversationId);
1039
894
  const current = projectConversationEvents(currentEvents, { defaultProfile: botConfig.defaultProfile });
1040
- const nextLocalMessages = stripRuntimeTurnContext(args.messages).map(normalizeDurableMessage);
895
+ const nextLocalMessages = args.messages.map(normalizeDurableMessage);
1041
896
  const matchingPrefix = countDurablePrefix(current.messages, nextLocalMessages);
1042
897
  const nextLocalProvenance = resolveCommitProvenance({
1043
898
  existing: current,
@@ -1064,7 +919,7 @@ async function commitMessagesLocked(args, executor) {
1064
919
  })) ?? [];
1065
920
  const appendedEvents = await eventStore.append(args.conversationId, [...newMessages.map((message, index) => ({
1066
921
  data: historyItemFromPiMessage(message, nextLocalProvenance[matchingPrefix + index]),
1067
- createdAtMs: messageTimestamp(message)
922
+ createdAtMs: messageTimestamp$1(message)
1068
923
  })), ...turnContextEvents]);
1069
924
  const cursor = appendedEvents.at(-1) ?? currentEvents.at(-1);
1070
925
  return {
@@ -1304,341 +1159,145 @@ async function recordGuardianActionReviewed(args) {
1304
1159
  }]);
1305
1160
  }
1306
1161
  //#endregion
1307
- //#region src/chat/tools/sandbox/file-utils.ts
1308
- const MAX_TEXT_CHARS = 6e4;
1309
- const RIPGREP_EXCLUDED_GLOBS = ["!**/.git/**", "!**/node_modules/**"];
1310
- const DEFAULT_SEARCH_COMMAND_TIMEOUT_MS = 3e4;
1311
- const SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
1312
- /** Normalize model-supplied numeric knobs before they reach filesystem tools. */
1313
- function positiveInteger(value) {
1314
- if (typeof value !== "number" || !Number.isFinite(value)) return;
1315
- const integer = Math.floor(value);
1316
- return integer > 0 ? integer : void 0;
1317
- }
1318
- /** Compare and slice text in one line-ending space while preserving callers' output rules. */
1319
- function normalizeToLf(value) {
1320
- return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1321
- }
1322
- /** Keep tool output inside the model-facing budget with an explicit notice. */
1323
- function truncateText(value, maxChars = MAX_TEXT_CHARS) {
1324
- if (value.length <= maxChars) return {
1325
- content: value,
1326
- truncated: false
1327
- };
1328
- const removed = value.length - maxChars;
1329
- return {
1330
- content: `${value.slice(0, maxChars)}\n\n[output truncated: ${removed} characters removed]`,
1331
- truncated: true
1332
- };
1162
+ //#region src/chat/turn-context-tag.ts
1163
+ /**
1164
+ * Canonical tag name for the runtime turn context block injected into Pi
1165
+ * messages. Shared between prompt assembly and turn context stripping to keep
1166
+ * both in sync without requiring the full prompt module.
1167
+ */
1168
+ const TURN_CONTEXT_TAG = "runtime-turn-context";
1169
+ //#endregion
1170
+ //#region src/chat/pi/transcript.ts
1171
+ const RUNTIME_TURN_CONTEXT_START = `<${TURN_CONTEXT_TAG}>`;
1172
+ const AGENTS_INSTRUCTIONS_START = "# AGENTS.md instructions";
1173
+ function userMessageContent(message) {
1174
+ const record = message;
1175
+ return record.role === "user" && Array.isArray(record.content) ? record.content : void 0;
1333
1176
  }
1334
- /** Detect model-facing missing paths without swallowing sandbox lifecycle/API failures. */
1335
- function isMissingPathError(error) {
1336
- if (!error || typeof error !== "object") return false;
1337
- const candidate = error;
1338
- if (candidate.code === "ENOENT") return true;
1339
- return typeof candidate.message === "string" && candidate.message.startsWith("File not found:");
1340
- }
1341
- /** Reject an expected missing search/list path through the tool-error channel. */
1342
- function missingPathSearchResult(params) {
1343
- const textPath = params.displayPath ?? params.missingPath ?? params.path;
1344
- throw new ToolInputError(`Path not found: ${textPath}`);
1345
- }
1346
- function escapeRegExp(value) {
1347
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1348
- }
1349
- function globToRegExp(pattern) {
1350
- let source = "";
1351
- for (let index = 0; index < pattern.length; index += 1) {
1352
- const char = pattern[index];
1353
- const next = pattern[index + 1];
1354
- if (char === "*" && next === "*") {
1355
- if (pattern[index + 2] === "/") {
1356
- source += "(?:.*/)?";
1357
- index += 2;
1358
- continue;
1359
- }
1360
- source += ".*";
1361
- index += 1;
1362
- continue;
1363
- }
1364
- if (char === "*") {
1365
- source += "[^/]*";
1366
- continue;
1367
- }
1368
- if (char === "?") {
1369
- source += "[^/]";
1370
- continue;
1371
- }
1372
- source += escapeRegExp(char);
1373
- }
1374
- return new RegExp(`^${source}$`);
1375
- }
1376
- function matchesGlob(relativePath, pattern) {
1377
- const matcher = globToRegExp(pattern);
1378
- if (matcher.test(relativePath)) return true;
1379
- if (pattern.startsWith("**/") && matchesGlob(relativePath, pattern.slice(3))) return true;
1380
- return !pattern.includes("/") && matcher.test(path.posix.basename(relativePath));
1381
- }
1382
- /** Keep sandbox filesystem tools scoped to the mounted workspace root. */
1383
- function resolveWorkspacePath(input, fallback = ".") {
1384
- const requested = (input ?? "").trim() || fallback;
1385
- const absolute = requested.startsWith("/") ? requested : path.posix.join(SANDBOX_WORKSPACE_ROOT, requested);
1386
- const normalized = path.posix.normalize(absolute);
1387
- if (normalized !== SANDBOX_WORKSPACE_ROOT && !normalized.startsWith(`${SANDBOX_WORKSPACE_ROOT}/`)) throw new ToolInputError(`Path must stay within ${SANDBOX_WORKSPACE_ROOT}: ${requested}`);
1388
- return normalized;
1389
- }
1390
- /** Anchor ripgrep paths and globs to the requested search root. */
1391
- function getRipgrepSearchLocation(root, rootIsDirectory) {
1392
- if (rootIsDirectory) return {
1393
- cwd: root,
1394
- target: "."
1395
- };
1396
- return {
1397
- cwd: path.posix.dirname(root),
1398
- target: path.posix.basename(root)
1399
- };
1177
+ function isRuntimeTurnContextPart(part) {
1178
+ return part !== null && typeof part === "object" && part.type === "text" && typeof part.text === "string" && (part.text.startsWith(RUNTIME_TURN_CONTEXT_START) || part.text.startsWith(AGENTS_INSTRUCTIONS_START));
1400
1179
  }
1401
- /** Share bounded workspace traversal across search tools so their skip rules stay aligned. */
1402
- async function collectFiles(params) {
1403
- const files = [];
1404
- let limitReached = false;
1405
- let missingPath;
1406
- const visit = async (dirPath) => {
1407
- if (missingPath) return;
1408
- let entries;
1409
- try {
1410
- entries = (await params.fs.readdir(dirPath)).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
1411
- } catch (error) {
1412
- if (isMissingPathError(error)) {
1413
- missingPath = dirPath;
1414
- return;
1415
- }
1416
- throw error;
1417
- }
1418
- for (const entry of entries) {
1419
- const fullPath = path.posix.join(dirPath, entry);
1420
- let stat;
1421
- try {
1422
- stat = await params.fs.stat(fullPath);
1423
- } catch (error) {
1424
- if (isMissingPathError(error)) {
1425
- missingPath = fullPath;
1426
- return;
1427
- }
1428
- throw error;
1429
- }
1430
- if (stat.isDirectory()) {
1431
- if (!SKIPPED_DIRECTORIES.has(entry)) await visit(fullPath);
1432
- if (limitReached) return;
1433
- continue;
1434
- }
1435
- const relativePath = path.posix.relative(params.root, fullPath);
1436
- if (!params.pattern || matchesGlob(relativePath, params.pattern)) {
1437
- files.push(fullPath);
1438
- if (params.limit && files.length >= params.limit) {
1439
- limitReached = true;
1440
- return;
1441
- }
1442
- }
1443
- }
1444
- };
1445
- let stat;
1446
- try {
1447
- stat = await params.fs.stat(params.root);
1448
- } catch (error) {
1449
- if (isMissingPathError(error)) return {
1450
- files,
1451
- limitReached: false,
1452
- missingPath: params.root,
1453
- missingRoot: true
1454
- };
1455
- throw error;
1456
- }
1457
- if (!stat.isDirectory()) {
1458
- const relativePath = path.posix.basename(params.root);
1459
- return {
1460
- files: !params.pattern || matchesGlob(relativePath, params.pattern) ? [params.root] : [],
1461
- limitReached: false,
1462
- missingRoot: false
1463
- };
1464
- }
1465
- await visit(params.root);
1466
- return {
1467
- files,
1468
- limitReached,
1469
- missingPath,
1470
- missingRoot: missingPath === params.root
1471
- };
1180
+ function hasCurrentInstruction(message) {
1181
+ return userMessageContent(message)?.some((part) => part !== null && typeof part === "object" && part.type === "text" && typeof part.text === "string" && part.text.includes("<current-instruction")) ?? false;
1472
1182
  }
1473
- //#endregion
1474
- //#region src/chat/repository-instructions.ts
1475
- const AGENTS_FILENAME = "AGENTS.md";
1476
- const MAX_REPOSITORY_INSTRUCTIONS_BYTES = 32768;
1477
- const AGENTS_REPLACEMENT_NOTICE = "These AGENTS.md instructions replace all previously provided AGENTS.md instructions.";
1478
- const AGENTS_REMOVAL_NOTICE = "The previously provided AGENTS.md instructions no longer apply.";
1479
- async function pathExists(fs, target) {
1480
- try {
1481
- await fs.stat(target);
1482
- return true;
1483
- } catch (error) {
1484
- if (isMissingPathError(error)) return false;
1485
- throw error;
1486
- }
1183
+ function messageTimestamp(message) {
1184
+ return message.timestamp;
1487
1185
  }
1488
- async function directoryExists(fs, target) {
1489
- try {
1490
- return (await fs.stat(target)).isDirectory();
1491
- } catch (error) {
1492
- if (isMissingPathError(error)) return false;
1493
- throw error;
1494
- }
1186
+ function isStandaloneRuntimeContextMessage(messages, index) {
1187
+ const message = messages[index];
1188
+ const next = messages[index + 1];
1189
+ return Boolean(message && next && userMessageContent(message) && userMessageContent(next) && messageTimestamp(message) === messageTimestamp(next) && !hasCurrentInstruction(message) && hasCurrentInstruction(next));
1495
1190
  }
1496
- async function findGitRoot(fs, cwd) {
1497
- let current = cwd;
1498
- for (;;) {
1499
- if (await pathExists(fs, path.posix.join(current, ".git"))) return current;
1500
- if (current === SANDBOX_WORKSPACE_ROOT) return;
1501
- const parent = path.posix.dirname(current);
1502
- if (parent === current || parent !== SANDBOX_WORKSPACE_ROOT && !parent.startsWith(`${SANDBOX_WORKSPACE_ROOT}/`)) return;
1503
- current = parent;
1504
- }
1191
+ const EMBEDDED_THREAD_CONTEXT_PATTERN = new RegExp(`<(${[
1192
+ "thread-context",
1193
+ "recent-thread-messages",
1194
+ "thread-compactions",
1195
+ "thread-transcript",
1196
+ "thread-background"
1197
+ ].join("|")})(?:\\s[^>]*)?>[\\s\\S]*</\\1>`, "g");
1198
+ /** Type guard for Pi SDK assistant messages. */
1199
+ function isAssistantMessage(value) {
1200
+ return typeof value === "object" && value !== null && value.role === "assistant";
1505
1201
  }
1506
- /** Return Git worktrees under repos/, sorted for stable instruction capture. */
1507
- async function listRepositoryDirectories(fs) {
1508
- if (!await directoryExists(fs, SANDBOX_REPOS_ROOT)) return [];
1509
- const entries = await fs.readdir(SANDBOX_REPOS_ROOT);
1510
- const repositories = [];
1511
- for (const entry of entries) {
1512
- const candidate = path.posix.join(SANDBOX_REPOS_ROOT, entry);
1513
- if (await directoryExists(fs, candidate) && await pathExists(fs, path.posix.join(candidate, ".git"))) repositories.push(candidate);
1514
- }
1515
- return repositories.sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
1202
+ /** Type guard for Pi SDK tool result messages. */
1203
+ function isToolResultMessage(value) {
1204
+ return typeof value === "object" && value !== null && value.role === "toolResult";
1516
1205
  }
1517
- async function readInstructionFile(fs, filePath) {
1518
- try {
1519
- const content = await fs.readFile(filePath, { encoding: "utf8" });
1520
- return content.trim() ? content : void 0;
1521
- } catch (error) {
1522
- if (isMissingPathError(error)) return;
1523
- throw error;
1524
- }
1206
+ /** Check whether a tool result carries an error flag. */
1207
+ function isToolResultError(result) {
1208
+ if (!result || typeof result !== "object") return false;
1209
+ return Boolean(result.isError);
1525
1210
  }
1526
- /** Resolve the effective root-to-cwd AGENTS.md bundle for one selected directory. */
1527
- async function resolveRepositoryInstructions(args) {
1528
- const gitRoot = await findGitRoot(args.fs, args.cwd);
1529
- if (!gitRoot) return;
1530
- const directories = [];
1531
- let current = args.cwd;
1532
- for (;;) {
1533
- directories.push(current);
1534
- if (current === gitRoot) break;
1535
- current = path.posix.dirname(current);
1536
- }
1537
- directories.reverse();
1538
- const sources = [];
1539
- let remaining = Math.max(0, args.maxBytes ?? MAX_REPOSITORY_INSTRUCTIONS_BYTES);
1540
- for (const directory of directories) {
1541
- if (remaining <= 0) break;
1542
- const filePath = path.posix.join(directory, AGENTS_FILENAME);
1543
- const content = await readInstructionFile(args.fs, filePath);
1544
- if (!content) continue;
1545
- const bytes = Buffer.from(content, "utf8");
1546
- const bounded = bytes.length <= remaining ? content : bytes.subarray(0, remaining).toString("utf8");
1547
- sources.push({
1548
- content: bounded,
1549
- path: filePath
1550
- });
1551
- remaining -= Buffer.byteLength(bounded, "utf8");
1552
- }
1553
- if (sources.length === 0) return;
1554
- const text = formatInstructionSources(sources);
1555
- const fingerprint = createHash("sha256").update(JSON.stringify({
1556
- directory: args.cwd,
1557
- sources
1558
- })).digest("hex");
1559
- return {
1560
- directory: args.cwd,
1561
- fingerprint,
1562
- sources,
1563
- text
1564
- };
1211
+ /** Extract the tool name from a raw tool result message. */
1212
+ function normalizeToolNameFromResult(result) {
1213
+ if (!result || typeof result !== "object") return void 0;
1214
+ const record = result;
1215
+ if (typeof record.toolName === "string" && record.toolName.length > 0) return record.toolName;
1216
+ if (typeof record.name === "string" && record.name.length > 0) return record.name;
1565
1217
  }
1566
- function formatInstructionSources(sources) {
1567
- if (sources.length === 1) return sources[0].content;
1568
- return sources.map((source) => `## ${path.posix.dirname(source.path)}\n\n${source.content}`).join("\n\n");
1218
+ /** Extract role string from a raw Pi message. */
1219
+ function getPiMessageRole(value) {
1220
+ if (!value || typeof value !== "object") return;
1221
+ const role = value.role;
1222
+ return typeof role === "string" ? role : void 0;
1569
1223
  }
1570
- /** Combine AGENTS.md bundles from one or more repository directories. */
1571
- function mergeRepositoryInstructions(bundles) {
1572
- if (bundles.length === 0) return;
1573
- if (bundles.length === 1) return bundles[0];
1574
- const sources = bundles.flatMap((bundle) => bundle.sources);
1575
- const text = formatInstructionSources(sources);
1576
- return {
1577
- fingerprint: createHash("sha256").update(JSON.stringify({
1578
- directories: bundles.map((bundle) => bundle.directory ?? null),
1579
- sources
1580
- })).digest("hex"),
1581
- sources,
1582
- text
1583
- };
1224
+ /** Return whether Pi can continue directly from the current history tail. */
1225
+ function isContinuablePiBoundary(messages) {
1226
+ const lastRole = getPiMessageRole(messages.at(-1));
1227
+ return lastRole === "user" || lastRole === "toolResult";
1584
1228
  }
1585
- /** Resolve AGENTS.md instructions for each selected repository directory. */
1586
- async function resolveRepositoryInstructionsForDirectories(args) {
1587
- const uniqueDirectories = [...new Set(args.directories.filter((directory) => directory === SANDBOX_WORKSPACE_ROOT || directory.startsWith(`${SANDBOX_WORKSPACE_ROOT}/`)))].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
1588
- let remaining = MAX_REPOSITORY_INSTRUCTIONS_BYTES;
1589
- const bundles = [];
1590
- for (const directory of uniqueDirectories) {
1591
- if (remaining <= 0) break;
1592
- const instructions = await resolveRepositoryInstructions({
1593
- cwd: directory,
1594
- fs: args.fs,
1595
- maxBytes: remaining
1596
- });
1597
- if (!instructions) continue;
1598
- bundles.push(instructions);
1599
- remaining -= instructions.sources.reduce((total, source) => total + Buffer.byteLength(source.content, "utf8"), 0);
1229
+ /** Concatenate text content parts from an assistant message. */
1230
+ function extractAssistantText(message) {
1231
+ return (message.content ?? []).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text).join("\n");
1232
+ }
1233
+ /** Return assistant messages that belong to the terminal post-tool reply phase. */
1234
+ function getTerminalAssistantMessages(messages) {
1235
+ let lastNonAssistantIndex = -1;
1236
+ for (let index = messages.length - 1; index >= 0; index -= 1) if (!isAssistantMessage(messages[index])) {
1237
+ lastNonAssistantIndex = index;
1238
+ break;
1600
1239
  }
1601
- return mergeRepositoryInstructions(bundles);
1240
+ return messages.slice(lastNonAssistantIndex + 1).filter(isAssistantMessage);
1602
1241
  }
1603
- /** Render repository instructions with Codex's exact model-visible wrapper. */
1604
- function renderAgentsInstructions(args) {
1605
- return `# AGENTS.md instructions${args.directory ? ` for ${args.directory}` : ""}\n\n<INSTRUCTIONS>\n${args.text}\n</INSTRUCTIONS>`;
1242
+ /** Remove trailing assistant messages before committing a resumable boundary. */
1243
+ function trimTrailingAssistantMessages(messages) {
1244
+ let end = messages.length;
1245
+ while (end > 0 && getPiMessageRole(messages[end - 1]) === "assistant") end -= 1;
1246
+ return end === messages.length ? [...messages] : messages.slice(0, end);
1606
1247
  }
1607
- /** Build one user-role repository-instructions context message. */
1608
- function buildAgentsInstructionsMessage(args) {
1609
- return {
1610
- role: "user",
1611
- content: [{
1612
- type: "text",
1613
- text: renderAgentsInstructions(args)
1614
- }],
1615
- timestamp: args.timestamp ?? Date.now()
1616
- };
1248
+ /** Return whether Pi history already carries session bootstrap context. */
1249
+ function hasRuntimeTurnContext(messages) {
1250
+ return messages.some((message, index) => isStandaloneRuntimeContextMessage(messages, index) || userMessageContent(message)?.some(isRuntimeTurnContextPart));
1617
1251
  }
1618
- /** Read the latest effective AGENTS.md state from model-visible history. */
1619
- function findVisibleAgentsInstructions(messages) {
1620
- for (let messageIndex = messages.length - 1; messageIndex >= 0; messageIndex -= 1) {
1621
- const message = messages[messageIndex];
1622
- if (message.role !== "user" || !Array.isArray(message.content)) continue;
1623
- for (let partIndex = message.content.length - 1; partIndex >= 0; partIndex -= 1) {
1624
- const part = message.content[partIndex];
1625
- if (part.type !== "text" || typeof part.text !== "string" || !part.text.startsWith("# AGENTS.md instructions")) continue;
1626
- const match = part.text.match(/^# AGENTS\.md instructions(?: for ([^\n]+))?\n\n<INSTRUCTIONS>\n([\s\S]*)\n<\/INSTRUCTIONS>$/);
1627
- if (!match) continue;
1628
- let text = match[2];
1629
- if (text === "The previously provided AGENTS.md instructions no longer apply.") return { active: false };
1630
- if (text.startsWith(`These AGENTS.md instructions replace all previously provided AGENTS.md instructions.\n\n`)) text = text.slice(86);
1631
- return {
1632
- active: true,
1633
- ...match[1] ? { directory: match[1] } : void 0,
1634
- text
1635
- };
1636
- }
1637
- }
1252
+ /** Keep only volatile bootstrap messages needed by an in-turn context replacement. */
1253
+ function retainRuntimeTurnContext(messages) {
1254
+ return messages.flatMap((message, index) => {
1255
+ if (isStandaloneRuntimeContextMessage(messages, index)) return [message];
1256
+ const runtimeContent = userMessageContent(message)?.filter(isRuntimeTurnContextPart) ?? [];
1257
+ return runtimeContent.length > 0 ? [{
1258
+ ...message,
1259
+ content: runtimeContent
1260
+ }] : [];
1261
+ });
1262
+ }
1263
+ /**
1264
+ * Reduce a runtime user-turn prompt to only the current turn's instruction.
1265
+ *
1266
+ * Live user prompts embed prior-thread context as `<thread-context
1267
+ * authority="evidence-only">` (or legacy transcript/background blocks) beside
1268
+ * the `<current-instruction>` block. Those blocks hold other participants'
1269
+ * verbatim messages, so completed-run projections consumed by plugins must
1270
+ * expose only the instruction authored by this turn's actor — otherwise
1271
+ * per-entry provenance can be defeated by reading another user's text out of an
1272
+ * instruction-authority entry. Prior thread context is projected separately as
1273
+ * per-author context-authority entries, so dropping it here is non-lossy for
1274
+ * plugins. This is projection-only; it never touches what the model sees during
1275
+ * a live run.
1276
+ */
1277
+ function instructionTextForProjection(text) {
1278
+ const withoutContext = text.replace(EMBEDDED_THREAD_CONTEXT_PATTERN, "").trim();
1279
+ return unwrapCurrentInstruction(withoutContext) ?? withoutContext;
1280
+ }
1281
+ /** Return the projected instruction text carried by one user message. */
1282
+ function getUserMessageInstructionText(message) {
1283
+ if (getPiMessageRole(message) !== "user") return "";
1284
+ const content = message.content;
1285
+ return instructionTextForProjection(typeof content === "string" ? content : Array.isArray(content) ? content.flatMap((part) => part && typeof part === "object" && part.type === "text" && typeof part.text === "string" ? [part.text] : []).join("\n") : "").trim();
1638
1286
  }
1639
- /** Whether one message is a generated AGENTS.md context update. */
1640
- function isAgentsInstructionsMessage(message) {
1641
- return Boolean(findVisibleAgentsInstructions([message]));
1287
+ /** Remove runtime context from a non-model projection or explicit replacement. */
1288
+ function stripRuntimeTurnContext(messages) {
1289
+ return messages.flatMap((message, index) => {
1290
+ if (isStandaloneRuntimeContextMessage(messages, index)) return [];
1291
+ const content = userMessageContent(message);
1292
+ if (!content) return [message];
1293
+ const nextContent = content.filter((part) => !isRuntimeTurnContextPart(part));
1294
+ if (nextContent.length === content.length) return [message];
1295
+ if (nextContent.length === 0) return [];
1296
+ return [{
1297
+ ...message,
1298
+ content: nextContent
1299
+ }];
1300
+ });
1642
1301
  }
1643
1302
  //#endregion
1644
1303
  //#region src/chat/services/turn-limit.ts
@@ -1893,8 +1552,7 @@ async function recordConversationActivityMetadata(args) {
1893
1552
  visibility: hasParent ? void 0 : args.destinationVisibility
1894
1553
  });
1895
1554
  }
1896
- function materializeTurnRecord(stored, piProjection, turnStartMessageIndex, restoreVolatileContext = true, runtimeMetrics) {
1897
- const restoredProjection = restoreVolatileContext && (stored.state === "running" || stored.state === "paused") ? restoreRuntimeContext(piProjection, stored.runtimeContext) : piProjection;
1555
+ function materializeTurnRecord(stored, piProjection, turnStartMessageIndex, runtimeMetrics) {
1898
1556
  return {
1899
1557
  ...definedProps$1({ actor: stored.actor }),
1900
1558
  schemaVersion: stored.schemaVersion,
@@ -1906,8 +1564,8 @@ function materializeTurnRecord(stored, piProjection, turnStartMessageIndex, rest
1906
1564
  startedAtMs: stored.startedAtMs,
1907
1565
  lastProgressAtMs: stored.lastProgressAtMs,
1908
1566
  updatedAtMs: stored.updatedAtMs,
1909
- piMessages: restoredProjection.messages,
1910
- piMessageProvenance: restoredProjection.provenance,
1567
+ piMessages: piProjection.messages,
1568
+ piMessageProvenance: piProjection.provenance,
1911
1569
  actors: instructionActors(piProjection.provenance),
1912
1570
  cumulativeDurationMs: runtimeMetrics?.cumulativeDurationMs ?? 0,
1913
1571
  ...definedProps$1({
@@ -1927,37 +1585,6 @@ function materializeTurnRecord(stored, piProjection, turnStartMessageIndex, rest
1927
1585
  })
1928
1586
  };
1929
1587
  }
1930
- function restoreRuntimeContext(projection, runtimeContext) {
1931
- if (!runtimeContext || runtimeContext.length === 0) return projection;
1932
- const restoredMessages = [...projection.messages];
1933
- const restoredProvenance = [...projection.provenance];
1934
- const unmatchedRuntimeContext = [];
1935
- for (const runtimeMessage of runtimeContext) {
1936
- const runtime = runtimeMessage;
1937
- const targetIndex = restoredMessages.findIndex((message) => {
1938
- const candidate = message;
1939
- return candidate.role === "user" && candidate.timestamp === runtime.timestamp;
1940
- });
1941
- if (targetIndex < 0) {
1942
- if (isAgentsInstructionsMessage(runtimeMessage)) {
1943
- const followingIndex = restoredMessages.findIndex((message) => {
1944
- const timestamp = message.timestamp;
1945
- return typeof runtime.timestamp === "number" && typeof timestamp === "number" && timestamp > runtime.timestamp;
1946
- });
1947
- const insertionIndex = followingIndex < 0 ? restoredMessages.length : followingIndex;
1948
- restoredMessages.splice(insertionIndex, 0, runtimeMessage);
1949
- restoredProvenance.splice(insertionIndex, 0, contextProvenance);
1950
- } else unmatchedRuntimeContext.push(runtimeMessage);
1951
- continue;
1952
- }
1953
- restoredMessages.splice(targetIndex, 0, runtimeMessage);
1954
- restoredProvenance.splice(targetIndex, 0, contextProvenance);
1955
- }
1956
- return {
1957
- messages: [...unmatchedRuntimeContext, ...restoredMessages],
1958
- provenance: [...unmatchedRuntimeContext.map(() => contextProvenance), ...restoredProvenance]
1959
- };
1960
- }
1961
1588
  /** Read only the stored metadata record without materializing transcript logs. */
1962
1589
  async function getStoredTurnRecord(conversationId, turnId) {
1963
1590
  const stateAdapter = getStateAdapter();
@@ -1982,7 +1609,7 @@ async function materializeStoredTurnRecord(conversationId, turnId, followCurrent
1982
1609
  const turnStartMessageIndex = followsReplacement ? 0 : parsed.turnStartSeq === void 0 ? void 0 : piProjection.seqs.filter((seq) => seq <= parsed.turnStartSeq).length;
1983
1610
  const conversation = await getConversationStore().get({ conversationId });
1984
1611
  const executionMetrics = executionMetricsForTurn(conversation, turnId);
1985
- return materializeTurnRecord(parsed, piProjection, turnStartMessageIndex, !followsReplacement && (parsed.historyVersion === void 0 || parsed.historyVersion === currentHistoryVersion), {
1612
+ return materializeTurnRecord(parsed, piProjection, turnStartMessageIndex, {
1986
1613
  channelName: conversation?.channelName,
1987
1614
  cumulativeDurationMs: executionMetrics?.durationMs,
1988
1615
  cumulativeUsage: executionMetrics?.usage
@@ -2022,7 +1649,6 @@ function buildStoredRecord(args) {
2022
1649
  source: args.source,
2023
1650
  resultMessageId: args.resultMessageId,
2024
1651
  resumedFromSliceId: args.resumedFromSliceId,
2025
- runtimeContext: args.runtimeContext && args.runtimeContext.length > 0 ? args.runtimeContext : void 0,
2026
1652
  surface: args.surface,
2027
1653
  traceId: args.traceId,
2028
1654
  turnStartSeq: args.turnStartSeq
@@ -2054,7 +1680,7 @@ async function setStoredRecord(args) {
2054
1680
  return materializeTurnRecord(storedRecord, {
2055
1681
  messages: [...args.piMessages],
2056
1682
  provenance: [...args.piMessageProvenance]
2057
- }, args.turnStartMessageIndex, true, args.runtimeMetrics);
1683
+ }, args.turnStartMessageIndex, args.runtimeMetrics);
2058
1684
  }
2059
1685
  /**
2060
1686
  * Transition an unfinished turn only if the caller still holds the
@@ -2127,9 +1753,7 @@ async function upsertTurnRecordLocked(args, fence) {
2127
1753
  turnId: args.turnId
2128
1754
  } } : void 0
2129
1755
  });
2130
- const durableTurnStartMessageIndex = args.turnStartMessageIndex === void 0 ? void 0 : stripRuntimeTurnContext(args.piMessages.slice(0, args.turnStartMessageIndex)).length;
2131
- const runtimeContext = retainRuntimeTurnContext(args.piMessages);
2132
- const retainedRuntimeContext = runtimeContext.length > 0 ? runtimeContext : existingRecord?.historyVersion === commit.historyVersion ? existingRecord.runtimeContext : void 0;
1756
+ const durableTurnStartMessageIndex = args.turnStartMessageIndex;
2133
1757
  const turnStartSeq = durableTurnStartMessageIndex === void 0 ? existingRecord?.turnStartSeq : durableTurnStartMessageIndex <= 0 ? -1 : commit.messageSeqs[durableTurnStartMessageIndex - 1] ?? commit.committedSeq;
2134
1758
  const turnStartMessageIndex = durableTurnStartMessageIndex ?? (turnStartSeq === void 0 ? void 0 : commit.messageSeqs.filter((seq) => seq <= turnStartSeq).length);
2135
1759
  const turnSource = args.source ?? existingRecord?.source;
@@ -2173,7 +1797,6 @@ async function upsertTurnRecordLocked(args, fence) {
2173
1797
  source: args.source ?? existingRecord?.source,
2174
1798
  resultMessageId: args.resultMessageId ?? existingRecord?.resultMessageId,
2175
1799
  resumedFromSliceId: args.resumedFromSliceId,
2176
- runtimeContext: args.state === "running" || args.state === "paused" ? retainedRuntimeContext : void 0,
2177
1800
  startedAtMs: existingRecord?.startedAtMs,
2178
1801
  surface: args.surface ?? existingRecord?.surface,
2179
1802
  traceId: args.traceId ?? existingRecord?.traceId,
@@ -2448,4 +2071,4 @@ async function saveDone(args) {
2448
2071
  });
2449
2072
  }
2450
2073
  //#endregion
2451
- export { persistConversationMessages as $, normalizeToLf as A, retainRuntimeTurnContext as At, openConversationProjection as B, DEFAULT_SEARCH_COMMAND_TIMEOUT_MS as C, hasRuntimeTurnContext as Ct, getRipgrepSearchLocation as D, isToolResultError as Dt, collectFiles as E, isContinuablePiBoundary as Et, commitMessages as F, recordAuthorizationRequested as G, recordAttachmentsDelivered as H, loadConnectedMcpProviders as I, recordSubscribedReplyRoute as J, recordGuardianActionReviewed as K, loadConversationProjection as L, resolveWorkspacePath as M, trimTrailingAssistantMessages as Mt, truncateText as N, TURN_CONTEXT_TAG as Nt, isMissingPathError as O, isToolResultMessage as Ot, commitAcceptedReply as P, hydrateConversationMessages as Q, loadProjection as R, resolveRepositoryInstructionsForDirectories as S, getUserMessageInstructionText as St, RIPGREP_EXCLUDED_GLOBS as T, isAssistantMessage as Tt, recordAuthenticationLinked as U, recordAgentsInstructionsUpdated as V, recordAuthorizationCompleted as W, recordTurnRoute as X, recordToolExecutionStarted as Y, appendConversationMessages as Z, AGENTS_REPLACEMENT_NOTICE as _, historyItemFromPiMessage as _t, completeTurnRecord as a, createConversationMemoryService as at, listRepositoryDirectories as b, getPiMessageRole as bt, getTurnRecordForResume as c, isHumanConversationMessage as ct, persistWithRetry as d, recordDeliveredAssistantMessage as dt, projectConversationMessages as et, TurnSliceLimitExceededError as f, turnHasReply as ft, AGENTS_REMOVAL_NOTICE as g, getAgentContextInputLimitTokens as gt, isTurnExecutionLimitExceededError as h, getAgentContextCompactionTriggerTokens as ht, abandonTurnRecord as i, buildConversationContext as it, positiveInteger as j, stripRuntimeTurnContext as jt, missingPathSearchResult as k, normalizeToolNameFromResult as kt, listTurnSummaries as l, markConversationMessage as lt, buildTurnErrorResponse as m, estimateTextTokens as mt, loadTurnCheckpoint as n, resolveConversationTitle as nt, failTurnRecord as o, estimateConversationContextTokens as ot, assertTurnToolCallLimit as p, upsertConversationMessage as pt, recordMcpProviderConnected as q, saveTurnCheckpoint as r, appendThreadContextMessages as rt, getTurnRecord as s, getConversationMessageSlackTs as st, continuableMessages as t, persistConversationMessageSummaries as tt, recordTurnSummary as u, normalizeConversationText as ut, buildAgentsInstructionsMessage as v, piMessageFromHistoryItem as vt, MAX_TEXT_CHARS as w, instructionTextForProjection as wt, renderAgentsInstructions as x, getTerminalAssistantMessages as xt, findVisibleAgentsInstructions as y, extractAssistantText as yt, loadTurnRoute as z };
2074
+ export { resolveConversationTitle as $, TURN_CONTEXT_TAG as A, recordAuthenticationLinked as B, isContinuablePiBoundary as C, retainRuntimeTurnContext as D, normalizeToolNameFromResult as E, loadProjection as F, recordSubscribedReplyRoute as G, recordAuthorizationRequested as H, loadTurnRoute as I, appendConversationMessages as J, recordToolExecutionStarted as K, openConversationProjection as L, commitMessages as M, loadConnectedMcpProviders as N, stripRuntimeTurnContext as O, loadConversationProjection as P, persistConversationMessageSummaries as Q, recordAgentsInstructionsUpdated as R, isAssistantMessage as S, isToolResultMessage as T, recordGuardianActionReviewed as U, recordAuthorizationCompleted as V, recordMcpProviderConnected as W, persistConversationMessages as X, hydrateConversationMessages as Y, projectConversationMessages as Z, getPiMessageRole as _, completeTurnRecord as a, isHumanConversationMessage as at, hasRuntimeTurnContext as b, getTurnRecordForResume as c, recordDeliveredAssistantMessage as ct, persistWithRetry as d, estimateTextTokens as dt, appendThreadContextMessages as et, TurnSliceLimitExceededError as f, getAgentContextCompactionTriggerTokens as ft, extractAssistantText as g, isTurnExecutionLimitExceededError as h, piMessageFromHistoryItem as ht, abandonTurnRecord as i, getConversationMessageSlackTs as it, commitAcceptedReply as j, trimTrailingAssistantMessages as k, listTurnSummaries as l, turnHasReply as lt, buildTurnErrorResponse as m, historyItemFromPiMessage as mt, loadTurnCheckpoint as n, createConversationMemoryService as nt, failTurnRecord as o, markConversationMessage as ot, assertTurnToolCallLimit as p, getAgentContextInputLimitTokens as pt, recordTurnRoute as q, saveTurnCheckpoint as r, estimateConversationContextTokens as rt, getTurnRecord as s, normalizeConversationText as st, continuableMessages as t, buildConversationContext as tt, recordTurnSummary as u, upsertConversationMessage as ut, getTerminalAssistantMessages as v, isToolResultError as w, instructionTextForProjection as x, getUserMessageInstructionText as y, recordAttachmentsDelivered as z };