@moxt-ai/mobius 0.0.307 → 0.0.308
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.
- package/dist/acp/adapter/codex/adapter.d.ts.map +1 -1
- package/dist/acp/adapter/codex/adapter.js +66 -9
- package/dist/acp/adapter/codex/adapter.js.map +1 -1
- package/dist/acp/adapter/codex/tool-event.d.ts +6 -0
- package/dist/acp/adapter/codex/tool-event.d.ts.map +1 -0
- package/dist/acp/adapter/codex/tool-event.js +66 -0
- package/dist/acp/adapter/codex/tool-event.js.map +1 -0
- package/dist/acp/adapter/session-events.d.ts +2 -2
- package/dist/acp/adapter/session-events.d.ts.map +1 -1
- package/dist/acp/adapter/session-events.js +81 -56
- package/dist/acp/adapter/session-events.js.map +1 -1
- package/dist/acp/content/content-externalizer.d.ts.map +1 -1
- package/dist/acp/content/content-externalizer.js +1 -31
- package/dist/acp/content/content-externalizer.js.map +1 -1
- package/dist/acp/harness/codex/agent.d.ts.map +1 -1
- package/dist/acp/harness/codex/agent.js +4 -75
- package/dist/acp/harness/codex/agent.js.map +1 -1
- package/dist/acp/harness/codex/app-server-protocol.d.ts +9 -0
- package/dist/acp/harness/codex/app-server-protocol.d.ts.map +1 -1
- package/dist/acp/harness/codex/app-server-protocol.js +1 -0
- package/dist/acp/harness/codex/app-server-protocol.js.map +1 -1
- package/dist/acp/harness/codex/history.d.ts +5 -0
- package/dist/acp/harness/codex/history.d.ts.map +1 -0
- package/dist/acp/harness/codex/history.js +116 -0
- package/dist/acp/harness/codex/history.js.map +1 -0
- package/dist/cli.js +723 -1346
- package/dist/cli.js.map +4 -4
- package/dist/runtime/command/registry/history.d.ts.map +1 -1
- package/dist/runtime/command/registry/history.js +5 -36
- package/dist/runtime/command/registry/history.js.map +1 -1
- package/dist/runtime.js +662 -1287
- package/dist/runtime.js.map +4 -4
- package/dist/session/history/session-event-history-projection.d.ts.map +1 -1
- package/dist/session/history/session-event-history-projection.js +23 -2
- package/dist/session/history/session-event-history-projection.js.map +1 -1
- package/package.json +2 -2
- package/dist/acp/native-history/codex/codex-history.d.ts +0 -17
- package/dist/acp/native-history/codex/codex-history.d.ts.map +0 -1
- package/dist/acp/native-history/codex/codex-history.js +0 -661
- package/dist/acp/native-history/codex/codex-history.js.map +0 -1
- package/dist/acp/native-history/codex/native-history-events.d.ts +0 -8
- package/dist/acp/native-history/codex/native-history-events.d.ts.map +0 -1
- package/dist/acp/native-history/codex/native-history-events.js +0 -92
- package/dist/acp/native-history/codex/native-history-events.js.map +0 -1
|
@@ -1,661 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { SESSION_HISTORY_CHARACTER_LIMIT, SESSION_HISTORY_MESSAGE_LIMIT } from "@moxt-ai/mobius-sdk/protocol";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { executableLaunch, platformSpawnOptions, revalidateExecutable, stopProcessTree, } from "../../../util/process/executable.js";
|
|
5
|
-
import { AcpExecutionError } from "../../acp-error.js";
|
|
6
|
-
import { nativeSessionIdSchema } from "../../protocol/native-session.js";
|
|
7
|
-
import { embedLocalHistoryImages, SESSION_HISTORY_PREVIEW_TURN_LIMIT, } from "./history-images.js";
|
|
8
|
-
const responseSchema = z.union([
|
|
9
|
-
z.object({
|
|
10
|
-
id: z.number(),
|
|
11
|
-
error: z.object({ code: z.number(), message: z.string() }),
|
|
12
|
-
}),
|
|
13
|
-
z.object({ id: z.number(), result: z.unknown() }),
|
|
14
|
-
z.object({ method: z.string() }),
|
|
15
|
-
]);
|
|
16
|
-
const threadSchema = z.object({
|
|
17
|
-
thread: z.object({
|
|
18
|
-
id: nativeSessionIdSchema,
|
|
19
|
-
cwd: z
|
|
20
|
-
.string()
|
|
21
|
-
.min(1)
|
|
22
|
-
.max(2_048)
|
|
23
|
-
.refine((path) => !path.includes("\0")),
|
|
24
|
-
}),
|
|
25
|
-
});
|
|
26
|
-
const turnsSchema = z.object({
|
|
27
|
-
data: z
|
|
28
|
-
.array(z.object({
|
|
29
|
-
id: z.string().min(1),
|
|
30
|
-
status: z.enum(["completed", "interrupted", "failed", "inProgress"]),
|
|
31
|
-
error: z.unknown().nullish(),
|
|
32
|
-
items: z.array(z.unknown()).max(100_000),
|
|
33
|
-
}))
|
|
34
|
-
.max(SESSION_HISTORY_MESSAGE_LIMIT),
|
|
35
|
-
nextCursor: z.string().nullish(),
|
|
36
|
-
});
|
|
37
|
-
const itemTypeSchema = z.object({ type: z.string().min(1).max(80) });
|
|
38
|
-
const userMessageSchema = z.object({
|
|
39
|
-
content: z.array(z.unknown()).max(10_000),
|
|
40
|
-
type: z.literal("userMessage"),
|
|
41
|
-
});
|
|
42
|
-
const textSchema = z.object({ text: z.string() });
|
|
43
|
-
const imageSchema = z.object({ url: z.string() });
|
|
44
|
-
const agentMessageSchema = textSchema.extend({
|
|
45
|
-
id: z.string().optional(),
|
|
46
|
-
phase: z.string().nullish(),
|
|
47
|
-
type: z.literal("agentMessage"),
|
|
48
|
-
});
|
|
49
|
-
const reasoningSchema = z.object({
|
|
50
|
-
id: z.string(),
|
|
51
|
-
summary: z.array(z.string()).max(10_000),
|
|
52
|
-
content: z.array(z.string()).max(10_000),
|
|
53
|
-
type: z.literal("reasoning"),
|
|
54
|
-
});
|
|
55
|
-
const commandExecutionSchema = z.object({
|
|
56
|
-
id: z.string(),
|
|
57
|
-
command: z.string(),
|
|
58
|
-
cwd: z.string(),
|
|
59
|
-
status: z.string(),
|
|
60
|
-
commandActions: z.array(z.object({ type: z.string() }).passthrough()).max(100),
|
|
61
|
-
aggregatedOutput: z.string().nullish(),
|
|
62
|
-
exitCode: z.number().int().nullish(),
|
|
63
|
-
type: z.literal("commandExecution"),
|
|
64
|
-
});
|
|
65
|
-
const fileChangeSchema = z.object({
|
|
66
|
-
changes: z.array(z.object({ diff: z.string(), path: z.string() })).max(10_000),
|
|
67
|
-
id: z.string(),
|
|
68
|
-
status: z.string(),
|
|
69
|
-
type: z.literal("fileChange"),
|
|
70
|
-
});
|
|
71
|
-
const mcpToolCallSchema = z.object({
|
|
72
|
-
arguments: z.unknown().optional(),
|
|
73
|
-
error: z.unknown().nullish(),
|
|
74
|
-
id: z.string(),
|
|
75
|
-
result: z.unknown().nullish(),
|
|
76
|
-
server: z.string(),
|
|
77
|
-
status: z.string(),
|
|
78
|
-
tool: z.string(),
|
|
79
|
-
type: z.literal("mcpToolCall"),
|
|
80
|
-
});
|
|
81
|
-
const dynamicToolCallSchema = z.object({
|
|
82
|
-
arguments: z.unknown().optional(),
|
|
83
|
-
contentItems: z.array(z.unknown()).max(10_000).nullish(),
|
|
84
|
-
id: z.string(),
|
|
85
|
-
namespace: z.string().nullish(),
|
|
86
|
-
status: z.string(),
|
|
87
|
-
tool: z.string(),
|
|
88
|
-
type: z.literal("dynamicToolCall"),
|
|
89
|
-
});
|
|
90
|
-
const planSchema = z.object({ id: z.string(), text: z.string(), type: z.literal("plan") });
|
|
91
|
-
const historyItemSchema = z.discriminatedUnion("type", [
|
|
92
|
-
userMessageSchema,
|
|
93
|
-
agentMessageSchema,
|
|
94
|
-
reasoningSchema,
|
|
95
|
-
commandExecutionSchema,
|
|
96
|
-
fileChangeSchema,
|
|
97
|
-
mcpToolCallSchema,
|
|
98
|
-
dynamicToolCallSchema,
|
|
99
|
-
planSchema,
|
|
100
|
-
]);
|
|
101
|
-
class CodexHistoryResponseError extends Error {
|
|
102
|
-
code;
|
|
103
|
-
constructor(response) {
|
|
104
|
-
super(response.message);
|
|
105
|
-
this.code = response.code;
|
|
106
|
-
this.name = "CodexHistoryResponseError";
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
function bounded(value, maximum) {
|
|
110
|
-
return value.slice(0, maximum);
|
|
111
|
-
}
|
|
112
|
-
function activity(activityId, activityType, sequence, fields) {
|
|
113
|
-
return {
|
|
114
|
-
activityId: bounded(activityId, 128) || `history-${sequence.toString()}`,
|
|
115
|
-
activityType,
|
|
116
|
-
content: [],
|
|
117
|
-
input: bounded(fields.input, 100_000),
|
|
118
|
-
locations: fields.locations
|
|
119
|
-
.filter((location) => location.length > 0)
|
|
120
|
-
.map((location) => bounded(location, 2_048))
|
|
121
|
-
.slice(0, 100),
|
|
122
|
-
name: bounded(fields.name, 128),
|
|
123
|
-
output: bounded(fields.output, 100_000),
|
|
124
|
-
sequence,
|
|
125
|
-
status: bounded(fields.status, 64),
|
|
126
|
-
title: bounded(fields.title, 500),
|
|
127
|
-
toolKind: bounded(fields.toolKind, 64),
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
function errorMessage(value) {
|
|
131
|
-
if (typeof value === "string") {
|
|
132
|
-
return value;
|
|
133
|
-
}
|
|
134
|
-
if (typeof value === "object" && value !== null) {
|
|
135
|
-
const message = Reflect.get(value, "message");
|
|
136
|
-
if (typeof message === "string") {
|
|
137
|
-
return message;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return JSON.stringify(value) ?? "Codex stopped before completing this turn.";
|
|
141
|
-
}
|
|
142
|
-
function formatted(value) {
|
|
143
|
-
if (value === undefined) {
|
|
144
|
-
return "";
|
|
145
|
-
}
|
|
146
|
-
if (typeof value === "string") {
|
|
147
|
-
return value;
|
|
148
|
-
}
|
|
149
|
-
return JSON.stringify(value) ?? "";
|
|
150
|
-
}
|
|
151
|
-
function toolStatus(status) {
|
|
152
|
-
return status === "inProgress" ? "running" : status === "completed" ? "completed" : "failed";
|
|
153
|
-
}
|
|
154
|
-
function diffContent(path, newText) {
|
|
155
|
-
return { newText, path, type: "diff" };
|
|
156
|
-
}
|
|
157
|
-
function messageSize(message) {
|
|
158
|
-
return (message.text.length +
|
|
159
|
-
(message.role === "assistant"
|
|
160
|
-
? JSON.stringify(message.activities).length + JSON.stringify(message.content).length
|
|
161
|
-
: 0));
|
|
162
|
-
}
|
|
163
|
-
const SESSION_HISTORY_MESSAGE_CHARACTER_LIMIT = Math.floor(SESSION_HISTORY_CHARACTER_LIMIT / 2);
|
|
164
|
-
function fitActivity(activity, maximumCharacters) {
|
|
165
|
-
const truncate = (limit) => ({
|
|
166
|
-
...activity,
|
|
167
|
-
input: bounded(activity.input, limit),
|
|
168
|
-
locations: activity.locations.map((location) => bounded(location, Math.max(1, limit))),
|
|
169
|
-
name: bounded(activity.name, limit),
|
|
170
|
-
output: bounded(activity.output, limit),
|
|
171
|
-
title: bounded(activity.title, limit),
|
|
172
|
-
toolKind: bounded(activity.toolKind, limit),
|
|
173
|
-
});
|
|
174
|
-
let minimum = 1;
|
|
175
|
-
let maximum = 100_000;
|
|
176
|
-
let fitted = truncate(0);
|
|
177
|
-
while (minimum <= maximum) {
|
|
178
|
-
const limit = Math.floor((minimum + maximum) / 2);
|
|
179
|
-
const candidate = truncate(limit);
|
|
180
|
-
if (JSON.stringify(candidate).length <= maximumCharacters) {
|
|
181
|
-
fitted = candidate;
|
|
182
|
-
minimum = limit + 1;
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
maximum = limit - 1;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
return fitted;
|
|
189
|
-
}
|
|
190
|
-
function fitActivities(activities, maximumCharacters) {
|
|
191
|
-
if (activities.length === 0) {
|
|
192
|
-
return activities;
|
|
193
|
-
}
|
|
194
|
-
const arrayCharacters = 2 + activities.length - 1;
|
|
195
|
-
const maximumActivityCharacters = Math.floor(Math.max(0, maximumCharacters - arrayCharacters) / activities.length);
|
|
196
|
-
return activities.map((activity) => fitActivity(activity, maximumActivityCharacters));
|
|
197
|
-
}
|
|
198
|
-
function fitHistoryMessage(message, maximumCharacters = SESSION_HISTORY_MESSAGE_CHARACTER_LIMIT) {
|
|
199
|
-
const text = message.text.slice(-maximumCharacters);
|
|
200
|
-
if (message.role === "user") {
|
|
201
|
-
return {
|
|
202
|
-
message: { ...message, text },
|
|
203
|
-
truncated: text.length < message.text.length,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
let activities = message.activities;
|
|
207
|
-
const content = message.content;
|
|
208
|
-
let fitted = { ...message, activities, content, text };
|
|
209
|
-
if (messageSize(fitted) > maximumCharacters && activities.length > 0) {
|
|
210
|
-
const maximumActivityCharacters = maximumCharacters - text.length - JSON.stringify(content).length;
|
|
211
|
-
activities = fitActivities(activities, maximumActivityCharacters);
|
|
212
|
-
fitted = { ...fitted, activities };
|
|
213
|
-
}
|
|
214
|
-
const excess = Math.max(0, messageSize(fitted) - maximumCharacters);
|
|
215
|
-
if (excess > 0) {
|
|
216
|
-
const maximumTextLength = Math.max(0, fitted.text.length - excess);
|
|
217
|
-
fitted = {
|
|
218
|
-
...fitted,
|
|
219
|
-
text: maximumTextLength === 0 ? "" : fitted.text.slice(-maximumTextLength),
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
return {
|
|
223
|
-
message: fitted,
|
|
224
|
-
truncated: fitted.text.length < message.text.length ||
|
|
225
|
-
JSON.stringify(activities).length < JSON.stringify(message.activities).length,
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
function turnOutcome(status) {
|
|
229
|
-
switch (status) {
|
|
230
|
-
case "completed":
|
|
231
|
-
return "succeeded";
|
|
232
|
-
case "interrupted":
|
|
233
|
-
return "cancelled";
|
|
234
|
-
case "failed":
|
|
235
|
-
return "failed";
|
|
236
|
-
case "inProgress":
|
|
237
|
-
return undefined;
|
|
238
|
-
default: {
|
|
239
|
-
const unhandled = status;
|
|
240
|
-
throw new Error(`Unknown Codex turn status: ${String(unhandled)}`);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
function decodeHistory(thread, value, options) {
|
|
245
|
-
const page = turnsSchema.parse(value);
|
|
246
|
-
const nativeTurn = options.nativeTurn?.();
|
|
247
|
-
const coveredCommandIds = nativeTurn != null && page.data[0]?.id === nativeTurn.nativeTurnId ? [nativeTurn.commandId] : [];
|
|
248
|
-
const messages = [];
|
|
249
|
-
let characters = 0;
|
|
250
|
-
let truncated = page.nextCursor != null;
|
|
251
|
-
for (const turn of page.data.toReversed()) {
|
|
252
|
-
const outcome = turnOutcome(turn.status);
|
|
253
|
-
const turnMessageCharacterLimit = Math.max(1, Math.floor(SESSION_HISTORY_CHARACTER_LIMIT / Math.max(1, turn.items.length + (turn.error == null ? 0 : 1))));
|
|
254
|
-
let sequence = 0;
|
|
255
|
-
let assistantMessageIndex = null;
|
|
256
|
-
const append = (message) => {
|
|
257
|
-
const fitted = fitHistoryMessage(message, turnMessageCharacterLimit);
|
|
258
|
-
if (fitted.message.text.length === 0 &&
|
|
259
|
-
(fitted.message.images?.length ?? 0) === 0 &&
|
|
260
|
-
(fitted.message.role === "user" ||
|
|
261
|
-
(fitted.message.activities.length === 0 && fitted.message.content.length === 0))) {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
truncated ||= fitted.truncated;
|
|
265
|
-
messages.push(fitted.message);
|
|
266
|
-
if (fitted.message.role === "assistant") {
|
|
267
|
-
assistantMessageIndex = messages.length - 1;
|
|
268
|
-
}
|
|
269
|
-
characters += messageSize(fitted.message);
|
|
270
|
-
};
|
|
271
|
-
for (const item of turn.items) {
|
|
272
|
-
const decoded = historyItemSchema.safeParse(item);
|
|
273
|
-
if (!decoded.success) {
|
|
274
|
-
continue;
|
|
275
|
-
}
|
|
276
|
-
const historyItem = decoded.data;
|
|
277
|
-
if (historyItem.type === "userMessage") {
|
|
278
|
-
const text = historyItem.content
|
|
279
|
-
.map((block) => {
|
|
280
|
-
const { type } = itemTypeSchema.parse(block);
|
|
281
|
-
if (type === "text") {
|
|
282
|
-
return textSchema.parse(block).text;
|
|
283
|
-
}
|
|
284
|
-
if (type === "image") {
|
|
285
|
-
return `[@image](${imageSchema.parse(block).url})`;
|
|
286
|
-
}
|
|
287
|
-
return "[Attachment]";
|
|
288
|
-
})
|
|
289
|
-
.join("");
|
|
290
|
-
append({
|
|
291
|
-
role: "user",
|
|
292
|
-
text,
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
else if (historyItem.type === "agentMessage") {
|
|
296
|
-
append({ activities: [], content: [], role: "assistant", text: historyItem.text });
|
|
297
|
-
}
|
|
298
|
-
else if (historyItem.type === "reasoning") {
|
|
299
|
-
const output = (historyItem.content.length > 0 ? historyItem.content : historyItem.summary).join("\n\n");
|
|
300
|
-
if (output.length > 0) {
|
|
301
|
-
append({
|
|
302
|
-
activities: [
|
|
303
|
-
activity(historyItem.id, "reasoning", sequence, {
|
|
304
|
-
input: "",
|
|
305
|
-
locations: [],
|
|
306
|
-
name: "",
|
|
307
|
-
output,
|
|
308
|
-
status: "completed",
|
|
309
|
-
title: "Reasoning",
|
|
310
|
-
toolKind: "",
|
|
311
|
-
}),
|
|
312
|
-
],
|
|
313
|
-
content: [],
|
|
314
|
-
role: "assistant",
|
|
315
|
-
text: "",
|
|
316
|
-
});
|
|
317
|
-
sequence += 1;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
else if (historyItem.type === "commandExecution") {
|
|
321
|
-
const action = historyItem.commandActions[0]?.type ?? "execute";
|
|
322
|
-
append({
|
|
323
|
-
activities: [
|
|
324
|
-
activity(historyItem.id, "tool", sequence, {
|
|
325
|
-
input: JSON.stringify({ command: historyItem.command, cwd: historyItem.cwd }),
|
|
326
|
-
locations: [historyItem.cwd],
|
|
327
|
-
name: "",
|
|
328
|
-
output: JSON.stringify({
|
|
329
|
-
formatted_output: historyItem.aggregatedOutput ?? "",
|
|
330
|
-
...(historyItem.exitCode == null ? {} : { exit_code: historyItem.exitCode }),
|
|
331
|
-
}),
|
|
332
|
-
status: toolStatus(historyItem.status),
|
|
333
|
-
title: historyItem.command,
|
|
334
|
-
toolKind: action,
|
|
335
|
-
}),
|
|
336
|
-
],
|
|
337
|
-
content: [],
|
|
338
|
-
role: "assistant",
|
|
339
|
-
text: "",
|
|
340
|
-
});
|
|
341
|
-
sequence += 1;
|
|
342
|
-
}
|
|
343
|
-
else if (historyItem.type === "fileChange") {
|
|
344
|
-
const paths = historyItem.changes.map((change) => change.path);
|
|
345
|
-
const content = historyItem.changes.map((change) => diffContent(change.path, change.diff));
|
|
346
|
-
append({
|
|
347
|
-
activities: [
|
|
348
|
-
activity(historyItem.id, "tool", sequence, {
|
|
349
|
-
input: JSON.stringify({ changes: paths }),
|
|
350
|
-
locations: paths,
|
|
351
|
-
name: "",
|
|
352
|
-
output: "",
|
|
353
|
-
status: toolStatus(historyItem.status),
|
|
354
|
-
title: paths.join(", "),
|
|
355
|
-
toolKind: "edit",
|
|
356
|
-
}),
|
|
357
|
-
].map((entry) => ({ ...entry, content })),
|
|
358
|
-
content: [],
|
|
359
|
-
role: "assistant",
|
|
360
|
-
text: "",
|
|
361
|
-
});
|
|
362
|
-
sequence += 1;
|
|
363
|
-
}
|
|
364
|
-
else if (historyItem.type === "mcpToolCall") {
|
|
365
|
-
append({
|
|
366
|
-
activities: [
|
|
367
|
-
activity(historyItem.id, "tool", sequence, {
|
|
368
|
-
input: formatted(historyItem.arguments),
|
|
369
|
-
locations: [],
|
|
370
|
-
name: historyItem.tool,
|
|
371
|
-
output: formatted(historyItem.error ?? historyItem.result),
|
|
372
|
-
status: toolStatus(historyItem.status),
|
|
373
|
-
title: `${historyItem.server}/${historyItem.tool}`,
|
|
374
|
-
toolKind: "other",
|
|
375
|
-
}),
|
|
376
|
-
],
|
|
377
|
-
content: [],
|
|
378
|
-
role: "assistant",
|
|
379
|
-
text: "",
|
|
380
|
-
});
|
|
381
|
-
sequence += 1;
|
|
382
|
-
}
|
|
383
|
-
else if (historyItem.type === "dynamicToolCall") {
|
|
384
|
-
append({
|
|
385
|
-
activities: [
|
|
386
|
-
activity(historyItem.id, "tool", sequence, {
|
|
387
|
-
input: formatted(historyItem.arguments),
|
|
388
|
-
locations: [],
|
|
389
|
-
name: historyItem.tool,
|
|
390
|
-
output: formatted(historyItem.contentItems),
|
|
391
|
-
status: toolStatus(historyItem.status),
|
|
392
|
-
title: historyItem.namespace == null
|
|
393
|
-
? historyItem.tool
|
|
394
|
-
: `${historyItem.namespace}/${historyItem.tool}`,
|
|
395
|
-
toolKind: "other",
|
|
396
|
-
}),
|
|
397
|
-
],
|
|
398
|
-
content: [],
|
|
399
|
-
role: "assistant",
|
|
400
|
-
text: "",
|
|
401
|
-
});
|
|
402
|
-
sequence += 1;
|
|
403
|
-
}
|
|
404
|
-
else if (historyItem.type === "plan") {
|
|
405
|
-
append({
|
|
406
|
-
activities: [
|
|
407
|
-
activity(historyItem.id, "plan", sequence, {
|
|
408
|
-
input: "",
|
|
409
|
-
locations: [],
|
|
410
|
-
name: "",
|
|
411
|
-
output: historyItem.text,
|
|
412
|
-
status: "completed",
|
|
413
|
-
title: "Plan",
|
|
414
|
-
toolKind: "",
|
|
415
|
-
}),
|
|
416
|
-
],
|
|
417
|
-
content: [],
|
|
418
|
-
role: "assistant",
|
|
419
|
-
text: "",
|
|
420
|
-
});
|
|
421
|
-
sequence += 1;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
if (turn.error != null) {
|
|
425
|
-
append({
|
|
426
|
-
activities: [
|
|
427
|
-
activity(`agent-error-${sequence.toString()}`, "tool", sequence, {
|
|
428
|
-
input: "",
|
|
429
|
-
locations: [],
|
|
430
|
-
name: "",
|
|
431
|
-
output: errorMessage(turn.error),
|
|
432
|
-
status: "failed",
|
|
433
|
-
title: "Agent error",
|
|
434
|
-
toolKind: "other",
|
|
435
|
-
}),
|
|
436
|
-
],
|
|
437
|
-
content: [],
|
|
438
|
-
role: "assistant",
|
|
439
|
-
text: "",
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
if (assistantMessageIndex !== null) {
|
|
443
|
-
const assistant = messages[assistantMessageIndex];
|
|
444
|
-
if (assistant?.role === "assistant") {
|
|
445
|
-
characters -= messageSize(assistant);
|
|
446
|
-
const updated = {
|
|
447
|
-
...assistant,
|
|
448
|
-
...(outcome === undefined ? {} : { outcome }),
|
|
449
|
-
};
|
|
450
|
-
const fitted = fitHistoryMessage(updated, turnMessageCharacterLimit);
|
|
451
|
-
truncated ||= fitted.truncated;
|
|
452
|
-
messages[assistantMessageIndex] = fitted.message;
|
|
453
|
-
characters += messageSize(fitted.message);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
else if (outcome !== undefined) {
|
|
457
|
-
const message = {
|
|
458
|
-
activities: [],
|
|
459
|
-
content: [],
|
|
460
|
-
role: "assistant",
|
|
461
|
-
...(outcome === undefined ? {} : { outcome }),
|
|
462
|
-
text: "",
|
|
463
|
-
};
|
|
464
|
-
const fitted = fitHistoryMessage(message, turnMessageCharacterLimit);
|
|
465
|
-
truncated ||= fitted.truncated;
|
|
466
|
-
messages.push(fitted.message);
|
|
467
|
-
characters += messageSize(fitted.message);
|
|
468
|
-
}
|
|
469
|
-
while (messages.length > SESSION_HISTORY_MESSAGE_LIMIT || characters > SESSION_HISTORY_CHARACTER_LIMIT) {
|
|
470
|
-
const removed = messages.shift();
|
|
471
|
-
if (removed !== undefined) {
|
|
472
|
-
characters -= messageSize(removed);
|
|
473
|
-
}
|
|
474
|
-
truncated = true;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
return {
|
|
478
|
-
nativeSessionId: thread.id,
|
|
479
|
-
cwd: thread.cwd,
|
|
480
|
-
history: {
|
|
481
|
-
status: "available",
|
|
482
|
-
messages,
|
|
483
|
-
truncated,
|
|
484
|
-
coveredCommandIds,
|
|
485
|
-
coveredMessageIds: [],
|
|
486
|
-
},
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
/** Metadata and paginated display history do not acquire the source writer. */
|
|
490
|
-
async function readCodexHistoryWithOptions(agent, cwd, nativeSessionId, signal, options) {
|
|
491
|
-
signal.throwIfAborted();
|
|
492
|
-
nativeSessionIdSchema.parse(nativeSessionId);
|
|
493
|
-
if (agent.id !== "codex" || !(await revalidateExecutable(agent.agentIdentity))) {
|
|
494
|
-
throw new AcpExecutionError("The local Codex executable changed. Restart Mobius to discover it again.", {}, "agent_executable_changed");
|
|
495
|
-
}
|
|
496
|
-
signal.throwIfAborted();
|
|
497
|
-
const launch = executableLaunch(agent.agentIdentity.realPath, ["app-server"]);
|
|
498
|
-
const child = spawn(launch.command, [...launch.arguments], {
|
|
499
|
-
cwd,
|
|
500
|
-
env: { ...process.env, ...agent.environment },
|
|
501
|
-
...platformSpawnOptions,
|
|
502
|
-
shell: false,
|
|
503
|
-
stdio: ["pipe", "pipe", "ignore"],
|
|
504
|
-
});
|
|
505
|
-
const response = Promise.withResolvers();
|
|
506
|
-
let state = { kind: "initializing", requestId: 1 };
|
|
507
|
-
const seenCursors = new Set();
|
|
508
|
-
let pendingBytes = 0;
|
|
509
|
-
let pendingFragments = [];
|
|
510
|
-
const fail = (cause) => response.reject(cause instanceof AcpExecutionError
|
|
511
|
-
? cause
|
|
512
|
-
: new AcpExecutionError("Saved Codex history could not be read.", { cause }, "native_history_unavailable"));
|
|
513
|
-
const send = (message) => {
|
|
514
|
-
child.stdin.write(`${JSON.stringify(message)}\n`);
|
|
515
|
-
};
|
|
516
|
-
const onData = (chunk) => {
|
|
517
|
-
try {
|
|
518
|
-
let cursor = 0;
|
|
519
|
-
let newline = chunk.indexOf("\n");
|
|
520
|
-
while (newline >= 0) {
|
|
521
|
-
const fragment = chunk.slice(cursor, newline);
|
|
522
|
-
const lineBytes = pendingBytes + Buffer.byteLength(fragment);
|
|
523
|
-
if (lineBytes > 32 * 1_024 * 1_024) {
|
|
524
|
-
throw new AcpExecutionError("This saved conversation is too large to preview.", {}, "native_history_too_large");
|
|
525
|
-
}
|
|
526
|
-
const line = pendingFragments.length === 0 ? fragment : [...pendingFragments, fragment].join("");
|
|
527
|
-
pendingBytes = 0;
|
|
528
|
-
pendingFragments = [];
|
|
529
|
-
cursor = newline + 1;
|
|
530
|
-
newline = chunk.indexOf("\n", cursor);
|
|
531
|
-
if (line.trim().length === 0) {
|
|
532
|
-
continue;
|
|
533
|
-
}
|
|
534
|
-
const value = JSON.parse(line);
|
|
535
|
-
const message = responseSchema.parse(value);
|
|
536
|
-
if (!("id" in message)) {
|
|
537
|
-
continue;
|
|
538
|
-
}
|
|
539
|
-
if (message.id !== state.requestId) {
|
|
540
|
-
throw new AcpExecutionError("Codex returned an invalid history response.", {}, "invalid_native_session_response");
|
|
541
|
-
}
|
|
542
|
-
if ("error" in message) {
|
|
543
|
-
fail(new AcpExecutionError("Saved Codex history could not be read.", { cause: new CodexHistoryResponseError(message.error) }, "native_history_unavailable"));
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
546
|
-
switch (state.kind) {
|
|
547
|
-
case "initializing":
|
|
548
|
-
state = { kind: "metadata", requestId: 2 };
|
|
549
|
-
send({ method: "initialized", params: {} });
|
|
550
|
-
send({
|
|
551
|
-
id: state.requestId,
|
|
552
|
-
method: "thread/read",
|
|
553
|
-
params: { threadId: nativeSessionId, includeTurns: false },
|
|
554
|
-
});
|
|
555
|
-
break;
|
|
556
|
-
case "metadata": {
|
|
557
|
-
const { thread } = threadSchema.parse(message.result);
|
|
558
|
-
state = { kind: "turns", requestId: 3, thread, pages: [] };
|
|
559
|
-
send({
|
|
560
|
-
id: state.requestId,
|
|
561
|
-
method: "thread/turns/list",
|
|
562
|
-
params: {
|
|
563
|
-
threadId: nativeSessionId,
|
|
564
|
-
limit: SESSION_HISTORY_PREVIEW_TURN_LIMIT,
|
|
565
|
-
sortDirection: "desc",
|
|
566
|
-
itemsView: "full",
|
|
567
|
-
},
|
|
568
|
-
});
|
|
569
|
-
break;
|
|
570
|
-
}
|
|
571
|
-
case "turns": {
|
|
572
|
-
const page = turnsSchema.parse(message.result);
|
|
573
|
-
const pages = [...state.pages, ...page.data];
|
|
574
|
-
const maxTurns = SESSION_HISTORY_MESSAGE_LIMIT;
|
|
575
|
-
if (page.nextCursor == null || pages.length >= maxTurns) {
|
|
576
|
-
response.resolve(decodeHistory(state.thread, {
|
|
577
|
-
data: pages.slice(0, maxTurns),
|
|
578
|
-
nextCursor: page.nextCursor,
|
|
579
|
-
}, options));
|
|
580
|
-
return;
|
|
581
|
-
}
|
|
582
|
-
if (page.data.length === 0 || seenCursors.has(page.nextCursor)) {
|
|
583
|
-
throw new AcpExecutionError("Codex repeated a history page cursor.", {}, "invalid_native_session_response");
|
|
584
|
-
}
|
|
585
|
-
seenCursors.add(page.nextCursor);
|
|
586
|
-
state = { kind: "turns", requestId: state.requestId + 1, thread: state.thread, pages };
|
|
587
|
-
send({
|
|
588
|
-
id: state.requestId,
|
|
589
|
-
method: "thread/turns/list",
|
|
590
|
-
params: {
|
|
591
|
-
threadId: nativeSessionId,
|
|
592
|
-
cursor: page.nextCursor,
|
|
593
|
-
limit: Math.min(SESSION_HISTORY_PREVIEW_TURN_LIMIT, maxTurns - pages.length),
|
|
594
|
-
sortDirection: "desc",
|
|
595
|
-
itemsView: "full",
|
|
596
|
-
},
|
|
597
|
-
});
|
|
598
|
-
break;
|
|
599
|
-
}
|
|
600
|
-
default: {
|
|
601
|
-
const unhandled = state;
|
|
602
|
-
throw new Error(`Unknown history read state: ${String(unhandled)}`);
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
const remainder = chunk.slice(cursor);
|
|
607
|
-
if (remainder.length > 0) {
|
|
608
|
-
pendingBytes += Buffer.byteLength(remainder);
|
|
609
|
-
if (pendingBytes > 32 * 1_024 * 1_024) {
|
|
610
|
-
throw new AcpExecutionError("This saved conversation is too large to preview.", {}, "native_history_too_large");
|
|
611
|
-
}
|
|
612
|
-
pendingFragments.push(remainder);
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
catch (cause) {
|
|
616
|
-
fail(cause);
|
|
617
|
-
}
|
|
618
|
-
};
|
|
619
|
-
const abort = () => response.reject(signal.reason);
|
|
620
|
-
const onExit = () => fail(new AcpExecutionError("Codex stopped before its saved history was available.", {}, "native_history_unavailable"));
|
|
621
|
-
const timeout = setTimeout(() => fail(new AcpExecutionError("Reading saved Codex history timed out.", {}, "native_history_timeout")), 60_000);
|
|
622
|
-
child.stdout.setEncoding("utf8");
|
|
623
|
-
child.stdout.on("data", onData);
|
|
624
|
-
child.stdin.on("error", fail);
|
|
625
|
-
child.on("error", fail);
|
|
626
|
-
child.on("exit", onExit);
|
|
627
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
628
|
-
try {
|
|
629
|
-
if (signal.aborted) {
|
|
630
|
-
abort();
|
|
631
|
-
}
|
|
632
|
-
else {
|
|
633
|
-
send({
|
|
634
|
-
id: 1,
|
|
635
|
-
method: "initialize",
|
|
636
|
-
params: { clientInfo: { name: "mobius_history", version: "1.0.0" } },
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
const snapshot = await response.promise;
|
|
640
|
-
return {
|
|
641
|
-
...snapshot,
|
|
642
|
-
history: await embedLocalHistoryImages(snapshot.history, signal),
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
finally {
|
|
646
|
-
clearTimeout(timeout);
|
|
647
|
-
signal.removeEventListener("abort", abort);
|
|
648
|
-
child.stdout.removeListener("data", onData);
|
|
649
|
-
if (child.pid !== undefined) {
|
|
650
|
-
await stopProcessTree(child);
|
|
651
|
-
}
|
|
652
|
-
child.stdin.removeListener("error", fail);
|
|
653
|
-
child.removeListener("error", fail);
|
|
654
|
-
child.removeListener("exit", onExit);
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
/** Reads the complete Codex turn items used to restore non-retained Chats. */
|
|
658
|
-
export const readCodexFullHistory = async (agent, cwd, nativeSessionId, signal, nativeTurn) => await readCodexHistoryWithOptions(agent, cwd, nativeSessionId, signal, {
|
|
659
|
-
...(nativeTurn === undefined ? {} : { nativeTurn }),
|
|
660
|
-
});
|
|
661
|
-
//# sourceMappingURL=codex-history.js.map
|