@code-yeongyu/senpi 2026.5.14 → 2026.5.15-3

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 (175) hide show
  1. package/CHANGELOG.md +1110 -1175
  2. package/README.md +1 -2
  3. package/dist/core/agent-session.d.ts +9 -0
  4. package/dist/core/agent-session.d.ts.map +1 -1
  5. package/dist/core/agent-session.js +109 -7
  6. package/dist/core/agent-session.js.map +1 -1
  7. package/dist/core/dynamic-prompt/verification.d.ts +31 -0
  8. package/dist/core/dynamic-prompt/verification.d.ts.map +1 -1
  9. package/dist/core/dynamic-prompt/verification.js +41 -0
  10. package/dist/core/dynamic-prompt/verification.js.map +1 -1
  11. package/dist/core/extensions/builtin/compaction/context-reduction.d.ts +97 -0
  12. package/dist/core/extensions/builtin/compaction/context-reduction.d.ts.map +1 -0
  13. package/dist/core/extensions/builtin/compaction/context-reduction.js +420 -0
  14. package/dist/core/extensions/builtin/compaction/context-reduction.js.map +1 -0
  15. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  16. package/dist/core/extensions/builtin/compaction/index.js +168 -31
  17. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  18. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +197 -0
  19. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -0
  20. package/dist/core/extensions/builtin/compaction/openai-remote.js +690 -0
  21. package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -0
  22. package/dist/core/extensions/builtin/compaction/prompts.d.ts +3 -3
  23. package/dist/core/extensions/builtin/compaction/prompts.d.ts.map +1 -1
  24. package/dist/core/extensions/builtin/compaction/prompts.js +0 -22
  25. package/dist/core/extensions/builtin/compaction/prompts.js.map +1 -1
  26. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.d.ts +4 -0
  27. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.d.ts.map +1 -0
  28. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.js +48 -0
  29. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.js.map +1 -0
  30. package/dist/core/extensions/builtin/compaction/speculative.d.ts +3 -1
  31. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  32. package/dist/core/extensions/builtin/compaction/speculative.js +82 -33
  33. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  34. package/dist/core/extensions/builtin/compaction/todo-bridge.d.ts +8 -0
  35. package/dist/core/extensions/builtin/compaction/todo-bridge.d.ts.map +1 -1
  36. package/dist/core/extensions/builtin/compaction/todo-bridge.js +12 -6
  37. package/dist/core/extensions/builtin/compaction/todo-bridge.js.map +1 -1
  38. package/dist/core/extensions/builtin/index.d.ts.map +1 -1
  39. package/dist/core/extensions/builtin/index.js +0 -2
  40. package/dist/core/extensions/builtin/index.js.map +1 -1
  41. package/dist/core/extensions/builtin/openai-web-search/index.d.ts.map +1 -1
  42. package/dist/core/extensions/builtin/openai-web-search/index.js +26 -1
  43. package/dist/core/extensions/builtin/openai-web-search/index.js.map +1 -1
  44. package/dist/core/extensions/builtin/permission-system/prompt.d.ts.map +1 -1
  45. package/dist/core/extensions/builtin/permission-system/prompt.js +0 -5
  46. package/dist/core/extensions/builtin/permission-system/prompt.js.map +1 -1
  47. package/dist/core/extensions/builtin/system-messages.d.ts +7 -7
  48. package/dist/core/extensions/builtin/system-messages.d.ts.map +1 -1
  49. package/dist/core/extensions/builtin/system-messages.js +10 -10
  50. package/dist/core/extensions/builtin/system-messages.js.map +1 -1
  51. package/dist/core/extensions/builtin/todotools/continuation/prompt.d.ts +1 -1
  52. package/dist/core/extensions/builtin/todotools/continuation/prompt.d.ts.map +1 -1
  53. package/dist/core/extensions/builtin/todotools/continuation/prompt.js +1 -1
  54. package/dist/core/extensions/builtin/todotools/continuation/prompt.js.map +1 -1
  55. package/dist/core/extensions/builtin/todotools/state.d.ts +1 -1
  56. package/dist/core/extensions/builtin/todotools/state.d.ts.map +1 -1
  57. package/dist/core/extensions/builtin/todotools/state.js +1 -1
  58. package/dist/core/extensions/builtin/todotools/state.js.map +1 -1
  59. package/dist/core/extensions/builtin/todotools/system-messages.d.ts +3 -3
  60. package/dist/core/extensions/builtin/todotools/system-messages.d.ts.map +1 -1
  61. package/dist/core/extensions/builtin/todotools/system-messages.js +6 -6
  62. package/dist/core/extensions/builtin/todotools/system-messages.js.map +1 -1
  63. package/dist/core/extensions/builtin/tool-pair-guard/index.d.ts +1 -1
  64. package/dist/core/extensions/builtin/tool-pair-guard/index.d.ts.map +1 -1
  65. package/dist/core/extensions/builtin/tool-pair-guard/index.js +8 -4
  66. package/dist/core/extensions/builtin/tool-pair-guard/index.js.map +1 -1
  67. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.d.ts +3 -0
  68. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.d.ts.map +1 -0
  69. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.js +89 -0
  70. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.js.map +1 -0
  71. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.d.ts +3 -0
  72. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.d.ts.map +1 -0
  73. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.js +122 -0
  74. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.js.map +1 -0
  75. package/dist/core/extensions/loader.d.ts.map +1 -1
  76. package/dist/core/extensions/loader.js +2 -0
  77. package/dist/core/extensions/loader.js.map +1 -1
  78. package/dist/core/extensions/runner.d.ts +3 -0
  79. package/dist/core/extensions/runner.d.ts.map +1 -1
  80. package/dist/core/extensions/runner.js +18 -0
  81. package/dist/core/extensions/runner.js.map +1 -1
  82. package/dist/core/extensions/types.d.ts +22 -0
  83. package/dist/core/extensions/types.d.ts.map +1 -1
  84. package/dist/core/extensions/types.js.map +1 -1
  85. package/dist/core/messages.d.ts +3 -3
  86. package/dist/core/messages.d.ts.map +1 -1
  87. package/dist/core/messages.js +5 -10
  88. package/dist/core/messages.js.map +1 -1
  89. package/dist/core/model-registry.d.ts.map +1 -1
  90. package/dist/core/model-registry.js +1 -0
  91. package/dist/core/model-registry.js.map +1 -1
  92. package/dist/core/sdk.d.ts +1 -1
  93. package/dist/core/sdk.d.ts.map +1 -1
  94. package/dist/core/sdk.js +7 -22
  95. package/dist/core/sdk.js.map +1 -1
  96. package/dist/core/session-manager.d.ts.map +1 -1
  97. package/dist/core/session-manager.js +1 -1
  98. package/dist/core/session-manager.js.map +1 -1
  99. package/dist/core/settings-manager.d.ts +0 -5
  100. package/dist/core/settings-manager.d.ts.map +1 -1
  101. package/dist/core/settings-manager.js.map +1 -1
  102. package/dist/core/thinking-levels.d.ts +6 -0
  103. package/dist/core/thinking-levels.d.ts.map +1 -0
  104. package/dist/core/thinking-levels.js +36 -0
  105. package/dist/core/thinking-levels.js.map +1 -0
  106. package/dist/core/tools/bash.d.ts.map +1 -1
  107. package/dist/core/tools/bash.js +15 -1
  108. package/dist/core/tools/bash.js.map +1 -1
  109. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  110. package/dist/modes/interactive/components/compaction-summary-message.js +20 -2
  111. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  112. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  113. package/dist/modes/interactive/components/keybinding-hints.js +3 -1
  114. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  115. package/dist/modes/interactive/interactive-mode.d.ts +8 -0
  116. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  117. package/dist/modes/interactive/interactive-mode.js +137 -49
  118. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  119. package/dist/modes/interactive/working-status.d.ts +15 -0
  120. package/dist/modes/interactive/working-status.d.ts.map +1 -0
  121. package/dist/modes/interactive/working-status.js +60 -0
  122. package/dist/modes/interactive/working-status.js.map +1 -0
  123. package/docs/extensions.md +0 -1
  124. package/docs/index.md +0 -1
  125. package/docs/sdk.md +0 -1
  126. package/docs/settings.md +1 -29
  127. package/docs/termux.md +2 -2
  128. package/docs/usage.md +1 -1
  129. package/examples/README.md +1 -1
  130. package/examples/extensions/README.md +0 -1
  131. package/examples/extensions/overlay-qa-tests.ts +1 -1
  132. package/package.json +4 -4
  133. package/dist/core/extensions/builtin/background-task/cancel-tool.d.ts +0 -10
  134. package/dist/core/extensions/builtin/background-task/cancel-tool.d.ts.map +0 -1
  135. package/dist/core/extensions/builtin/background-task/cancel-tool.js +0 -109
  136. package/dist/core/extensions/builtin/background-task/cancel-tool.js.map +0 -1
  137. package/dist/core/extensions/builtin/background-task/index.d.ts +0 -3
  138. package/dist/core/extensions/builtin/background-task/index.d.ts.map +0 -1
  139. package/dist/core/extensions/builtin/background-task/index.js +0 -207
  140. package/dist/core/extensions/builtin/background-task/index.js.map +0 -1
  141. package/dist/core/extensions/builtin/background-task/manager.d.ts +0 -17
  142. package/dist/core/extensions/builtin/background-task/manager.d.ts.map +0 -1
  143. package/dist/core/extensions/builtin/background-task/manager.js +0 -114
  144. package/dist/core/extensions/builtin/background-task/manager.js.map +0 -1
  145. package/dist/core/extensions/builtin/background-task/notification.d.ts +0 -22
  146. package/dist/core/extensions/builtin/background-task/notification.d.ts.map +0 -1
  147. package/dist/core/extensions/builtin/background-task/notification.js +0 -105
  148. package/dist/core/extensions/builtin/background-task/notification.js.map +0 -1
  149. package/dist/core/extensions/builtin/background-task/output-tool.d.ts +0 -11
  150. package/dist/core/extensions/builtin/background-task/output-tool.d.ts.map +0 -1
  151. package/dist/core/extensions/builtin/background-task/output-tool.js +0 -127
  152. package/dist/core/extensions/builtin/background-task/output-tool.js.map +0 -1
  153. package/dist/core/extensions/builtin/background-task/spawner.d.ts +0 -8
  154. package/dist/core/extensions/builtin/background-task/spawner.d.ts.map +0 -1
  155. package/dist/core/extensions/builtin/background-task/spawner.js +0 -207
  156. package/dist/core/extensions/builtin/background-task/spawner.js.map +0 -1
  157. package/dist/core/extensions/builtin/background-task/task-tool.d.ts +0 -20
  158. package/dist/core/extensions/builtin/background-task/task-tool.d.ts.map +0 -1
  159. package/dist/core/extensions/builtin/background-task/task-tool.js +0 -302
  160. package/dist/core/extensions/builtin/background-task/task-tool.js.map +0 -1
  161. package/dist/core/extensions/builtin/background-task/types.d.ts +0 -72
  162. package/dist/core/extensions/builtin/background-task/types.d.ts.map +0 -1
  163. package/dist/core/extensions/builtin/background-task/types.js +0 -32
  164. package/dist/core/extensions/builtin/background-task/types.js.map +0 -1
  165. package/docs/agents.md +0 -348
  166. package/examples/extensions/subagent/README.md +0 -172
  167. package/examples/extensions/subagent/agents/planner.md +0 -37
  168. package/examples/extensions/subagent/agents/reviewer.md +0 -35
  169. package/examples/extensions/subagent/agents/scout.md +0 -50
  170. package/examples/extensions/subagent/agents/worker.md +0 -24
  171. package/examples/extensions/subagent/agents.ts +0 -126
  172. package/examples/extensions/subagent/index.ts +0 -987
  173. package/examples/extensions/subagent/prompts/implement-and-review.md +0 -10
  174. package/examples/extensions/subagent/prompts/implement.md +0 -10
  175. package/examples/extensions/subagent/prompts/scout-and-plan.md +0 -9
@@ -0,0 +1,690 @@
1
+ import { streamSimple, } from "@earendil-works/pi-ai";
2
+ export const OPENAI_REMOTE_COMPACTION_SCHEMA = "senpi.compaction.openai-remote.v1";
3
+ export const SENPI_COMPACTION_EVENT = "senpi:compaction";
4
+ function isRecord(value) {
5
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6
+ }
7
+ function parseJsonRecord(value) {
8
+ if (!value?.startsWith("{"))
9
+ return undefined;
10
+ try {
11
+ const parsed = JSON.parse(value);
12
+ return isRecord(parsed) ? parsed : undefined;
13
+ }
14
+ catch {
15
+ return undefined;
16
+ }
17
+ }
18
+ function parseTextSignature(signature) {
19
+ if (!signature)
20
+ return undefined;
21
+ const parsed = parseJsonRecord(signature);
22
+ if (parsed?.v === 1 && typeof parsed.id === "string") {
23
+ if (parsed.phase === "commentary" || parsed.phase === "final_answer") {
24
+ return { id: parsed.id, phase: parsed.phase };
25
+ }
26
+ return { id: parsed.id };
27
+ }
28
+ return { id: signature };
29
+ }
30
+ function isOpenAiResponsesModel(model) {
31
+ return model?.provider === "openai" && model.api === "openai-responses";
32
+ }
33
+ function supportsOpenAiResponsesWebSocket(model) {
34
+ if (model.compat?.supportsWebSocket !== undefined)
35
+ return model.compat.supportsWebSocket;
36
+ try {
37
+ return new URL(model.baseUrl || "https://api.openai.com/v1").hostname === "api.openai.com";
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ }
43
+ function toolResultText(content) {
44
+ if (typeof content === "string")
45
+ return content;
46
+ const parts = [];
47
+ for (const block of content) {
48
+ if (block.type !== "text")
49
+ return undefined;
50
+ parts.push(block.text);
51
+ }
52
+ return parts.join("\n");
53
+ }
54
+ function convertUserContent(content) {
55
+ if (typeof content === "string")
56
+ return [{ type: "input_text", text: content }];
57
+ return content.map((block) => {
58
+ if (block.type === "text")
59
+ return { type: "input_text", text: block.text };
60
+ return {
61
+ type: "input_image",
62
+ detail: "auto",
63
+ image_url: `data:${block.mimeType};base64,${block.data}`,
64
+ };
65
+ });
66
+ }
67
+ function providerNativeItem(raw) {
68
+ if (!isRecord(raw) || typeof raw.type !== "string")
69
+ return undefined;
70
+ return { ...raw, type: raw.type };
71
+ }
72
+ function convertThinking(block) {
73
+ const parsed = parseJsonRecord(block.thinkingSignature);
74
+ if (parsed?.type !== "reasoning")
75
+ return undefined;
76
+ return { ...parsed, type: "reasoning" };
77
+ }
78
+ function convertTextBlock(block, messageIndex) {
79
+ const signature = parseTextSignature(block.textSignature);
80
+ const item = {
81
+ type: "message",
82
+ role: "assistant",
83
+ status: "completed",
84
+ id: signature?.id ?? `msg_${messageIndex}`,
85
+ content: [{ type: "output_text", text: block.text, annotations: [] }],
86
+ ...(signature?.phase ? { phase: signature.phase } : {}),
87
+ };
88
+ return item;
89
+ }
90
+ function convertToolCall(block) {
91
+ const [callId = block.id, itemId] = block.id.split("|");
92
+ return {
93
+ type: "function_call",
94
+ ...(itemId ? { id: itemId } : {}),
95
+ call_id: callId,
96
+ name: block.name,
97
+ arguments: JSON.stringify(block.arguments ?? {}),
98
+ };
99
+ }
100
+ function isSameOpenAiResponsesAssistant(message) {
101
+ return message.provider === "openai" && message.api === "openai-responses";
102
+ }
103
+ function convertAssistantMessage(message, messageIndex) {
104
+ if (!isSameOpenAiResponsesAssistant(message))
105
+ return undefined;
106
+ const items = [];
107
+ for (const block of message.content) {
108
+ switch (block.type) {
109
+ case "text":
110
+ items.push(convertTextBlock(block, messageIndex));
111
+ break;
112
+ case "thinking": {
113
+ const reasoning = convertThinking(block);
114
+ if (!reasoning)
115
+ return undefined;
116
+ items.push(reasoning);
117
+ break;
118
+ }
119
+ case "toolCall":
120
+ items.push(convertToolCall(block));
121
+ break;
122
+ case "providerNative": {
123
+ const item = providerNativeItem(block.raw);
124
+ if (!item)
125
+ return undefined;
126
+ items.push(item);
127
+ break;
128
+ }
129
+ }
130
+ }
131
+ return items.length > 0 ? items : undefined;
132
+ }
133
+ function convertAgentMessage(message, messageIndex) {
134
+ switch (message.role) {
135
+ case "user":
136
+ return [{ role: "user", content: convertUserContent(message.content) }];
137
+ case "assistant":
138
+ return convertAssistantMessage(message, messageIndex);
139
+ case "toolResult": {
140
+ const [callId = message.toolCallId] = message.toolCallId.split("|");
141
+ const output = toolResultText(message.content);
142
+ if (output === undefined)
143
+ return undefined;
144
+ return [{ type: "function_call_output", call_id: callId, output }];
145
+ }
146
+ case "bashExecution":
147
+ case "branchSummary":
148
+ case "compactionSummary":
149
+ case "custom":
150
+ return undefined;
151
+ default: {
152
+ const exhaustive = message;
153
+ return exhaustive;
154
+ }
155
+ }
156
+ }
157
+ function detailsFromEntry(entry) {
158
+ if (entry.type !== "compaction")
159
+ return undefined;
160
+ return getOpenAiRemoteCompactionDetails(entry.details);
161
+ }
162
+ export function getOpenAiRemoteCompactionDetails(value) {
163
+ if (!isRecord(value))
164
+ return undefined;
165
+ if (value.schema !== OPENAI_REMOTE_COMPACTION_SCHEMA || value.mode !== "openai-remote")
166
+ return undefined;
167
+ if (value.provider !== "openai" || value.api !== "openai-responses")
168
+ return undefined;
169
+ if (typeof value.modelId !== "string" || typeof value.responseId !== "string")
170
+ return undefined;
171
+ if (typeof value.createdAt !== "number")
172
+ return undefined;
173
+ if (typeof value.requestInputItemCount !== "number" || typeof value.retainedInputItemCount !== "number") {
174
+ return undefined;
175
+ }
176
+ if (!Array.isArray(value.replacementInput))
177
+ return undefined;
178
+ return {
179
+ schema: OPENAI_REMOTE_COMPACTION_SCHEMA,
180
+ mode: "openai-remote",
181
+ provider: "openai",
182
+ api: "openai-responses",
183
+ transport: value.transport === "websocket" ? "websocket" : "compact-endpoint",
184
+ modelId: value.modelId,
185
+ responseId: value.responseId,
186
+ createdAt: value.createdAt,
187
+ requestInputItemCount: value.requestInputItemCount,
188
+ retainedInputItemCount: value.retainedInputItemCount,
189
+ replacementInput: value.replacementInput.filter((item) => isRecord(item)),
190
+ ...(isRecord(value.usage) ? { usage: value.usage } : {}),
191
+ };
192
+ }
193
+ function convertBranchEntries(entries) {
194
+ const items = [];
195
+ let messageIndex = 0;
196
+ for (const entry of entries) {
197
+ switch (entry.type) {
198
+ case "message": {
199
+ const converted = convertAgentMessage(entry.message, messageIndex);
200
+ if (!converted)
201
+ return undefined;
202
+ items.push(...converted);
203
+ messageIndex++;
204
+ break;
205
+ }
206
+ case "compaction": {
207
+ const details = detailsFromEntry(entry);
208
+ if (!details)
209
+ return undefined;
210
+ items.push(...details.replacementInput);
211
+ break;
212
+ }
213
+ case "branch_summary":
214
+ case "custom_message":
215
+ return undefined;
216
+ case "thinking_level_change":
217
+ case "model_change":
218
+ case "custom":
219
+ case "label":
220
+ case "session_info":
221
+ break;
222
+ }
223
+ }
224
+ return items;
225
+ }
226
+ export function createOpenAiRemoteCompactionRequest(options) {
227
+ if (!isOpenAiResponsesModel(options.model))
228
+ return undefined;
229
+ const input = convertBranchEntries(options.branchEntries);
230
+ if (!input || input.length === 0)
231
+ return undefined;
232
+ return {
233
+ body: {
234
+ model: options.model.id,
235
+ input,
236
+ ...(options.systemPrompt ? { instructions: options.systemPrompt } : {}),
237
+ ...(options.promptCacheKey ? { prompt_cache_key: options.promptCacheKey } : {}),
238
+ ...(options.serviceTier ? { service_tier: options.serviceTier } : {}),
239
+ },
240
+ inputItemCount: input.length,
241
+ tokensBefore: options.tokensBefore,
242
+ };
243
+ }
244
+ function isOpenAiCompactionItem(item) {
245
+ return item.type === "compaction" && typeof item.encrypted_content === "string";
246
+ }
247
+ function isOpenAiContextCompactionItem(item) {
248
+ return item.type === "context_compaction" && typeof item.encrypted_content === "string";
249
+ }
250
+ function isOpenAiRemoteCompactionOutputItem(item) {
251
+ return isOpenAiCompactionItem(item) || isOpenAiContextCompactionItem(item);
252
+ }
253
+ function isRetainedRemoteOutputItem(item) {
254
+ if (isOpenAiRemoteCompactionOutputItem(item))
255
+ return true;
256
+ return item.type === "message" && (item.role === "user" || item.role === "system" || item.role === "developer");
257
+ }
258
+ function isRetainedResponsesStreamInputItem(item) {
259
+ if (item.type === "message")
260
+ return item.role === "user";
261
+ return "role" in item && item.role === "user";
262
+ }
263
+ function isOpenAiCompactedResponse(value) {
264
+ if (!isRecord(value))
265
+ return false;
266
+ if (value.object !== "response.compaction" || typeof value.id !== "string" || typeof value.created_at !== "number") {
267
+ return false;
268
+ }
269
+ return Array.isArray(value.output);
270
+ }
271
+ export function buildOpenAiRemoteCompactionResult(options) {
272
+ const replacementInput = options.response.output.filter(isRetainedRemoteOutputItem);
273
+ const compactionItem = replacementInput.find(isOpenAiRemoteCompactionOutputItem);
274
+ if (!compactionItem) {
275
+ throw new Error("OpenAI remote compaction did not return a compaction item");
276
+ }
277
+ const details = {
278
+ schema: OPENAI_REMOTE_COMPACTION_SCHEMA,
279
+ mode: "openai-remote",
280
+ provider: "openai",
281
+ api: "openai-responses",
282
+ transport: "compact-endpoint",
283
+ modelId: options.model.id,
284
+ responseId: options.response.id,
285
+ createdAt: options.response.created_at,
286
+ requestInputItemCount: options.requestInputItemCount,
287
+ retainedInputItemCount: replacementInput.length,
288
+ replacementInput,
289
+ ...(options.response.usage ? { usage: options.response.usage } : {}),
290
+ };
291
+ return {
292
+ summary: [
293
+ "OpenAI remote compaction checkpoint.",
294
+ `Native /v1/responses/compact replay is active for ${replacementInput.length.toLocaleString()} retained item(s).`,
295
+ `Original OpenAI input items compacted: ${options.requestInputItemCount.toLocaleString()}.`,
296
+ ].join("\n"),
297
+ firstKeptEntryId: options.firstKeptEntryId,
298
+ tokensBefore: options.tokensBefore,
299
+ details,
300
+ };
301
+ }
302
+ function compactEndpointUrl(model) {
303
+ const baseUrl = model.baseUrl || "https://api.openai.com/v1";
304
+ return new URL("responses/compact", baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`).toString();
305
+ }
306
+ function createHeaders(auth) {
307
+ const headers = new Headers(auth.headers);
308
+ headers.set("content-type", "application/json");
309
+ if (!headers.has("authorization") && auth.apiKey) {
310
+ headers.set("authorization", `Bearer ${auth.apiKey}`);
311
+ }
312
+ return headers.has("authorization") ? headers : undefined;
313
+ }
314
+ function createOpenAiResponsesStreamCompactionInput(request) {
315
+ return [...request.body.input, { type: "context_compaction" }];
316
+ }
317
+ export function createOpenAiResponsesStreamCompactionPayload(payload, request) {
318
+ if (!isRecord(payload))
319
+ return undefined;
320
+ return {
321
+ ...payload,
322
+ model: request.body.model,
323
+ input: [...leadingPromptMessages(payload.input), ...createOpenAiResponsesStreamCompactionInput(request)],
324
+ ...(request.body.prompt_cache_key ? { prompt_cache_key: request.body.prompt_cache_key } : {}),
325
+ ...(request.body.service_tier ? { service_tier: request.body.service_tier } : {}),
326
+ };
327
+ }
328
+ function findResponsesStreamCompactionOutput(message) {
329
+ for (const block of message.content) {
330
+ if (block.type !== "providerNative")
331
+ continue;
332
+ const item = providerNativeItem(block.raw);
333
+ if (item && isOpenAiContextCompactionItem(item))
334
+ return item;
335
+ }
336
+ return undefined;
337
+ }
338
+ function usageRecordFromAssistant(message) {
339
+ return {
340
+ input: message.usage.input,
341
+ output: message.usage.output,
342
+ cacheRead: message.usage.cacheRead,
343
+ cacheWrite: message.usage.cacheWrite,
344
+ totalTokens: message.usage.totalTokens,
345
+ };
346
+ }
347
+ export function buildOpenAiResponsesStreamCompactionResult(options) {
348
+ const compactionItem = findResponsesStreamCompactionOutput(options.response);
349
+ if (!compactionItem) {
350
+ throw new Error("OpenAI Responses stream compaction did not return a context_compaction item");
351
+ }
352
+ const retainedInput = options.requestInput.filter(isRetainedResponsesStreamInputItem);
353
+ const replacementInput = [...retainedInput, compactionItem];
354
+ const details = {
355
+ schema: OPENAI_REMOTE_COMPACTION_SCHEMA,
356
+ mode: "openai-remote",
357
+ provider: "openai",
358
+ api: "openai-responses",
359
+ transport: "websocket",
360
+ modelId: options.model.id,
361
+ responseId: options.response.responseId ?? `response-${options.now()}`,
362
+ createdAt: Math.floor(options.response.timestamp / 1000),
363
+ requestInputItemCount: options.requestInput.length,
364
+ retainedInputItemCount: replacementInput.length,
365
+ replacementInput,
366
+ usage: usageRecordFromAssistant(options.response),
367
+ };
368
+ return {
369
+ summary: [
370
+ "OpenAI remote compaction checkpoint.",
371
+ `Native Responses WebSocket replay is active for ${replacementInput.length.toLocaleString()} retained item(s).`,
372
+ `Original OpenAI input items compacted: ${options.requestInput.length.toLocaleString()}.`,
373
+ ].join("\n"),
374
+ firstKeptEntryId: options.firstKeptEntryId,
375
+ tokensBefore: options.tokensBefore,
376
+ details,
377
+ };
378
+ }
379
+ async function runOpenAiResponsesStreamCompaction(options) {
380
+ const stream = options.streamRunner(options.model, { systemPrompt: options.systemPrompt, messages: [] }, {
381
+ apiKey: options.auth.apiKey,
382
+ cacheRetention: "short",
383
+ extraBody: options.auth.extraBody,
384
+ headers: options.auth.headers,
385
+ onPayload: (payload) => {
386
+ const rewritten = createOpenAiResponsesStreamCompactionPayload(payload, options.request);
387
+ if (!isRecord(rewritten) || !Array.isArray(rewritten.input)) {
388
+ throw new Error("Unable to build OpenAI Responses stream compaction payload");
389
+ }
390
+ return rewritten;
391
+ },
392
+ sessionId: options.request.body.prompt_cache_key,
393
+ signal: options.signal,
394
+ transport: "websocket",
395
+ });
396
+ const response = await stream.result();
397
+ if (response.stopReason === "error" || response.stopReason === "aborted") {
398
+ return undefined;
399
+ }
400
+ return buildOpenAiResponsesStreamCompactionResult({
401
+ model: options.model,
402
+ firstKeptEntryId: options.firstKeptEntryId,
403
+ tokensBefore: options.request.tokensBefore,
404
+ requestInput: options.request.body.input,
405
+ response,
406
+ now: options.now,
407
+ });
408
+ }
409
+ async function runOpenAiCompactEndpointCompaction(options) {
410
+ options.emit?.({
411
+ version: 1,
412
+ action: "remote_started",
413
+ route: "builtin.compaction.openai_remote",
414
+ requestId: options.requestId,
415
+ modelId: options.model.id,
416
+ inputItemCount: options.request.inputItemCount,
417
+ transport: "compact-endpoint",
418
+ });
419
+ let response;
420
+ try {
421
+ response = await options.fetchImpl(compactEndpointUrl(options.model), {
422
+ method: "POST",
423
+ headers: options.headers,
424
+ body: JSON.stringify(options.request.body),
425
+ signal: options.signal,
426
+ });
427
+ }
428
+ catch (error) {
429
+ if (options.signal.aborted)
430
+ throw error;
431
+ options.emit?.({
432
+ version: 1,
433
+ action: "remote_fallback",
434
+ route: "builtin.compaction.openai_remote",
435
+ requestId: options.requestId,
436
+ modelId: options.model.id,
437
+ reason: error instanceof Error ? error.message : String(error),
438
+ transport: "compact-endpoint",
439
+ });
440
+ return undefined;
441
+ }
442
+ if (!response.ok) {
443
+ options.emit?.({
444
+ version: 1,
445
+ action: "remote_fallback",
446
+ route: "builtin.compaction.openai_remote",
447
+ requestId: options.requestId,
448
+ modelId: options.model.id,
449
+ reason: `HTTP ${response.status}`,
450
+ transport: "compact-endpoint",
451
+ });
452
+ return undefined;
453
+ }
454
+ let payload;
455
+ try {
456
+ payload = await response.json();
457
+ }
458
+ catch (error) {
459
+ options.emit?.({
460
+ version: 1,
461
+ action: "remote_fallback",
462
+ route: "builtin.compaction.openai_remote",
463
+ requestId: options.requestId,
464
+ modelId: options.model.id,
465
+ reason: error instanceof Error ? error.message : String(error),
466
+ transport: "compact-endpoint",
467
+ });
468
+ return undefined;
469
+ }
470
+ if (!isOpenAiCompactedResponse(payload)) {
471
+ options.emit?.({
472
+ version: 1,
473
+ action: "remote_fallback",
474
+ route: "builtin.compaction.openai_remote",
475
+ requestId: options.requestId,
476
+ modelId: options.model.id,
477
+ reason: "invalid-compact-response",
478
+ transport: "compact-endpoint",
479
+ });
480
+ return undefined;
481
+ }
482
+ let result;
483
+ try {
484
+ result = buildOpenAiRemoteCompactionResult({
485
+ model: options.model,
486
+ firstKeptEntryId: options.firstKeptEntryId,
487
+ tokensBefore: options.request.tokensBefore,
488
+ requestInputItemCount: options.request.inputItemCount,
489
+ response: payload,
490
+ });
491
+ }
492
+ catch (error) {
493
+ options.emit?.({
494
+ version: 1,
495
+ action: "remote_fallback",
496
+ route: "builtin.compaction.openai_remote",
497
+ requestId: options.requestId,
498
+ modelId: options.model.id,
499
+ reason: error instanceof Error ? error.message : String(error),
500
+ transport: "compact-endpoint",
501
+ });
502
+ return undefined;
503
+ }
504
+ options.emit?.({
505
+ version: 1,
506
+ action: "remote_completed",
507
+ route: "builtin.compaction.openai_remote",
508
+ requestId: options.requestId,
509
+ modelId: options.model.id,
510
+ responseId: payload.id,
511
+ retainedInputItemCount: result.details.retainedInputItemCount,
512
+ transport: "compact-endpoint",
513
+ });
514
+ return result;
515
+ }
516
+ export async function runOpenAiRemoteCompaction(ctx, event, emit, dependencies = {}) {
517
+ const model = ctx.model;
518
+ if (!isOpenAiResponsesModel(model) || event.reason === "branch") {
519
+ emit?.({
520
+ version: 1,
521
+ action: "remote_fallback",
522
+ route: "builtin.compaction.openai_remote",
523
+ requestId: event.requestId,
524
+ modelId: model?.id,
525
+ reason: event.reason === "branch" ? "branch-compaction" : "not-openai-responses",
526
+ });
527
+ return undefined;
528
+ }
529
+ const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
530
+ if (!auth.ok) {
531
+ emit?.({
532
+ version: 1,
533
+ action: "remote_fallback",
534
+ route: "builtin.compaction.openai_remote",
535
+ requestId: event.requestId,
536
+ modelId: model.id,
537
+ reason: auth.error,
538
+ });
539
+ return undefined;
540
+ }
541
+ const requestModel = auth.upstreamModelId ? { ...model, id: auth.upstreamModelId } : model;
542
+ const serviceTier = ctx.serviceTier ?? auth.serviceTier;
543
+ const request = createOpenAiRemoteCompactionRequest({
544
+ model: requestModel,
545
+ systemPrompt: ctx.getSystemPrompt(),
546
+ branchEntries: event.branchEntries,
547
+ tokensBefore: event.preparation.tokensBefore,
548
+ promptCacheKey: ctx.sessionManager.getSessionId(),
549
+ serviceTier,
550
+ });
551
+ if (!request) {
552
+ emit?.({
553
+ version: 1,
554
+ action: "remote_fallback",
555
+ route: "builtin.compaction.openai_remote",
556
+ requestId: event.requestId,
557
+ modelId: model.id,
558
+ reason: "session-not-openai-native",
559
+ });
560
+ return undefined;
561
+ }
562
+ if (supportsOpenAiResponsesWebSocket(requestModel)) {
563
+ emit?.({
564
+ version: 1,
565
+ action: "remote_started",
566
+ route: "builtin.compaction.openai_remote",
567
+ requestId: event.requestId,
568
+ modelId: requestModel.id,
569
+ inputItemCount: request.inputItemCount,
570
+ transport: "websocket",
571
+ });
572
+ try {
573
+ const result = await runOpenAiResponsesStreamCompaction({
574
+ model: requestModel,
575
+ auth,
576
+ firstKeptEntryId: event.preparation.firstKeptEntryId,
577
+ now: dependencies.now ?? Date.now,
578
+ request,
579
+ signal: event.signal,
580
+ streamRunner: dependencies.streamRunner ??
581
+ ((streamModel, context, options) => streamSimple(streamModel, context, options)),
582
+ systemPrompt: ctx.getSystemPrompt(),
583
+ });
584
+ if (result) {
585
+ emit?.({
586
+ version: 1,
587
+ action: "remote_completed",
588
+ route: "builtin.compaction.openai_remote",
589
+ requestId: event.requestId,
590
+ modelId: requestModel.id,
591
+ responseId: result.details.responseId,
592
+ retainedInputItemCount: result.details.retainedInputItemCount,
593
+ transport: "websocket",
594
+ });
595
+ return result;
596
+ }
597
+ emit?.({
598
+ version: 1,
599
+ action: "remote_fallback",
600
+ route: "builtin.compaction.openai_remote",
601
+ requestId: event.requestId,
602
+ modelId: requestModel.id,
603
+ reason: "websocket-compaction-no-result",
604
+ transport: "websocket",
605
+ });
606
+ }
607
+ catch (error) {
608
+ if (event.signal.aborted)
609
+ throw error;
610
+ emit?.({
611
+ version: 1,
612
+ action: "remote_fallback",
613
+ route: "builtin.compaction.openai_remote",
614
+ requestId: event.requestId,
615
+ modelId: requestModel.id,
616
+ reason: error instanceof Error ? error.message : String(error),
617
+ transport: "websocket",
618
+ });
619
+ }
620
+ }
621
+ const headers = createHeaders(auth);
622
+ if (!headers) {
623
+ emit?.({
624
+ version: 1,
625
+ action: "remote_fallback",
626
+ route: "builtin.compaction.openai_remote",
627
+ requestId: event.requestId,
628
+ modelId: model.id,
629
+ reason: "missing-openai-auth",
630
+ });
631
+ return undefined;
632
+ }
633
+ return runOpenAiCompactEndpointCompaction({
634
+ fetchImpl: dependencies.fetch ?? fetch,
635
+ headers,
636
+ model: requestModel,
637
+ request,
638
+ requestId: event.requestId,
639
+ signal: event.signal,
640
+ firstKeptEntryId: event.preparation.firstKeptEntryId,
641
+ emit,
642
+ });
643
+ }
644
+ function latestRemoteCompaction(entries) {
645
+ for (let index = entries.length - 1; index >= 0; index--) {
646
+ const entry = entries[index];
647
+ if (entry?.type !== "compaction")
648
+ continue;
649
+ const details = getOpenAiRemoteCompactionDetails(entry.details);
650
+ if (details)
651
+ return { entryId: entry.id, index, details };
652
+ return undefined;
653
+ }
654
+ return undefined;
655
+ }
656
+ function leadingPromptMessages(input) {
657
+ if (!Array.isArray(input))
658
+ return [];
659
+ const result = [];
660
+ for (const item of input) {
661
+ if (!isRecord(item))
662
+ break;
663
+ const role = item.role;
664
+ if (role !== "system" && role !== "developer")
665
+ break;
666
+ result.push(providerNativeItem(item) ?? { role, content: typeof item.content === "string" ? item.content : [] });
667
+ }
668
+ return result;
669
+ }
670
+ export function rewriteOpenAiPayloadWithRemoteCompaction(payload, options, emit) {
671
+ if (!isOpenAiResponsesModel(options.model) || !isRecord(payload))
672
+ return undefined;
673
+ const remote = latestRemoteCompaction(options.branchEntries);
674
+ if (!remote)
675
+ return undefined;
676
+ const postCompactionItems = convertBranchEntries(options.branchEntries.slice(remote.index + 1));
677
+ if (!postCompactionItems)
678
+ return undefined;
679
+ const input = [...leadingPromptMessages(payload.input), ...remote.details.replacementInput, ...postCompactionItems];
680
+ emit?.({
681
+ version: 1,
682
+ action: "remote_payload_rewritten",
683
+ route: "builtin.compaction.openai_remote",
684
+ modelId: options.model.id,
685
+ compactionEntryId: remote.entryId,
686
+ inputItemCount: input.length,
687
+ });
688
+ return { ...payload, input };
689
+ }
690
+ //# sourceMappingURL=openai-remote.js.map