@code-yeongyu/senpi 2026.7.22-2 → 2026.7.23

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 (91) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  3. package/dist/core/extensions/builtin/compaction/index.js +14 -6
  4. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  5. package/dist/core/extensions/builtin/compaction/openai-remote-convert.d.ts +26 -0
  6. package/dist/core/extensions/builtin/compaction/openai-remote-convert.d.ts.map +1 -0
  7. package/dist/core/extensions/builtin/compaction/openai-remote-convert.js +194 -0
  8. package/dist/core/extensions/builtin/compaction/openai-remote-convert.js.map +1 -0
  9. package/dist/core/extensions/builtin/compaction/openai-remote-model.d.ts +22 -0
  10. package/dist/core/extensions/builtin/compaction/openai-remote-model.d.ts.map +1 -0
  11. package/dist/core/extensions/builtin/compaction/openai-remote-model.js +92 -0
  12. package/dist/core/extensions/builtin/compaction/openai-remote-model.js.map +1 -0
  13. package/dist/core/extensions/builtin/compaction/openai-remote-schema.d.ts +92 -0
  14. package/dist/core/extensions/builtin/compaction/openai-remote-schema.d.ts.map +1 -0
  15. package/dist/core/extensions/builtin/compaction/openai-remote-schema.js +56 -0
  16. package/dist/core/extensions/builtin/compaction/openai-remote-schema.js.map +1 -0
  17. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +7 -84
  18. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -1
  19. package/dist/core/extensions/builtin/compaction/openai-remote.js +55 -271
  20. package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -1
  21. package/dist/core/extensions/builtin/prompt-preset/gpt-5.2.d.ts.map +1 -1
  22. package/dist/core/extensions/builtin/prompt-preset/gpt-5.2.js +3 -0
  23. package/dist/core/extensions/builtin/prompt-preset/gpt-5.2.js.map +1 -1
  24. package/dist/core/extensions/builtin/prompt-preset/gpt-5.3-codex.d.ts.map +1 -1
  25. package/dist/core/extensions/builtin/prompt-preset/gpt-5.3-codex.js +3 -0
  26. package/dist/core/extensions/builtin/prompt-preset/gpt-5.3-codex.js.map +1 -1
  27. package/dist/core/extensions/builtin/prompt-preset/gpt-5.4.d.ts.map +1 -1
  28. package/dist/core/extensions/builtin/prompt-preset/gpt-5.4.js +3 -0
  29. package/dist/core/extensions/builtin/prompt-preset/gpt-5.4.js.map +1 -1
  30. package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.d.ts.map +1 -1
  31. package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.js +3 -0
  32. package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.js.map +1 -1
  33. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts.map +1 -1
  34. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +3 -0
  35. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js.map +1 -1
  36. package/dist/core/extensions/builtin/prompt-preset/gpt-5.d.ts.map +1 -1
  37. package/dist/core/extensions/builtin/prompt-preset/gpt-5.js +3 -0
  38. package/dist/core/extensions/builtin/prompt-preset/gpt-5.js.map +1 -1
  39. package/dist/core/extensions/builtin/prompt-preset/gpt-eval-routing.d.ts +3 -0
  40. package/dist/core/extensions/builtin/prompt-preset/gpt-eval-routing.d.ts.map +1 -0
  41. package/dist/core/extensions/builtin/prompt-preset/gpt-eval-routing.js +6 -0
  42. package/dist/core/extensions/builtin/prompt-preset/gpt-eval-routing.js.map +1 -0
  43. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  44. package/dist/modes/interactive/components/assistant-message.js +34 -4
  45. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  46. package/dist/modes/interactive/tool-progress.js +1 -1
  47. package/dist/modes/interactive/tool-progress.js.map +1 -1
  48. package/dist/utils/duration.d.ts +2 -0
  49. package/dist/utils/duration.d.ts.map +1 -0
  50. package/dist/utils/duration.js +16 -0
  51. package/dist/utils/duration.js.map +1 -0
  52. package/docs/session-format.md +4 -0
  53. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +14 -0
  54. package/node_modules/@code-yeongyu/senpi-codemode/README.md +15 -6
  55. package/node_modules/@code-yeongyu/senpi-codemode/package.json +3 -3
  56. package/node_modules/@code-yeongyu/senpi-codemode/src/codemode/runtime.ts +258 -0
  57. package/node_modules/@code-yeongyu/senpi-codemode/src/codemode/tools.ts +106 -0
  58. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/runtime-factory.ts +114 -0
  59. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/session-manager-proxy.ts +116 -0
  60. package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +73 -204
  61. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-runtime.js +1 -0
  62. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  63. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +34 -0
  64. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
  65. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  66. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +20 -0
  67. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
  68. package/node_modules/@earendil-works/pi-ai/dist/providers/data/google-vertex.json +1 -1
  69. package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
  70. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  71. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  72. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  73. package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.models.d.ts +8 -0
  74. package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.models.d.ts.map +1 -1
  75. package/node_modules/@earendil-works/pi-ai/dist/providers/google-vertex.models.js.map +1 -1
  76. package/node_modules/@earendil-works/pi-ai/dist/providers/huggingface.models.d.ts +4 -0
  77. package/node_modules/@earendil-works/pi-ai/dist/providers/huggingface.models.d.ts.map +1 -1
  78. package/node_modules/@earendil-works/pi-ai/dist/providers/huggingface.models.js.map +1 -1
  79. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.d.ts +4 -0
  80. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.d.ts.map +1 -1
  81. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.js.map +1 -1
  82. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.d.ts +4 -0
  83. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -1
  84. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js.map +1 -1
  85. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +4 -0
  86. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  87. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  88. package/node_modules/@earendil-works/pi-ai/package.json +1 -1
  89. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  90. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  91. package/package.json +6 -6
@@ -1,38 +1,13 @@
1
1
  import { streamSimple } from "@earendil-works/pi-ai/compat";
2
- export const OPENAI_REMOTE_COMPACTION_SCHEMA = "senpi.compaction.openai-remote.v1";
2
+ import { convertBranchEntries, convertPendingMessages, getOpenAiRemoteCompactionDetails, isOpenAiContextCompactionItem, isOpenAiRemoteCompactionOutputItem, isRecord, isRetainedRemoteOutputItem, isRetainedResponsesStreamInputItem, OPENAI_REMOTE_COMPACTION_SCHEMA, providerNativeItem, } from "./openai-remote-convert.js";
3
+ import { createOpenAiRemoteCompactionHeaders, isOpenAiRemoteCompactionModel, matchesOpenAiRemoteCompactionIdentity, openAiRemoteCompactionEndpointPath, openAiRemoteCompactionEndpointUrl, openAiRemoteCompactionIdentity, } from "./openai-remote-model.js";
4
+ export { getOpenAiRemoteCompactionDetails, OPENAI_REMOTE_COMPACTION_SCHEMA } from "./openai-remote-convert.js";
3
5
  export const SENPI_COMPACTION_EVENT = "senpi:compaction";
4
6
  const OPENAI_REMOTE_COMPACTION_TIMEOUT_MS = 15_000;
5
7
  const REMOTE_COMPACTION_TIMEOUT_REASON = "remote-compaction-timeout";
6
- function isRecord(value) {
7
- return typeof value === "object" && value !== null && !Array.isArray(value);
8
- }
9
- function parseJsonRecord(value) {
10
- if (!value?.startsWith("{"))
11
- return undefined;
12
- try {
13
- const parsed = JSON.parse(value);
14
- return isRecord(parsed) ? parsed : undefined;
15
- }
16
- catch {
17
- return undefined;
18
- }
19
- }
20
- function parseTextSignature(signature) {
21
- if (!signature)
22
- return undefined;
23
- const parsed = parseJsonRecord(signature);
24
- if (parsed?.v === 1 && typeof parsed.id === "string") {
25
- if (parsed.phase === "commentary" || parsed.phase === "final_answer") {
26
- return { id: parsed.id, phase: parsed.phase };
27
- }
28
- return { id: parsed.id };
29
- }
30
- return { id: signature };
31
- }
32
- function isOpenAiResponsesModel(model) {
33
- return model?.provider === "openai" && model.api === "openai-responses";
34
- }
35
8
  function supportsOpenAiResponsesWebSocket(model) {
9
+ if (model.provider !== "openai" || model.api !== "openai-responses")
10
+ return false;
36
11
  if (model.compat?.supportsWebSocket !== undefined)
37
12
  return model.compat.supportsWebSocket;
38
13
  try {
@@ -42,196 +17,11 @@ function supportsOpenAiResponsesWebSocket(model) {
42
17
  return false;
43
18
  }
44
19
  }
45
- function toolResultText(content) {
46
- if (typeof content === "string")
47
- return content;
48
- const parts = [];
49
- for (const block of content) {
50
- if (block.type !== "text")
51
- return undefined;
52
- parts.push(block.text);
53
- }
54
- return parts.join("\n");
55
- }
56
- function convertUserContent(content) {
57
- if (typeof content === "string")
58
- return [{ type: "input_text", text: content }];
59
- return content.map((block) => {
60
- if (block.type === "text")
61
- return { type: "input_text", text: block.text };
62
- return {
63
- type: "input_image",
64
- detail: "auto",
65
- image_url: `data:${block.mimeType};base64,${block.data}`,
66
- };
67
- });
68
- }
69
- function providerNativeItem(raw) {
70
- if (!isRecord(raw) || typeof raw.type !== "string")
71
- return undefined;
72
- return { ...raw, type: raw.type };
73
- }
74
- function convertThinking(block) {
75
- const parsed = parseJsonRecord(block.thinkingSignature);
76
- if (parsed?.type !== "reasoning")
77
- return undefined;
78
- return { ...parsed, type: "reasoning" };
79
- }
80
- function convertTextBlock(block, messageIndex) {
81
- const signature = parseTextSignature(block.textSignature);
82
- const item = {
83
- type: "message",
84
- role: "assistant",
85
- status: "completed",
86
- id: signature?.id ?? `msg_${messageIndex}`,
87
- content: [{ type: "output_text", text: block.text, annotations: [] }],
88
- ...(signature?.phase ? { phase: signature.phase } : {}),
89
- };
90
- return item;
91
- }
92
- function convertToolCall(block) {
93
- const [callId = block.id, itemId] = block.id.split("|");
94
- return {
95
- type: "function_call",
96
- // The Responses API rejects item ids not beginning with "fc"; custom tool
97
- // calls carry the "<call_id>|custom" sentinel, not a server-issued id.
98
- ...(itemId?.startsWith("fc") ? { id: itemId } : {}),
99
- call_id: callId,
100
- name: block.name,
101
- arguments: JSON.stringify(block.arguments ?? {}),
102
- };
103
- }
104
- function isSameOpenAiResponsesAssistant(message) {
105
- return message.provider === "openai" && message.api === "openai-responses";
106
- }
107
- function convertAssistantMessage(message, messageIndex) {
108
- if (!isSameOpenAiResponsesAssistant(message))
109
- return undefined;
110
- const items = [];
111
- for (const block of message.content) {
112
- switch (block.type) {
113
- case "text":
114
- items.push(convertTextBlock(block, messageIndex));
115
- break;
116
- case "thinking": {
117
- const reasoning = convertThinking(block);
118
- if (!reasoning)
119
- return undefined;
120
- items.push(reasoning);
121
- break;
122
- }
123
- case "toolCall":
124
- items.push(convertToolCall(block));
125
- break;
126
- case "providerNative": {
127
- const item = providerNativeItem(block.raw);
128
- if (!item)
129
- return undefined;
130
- items.push(item);
131
- break;
132
- }
133
- }
134
- }
135
- return items.length > 0 ? items : undefined;
136
- }
137
- function convertAgentMessage(message, messageIndex) {
138
- switch (message.role) {
139
- case "user":
140
- return [{ role: "user", content: convertUserContent(message.content) }];
141
- case "assistant":
142
- return convertAssistantMessage(message, messageIndex);
143
- case "toolResult": {
144
- const [callId = message.toolCallId] = message.toolCallId.split("|");
145
- const output = toolResultText(message.content);
146
- if (output === undefined)
147
- return undefined;
148
- return [{ type: "function_call_output", call_id: callId, output }];
149
- }
150
- case "bashExecution":
151
- case "branchSummary":
152
- case "compactionSummary":
153
- case "custom":
154
- return undefined;
155
- default: {
156
- const exhaustive = message;
157
- return exhaustive;
158
- }
159
- }
160
- }
161
- function detailsFromEntry(entry) {
162
- if (entry.type !== "compaction")
163
- return undefined;
164
- return getOpenAiRemoteCompactionDetails(entry.details);
165
- }
166
- export function getOpenAiRemoteCompactionDetails(value) {
167
- if (!isRecord(value))
168
- return undefined;
169
- if (value.schema !== OPENAI_REMOTE_COMPACTION_SCHEMA || value.mode !== "openai-remote")
170
- return undefined;
171
- if (value.provider !== "openai" || value.api !== "openai-responses")
172
- return undefined;
173
- if (typeof value.modelId !== "string" || typeof value.responseId !== "string")
174
- return undefined;
175
- if (typeof value.createdAt !== "number")
176
- return undefined;
177
- if (typeof value.requestInputItemCount !== "number" || typeof value.retainedInputItemCount !== "number") {
178
- return undefined;
179
- }
180
- if (!Array.isArray(value.replacementInput))
181
- return undefined;
182
- return {
183
- schema: OPENAI_REMOTE_COMPACTION_SCHEMA,
184
- mode: "openai-remote",
185
- provider: "openai",
186
- api: "openai-responses",
187
- transport: value.transport === "websocket" ? "websocket" : "compact-endpoint",
188
- modelId: value.modelId,
189
- responseId: value.responseId,
190
- createdAt: value.createdAt,
191
- requestInputItemCount: value.requestInputItemCount,
192
- retainedInputItemCount: value.retainedInputItemCount,
193
- replacementInput: value.replacementInput.filter((item) => isRecord(item)),
194
- ...(isRecord(value.usage) ? { usage: value.usage } : {}),
195
- };
196
- }
197
- function convertBranchEntries(entries) {
198
- const items = [];
199
- let messageIndex = 0;
200
- for (const entry of entries) {
201
- switch (entry.type) {
202
- case "message": {
203
- const converted = convertAgentMessage(entry.message, messageIndex);
204
- if (!converted)
205
- return undefined;
206
- items.push(...converted);
207
- messageIndex++;
208
- break;
209
- }
210
- case "compaction": {
211
- const details = detailsFromEntry(entry);
212
- if (!details)
213
- return undefined;
214
- items.push(...details.replacementInput);
215
- break;
216
- }
217
- case "branch_summary":
218
- case "custom_message":
219
- return undefined;
220
- case "thinking_level_change":
221
- case "model_change":
222
- case "custom":
223
- case "label":
224
- case "session_info":
225
- break;
226
- }
227
- }
228
- return items;
229
- }
230
20
  export function createOpenAiRemoteCompactionRequest(options) {
231
- if (!isOpenAiResponsesModel(options.model))
21
+ if (!isOpenAiRemoteCompactionModel(options.model))
232
22
  return undefined;
233
- const input = convertBranchEntries(options.branchEntries);
234
- if (!input || input.length === 0)
23
+ const input = convertBranchEntries(options.branchEntries, options.model);
24
+ if (input.length === 0)
235
25
  return undefined;
236
26
  return {
237
27
  body: {
@@ -245,32 +35,23 @@ export function createOpenAiRemoteCompactionRequest(options) {
245
35
  tokensBefore: options.tokensBefore,
246
36
  };
247
37
  }
248
- function isOpenAiCompactionItem(item) {
249
- return item.type === "compaction" && typeof item.encrypted_content === "string";
250
- }
251
- function isOpenAiContextCompactionItem(item) {
252
- return item.type === "context_compaction" && typeof item.encrypted_content === "string";
253
- }
254
- function isOpenAiRemoteCompactionOutputItem(item) {
255
- return isOpenAiCompactionItem(item) || isOpenAiContextCompactionItem(item);
256
- }
257
- function isRetainedRemoteOutputItem(item) {
258
- if (isOpenAiRemoteCompactionOutputItem(item))
259
- return true;
260
- return item.type === "message" && (item.role === "user" || item.role === "system" || item.role === "developer");
261
- }
262
- function isRetainedResponsesStreamInputItem(item) {
263
- if (item.type === "message")
264
- return item.role === "user";
265
- return "role" in item && item.role === "user";
266
- }
267
- function isOpenAiCompactedResponse(value) {
268
- if (!isRecord(value))
269
- return false;
270
- if (value.object !== "response.compaction" || typeof value.id !== "string" || typeof value.created_at !== "number") {
271
- return false;
38
+ function parseOpenAiCompactedResponse(options) {
39
+ if (!isRecord(options.value) || !Array.isArray(options.value.output))
40
+ return undefined;
41
+ if (options.model.api === "openai-responses") {
42
+ if (options.value.object !== "response.compaction" ||
43
+ typeof options.value.id !== "string" ||
44
+ typeof options.value.created_at !== "number") {
45
+ return undefined;
46
+ }
272
47
  }
273
- return Array.isArray(value.output);
48
+ return {
49
+ id: typeof options.value.id === "string" ? options.value.id : `codex-compact:${options.requestId}`,
50
+ created_at: typeof options.value.created_at === "number" ? options.value.created_at : Math.floor(options.now() / 1000),
51
+ object: "response.compaction",
52
+ output: options.value.output.filter((item) => isRecord(item)),
53
+ ...(isRecord(options.value.usage) ? { usage: options.value.usage } : {}),
54
+ };
274
55
  }
275
56
  export function buildOpenAiRemoteCompactionResult(options) {
276
57
  const replacementInput = options.response.output.filter(isRetainedRemoteOutputItem);
@@ -281,8 +62,7 @@ export function buildOpenAiRemoteCompactionResult(options) {
281
62
  const details = {
282
63
  schema: OPENAI_REMOTE_COMPACTION_SCHEMA,
283
64
  mode: "openai-remote",
284
- provider: "openai",
285
- api: "openai-responses",
65
+ ...openAiRemoteCompactionIdentity(options.model),
286
66
  transport: "compact-endpoint",
287
67
  modelId: options.model.id,
288
68
  responseId: options.response.id,
@@ -292,10 +72,13 @@ export function buildOpenAiRemoteCompactionResult(options) {
292
72
  replacementInput,
293
73
  ...(options.response.usage ? { usage: options.response.usage } : {}),
294
74
  };
75
+ const endpointPath = options.model.api === "openai-codex-responses"
76
+ ? `/${openAiRemoteCompactionEndpointPath(options.model)}`
77
+ : "/v1/responses/compact";
295
78
  return {
296
79
  summary: [
297
80
  "OpenAI remote compaction checkpoint.",
298
- `Native /v1/responses/compact replay is active for ${replacementInput.length.toLocaleString()} retained item(s).`,
81
+ `Native ${endpointPath} replay is active for ${replacementInput.length.toLocaleString()} retained item(s).`,
299
82
  `Original OpenAI input items compacted: ${options.requestInputItemCount.toLocaleString()}.`,
300
83
  ].join("\n"),
301
84
  firstKeptEntryId: options.firstKeptEntryId,
@@ -303,18 +86,6 @@ export function buildOpenAiRemoteCompactionResult(options) {
303
86
  details,
304
87
  };
305
88
  }
306
- function compactEndpointUrl(model) {
307
- const baseUrl = model.baseUrl || "https://api.openai.com/v1";
308
- return new URL("responses/compact", baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
309
- }
310
- function createHeaders(auth) {
311
- const headers = new Headers(auth.headers);
312
- headers.set("content-type", "application/json");
313
- if (!headers.has("authorization") && auth.apiKey) {
314
- headers.set("authorization", `Bearer ${auth.apiKey}`);
315
- }
316
- return headers.has("authorization") ? headers : undefined;
317
- }
318
89
  async function runWithRemoteTimeout(options) {
319
90
  if (options.signal.aborted) {
320
91
  throw new Error("Request was aborted");
@@ -464,7 +235,7 @@ async function runOpenAiCompactEndpointCompaction(options) {
464
235
  });
465
236
  let response;
466
237
  try {
467
- response = await options.fetchImpl(compactEndpointUrl(options.model), {
238
+ response = await options.fetchImpl(openAiRemoteCompactionEndpointUrl(options.model), {
468
239
  method: "POST",
469
240
  headers: options.headers,
470
241
  body: JSON.stringify(options.request.body),
@@ -513,7 +284,13 @@ async function runOpenAiCompactEndpointCompaction(options) {
513
284
  });
514
285
  return undefined;
515
286
  }
516
- if (!isOpenAiCompactedResponse(payload)) {
287
+ const compactedResponse = parseOpenAiCompactedResponse({
288
+ value: payload,
289
+ model: options.model,
290
+ requestId: options.requestId,
291
+ now: options.now,
292
+ });
293
+ if (!compactedResponse) {
517
294
  options.emit?.({
518
295
  version: 1,
519
296
  action: "remote_fallback",
@@ -532,7 +309,7 @@ async function runOpenAiCompactEndpointCompaction(options) {
532
309
  firstKeptEntryId: options.firstKeptEntryId,
533
310
  tokensBefore: options.request.tokensBefore,
534
311
  requestInputItemCount: options.request.inputItemCount,
535
- response: payload,
312
+ response: compactedResponse,
536
313
  });
537
314
  }
538
315
  catch (error) {
@@ -553,7 +330,7 @@ async function runOpenAiCompactEndpointCompaction(options) {
553
330
  route: "builtin.compaction.openai_remote",
554
331
  requestId: options.requestId,
555
332
  modelId: options.model.id,
556
- responseId: payload.id,
333
+ responseId: compactedResponse.id,
557
334
  retainedInputItemCount: result.details.retainedInputItemCount,
558
335
  transport: "compact-endpoint",
559
336
  });
@@ -561,7 +338,7 @@ async function runOpenAiCompactEndpointCompaction(options) {
561
338
  }
562
339
  export async function runOpenAiRemoteCompaction(ctx, event, emit, dependencies = {}) {
563
340
  const model = ctx.model;
564
- if (!isOpenAiResponsesModel(model) || event.reason === "branch") {
341
+ if (!isOpenAiRemoteCompactionModel(model) || event.reason === "branch") {
565
342
  emit?.({
566
343
  version: 1,
567
344
  action: "remote_fallback",
@@ -601,7 +378,7 @@ export async function runOpenAiRemoteCompaction(ctx, event, emit, dependencies =
601
378
  route: "builtin.compaction.openai_remote",
602
379
  requestId: event.requestId,
603
380
  modelId: model.id,
604
- reason: "session-not-openai-native",
381
+ reason: "empty-compaction-input",
605
382
  });
606
383
  return undefined;
607
384
  }
@@ -678,7 +455,7 @@ export async function runOpenAiRemoteCompaction(ctx, event, emit, dependencies =
678
455
  });
679
456
  }
680
457
  }
681
- const headers = createHeaders(auth);
458
+ const headers = createOpenAiRemoteCompactionHeaders(requestModel, auth, request.body.prompt_cache_key);
682
459
  if (!headers) {
683
460
  emit?.({
684
461
  version: 1,
@@ -710,6 +487,7 @@ export async function runOpenAiRemoteCompaction(ctx, event, emit, dependencies =
710
487
  requestId: event.requestId,
711
488
  signal,
712
489
  firstKeptEntryId: event.preparation.firstKeptEntryId,
490
+ now: dependencies.now ?? Date.now,
713
491
  emit,
714
492
  }),
715
493
  });
@@ -741,15 +519,21 @@ function leadingPromptMessages(input) {
741
519
  return result;
742
520
  }
743
521
  export function rewriteOpenAiPayloadWithRemoteCompaction(payload, options, emit) {
744
- if (!isOpenAiResponsesModel(options.model) || !isRecord(payload))
522
+ if (!isOpenAiRemoteCompactionModel(options.model) || !isRecord(payload))
745
523
  return undefined;
746
524
  const remote = latestRemoteCompaction(options.branchEntries);
747
- if (!remote)
748
- return undefined;
749
- const postCompactionItems = convertBranchEntries(options.branchEntries.slice(remote.index + 1));
750
- if (!postCompactionItems)
525
+ if (!remote || !matchesOpenAiRemoteCompactionIdentity(options.model, remote.details))
751
526
  return undefined;
752
- const input = [...leadingPromptMessages(payload.input), ...remote.details.replacementInput, ...postCompactionItems];
527
+ const postCompactionItems = convertBranchEntries(options.branchEntries.slice(remote.index + 1), options.model);
528
+ // The in-flight prompt is not yet persisted in the branch at provider-request
529
+ // time; append it so the replayed payload still carries the user's message.
530
+ const pendingItems = convertPendingMessages(options.pendingMessages ?? [], options.model);
531
+ const input = [
532
+ ...leadingPromptMessages(payload.input),
533
+ ...remote.details.replacementInput,
534
+ ...postCompactionItems,
535
+ ...pendingItems,
536
+ ];
753
537
  emit?.({
754
538
  version: 1,
755
539
  action: "remote_payload_rewritten",