shennian 0.2.70 → 0.2.72
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.
|
@@ -60,11 +60,12 @@ function parseCodexTextElements(value) {
|
|
|
60
60
|
function stripCodexUserMessageWrapper(text) {
|
|
61
61
|
const normalized = normalizeText(text);
|
|
62
62
|
if (!normalized)
|
|
63
|
-
return '';
|
|
63
|
+
return { text: '', strippedWrapper: false };
|
|
64
64
|
const requestMatch = normalized.match(/My request for Codex:\s*([\s\S]*)$/i);
|
|
65
|
-
if (requestMatch && requestMatch[1])
|
|
66
|
-
return stripCodexImagePlaceholders(requestMatch[1]);
|
|
67
|
-
|
|
65
|
+
if (requestMatch && requestMatch[1]) {
|
|
66
|
+
return { text: stripCodexImagePlaceholders(requestMatch[1]), strippedWrapper: true };
|
|
67
|
+
}
|
|
68
|
+
return { text: normalized, strippedWrapper: false };
|
|
68
69
|
}
|
|
69
70
|
function stripCodexImagePlaceholders(text) {
|
|
70
71
|
return normalizeText(text.replace(/<image>\s*<\/image>/gi, ''));
|
|
@@ -74,10 +75,12 @@ function parseCodexUserMessage(payload) {
|
|
|
74
75
|
const textFromInput = Array.isArray(payload.input)
|
|
75
76
|
? parseCodexTextElements(payload.input)
|
|
76
77
|
: '';
|
|
77
|
-
const
|
|
78
|
+
const strippedMessage = typeof payload.message === 'string'
|
|
78
79
|
? stripCodexUserMessageWrapper(payload.message)
|
|
79
|
-
: '';
|
|
80
|
+
: { text: '', strippedWrapper: false };
|
|
81
|
+
const textFromMessage = strippedMessage.text;
|
|
80
82
|
const text = textFromElements || textFromInput || textFromMessage;
|
|
83
|
+
const repairHint = strippedMessage.strippedWrapper ? 'codex_app_context_wrapper' : undefined;
|
|
81
84
|
const attachments = Array.isArray(payload.local_images)
|
|
82
85
|
? payload.local_images
|
|
83
86
|
.map(parseCodexLocalImageAttachment)
|
|
@@ -91,9 +94,10 @@ function parseCodexUserMessage(payload) {
|
|
|
91
94
|
return {
|
|
92
95
|
payload: buildUserMessagePayload(text, attachments),
|
|
93
96
|
titleText: text,
|
|
97
|
+
repairHint,
|
|
94
98
|
};
|
|
95
99
|
}
|
|
96
|
-
return { payload: text, titleText: text };
|
|
100
|
+
return { payload: text, titleText: text, repairHint };
|
|
97
101
|
}
|
|
98
102
|
function isCodexTerminalEventType(eventType) {
|
|
99
103
|
return eventType === 'turn_completed' || eventType === 'turn_complete' || eventType === 'task_complete';
|
|
@@ -174,7 +178,8 @@ function parseCodexResponseMessage(payload) {
|
|
|
174
178
|
return null;
|
|
175
179
|
return text ? { role, payload: text, titleText: text } : null;
|
|
176
180
|
}
|
|
177
|
-
const
|
|
181
|
+
const stripped = stripCodexUserMessageWrapper(codexMessageContentText(payload.content, 'input_text'));
|
|
182
|
+
const text = stripped.text;
|
|
178
183
|
const attachments = codexMessageInputImageAttachments(payload.content);
|
|
179
184
|
if (!text && attachments.length === 0)
|
|
180
185
|
return null;
|
|
@@ -184,6 +189,7 @@ function parseCodexResponseMessage(payload) {
|
|
|
184
189
|
role,
|
|
185
190
|
payload: attachments.length > 0 ? buildUserMessagePayload(text, attachments) : text,
|
|
186
191
|
titleText: text,
|
|
192
|
+
repairHint: stripped.strippedWrapper ? 'codex_app_context_wrapper' : undefined,
|
|
187
193
|
};
|
|
188
194
|
}
|
|
189
195
|
function codexDedupeText(payload) {
|
|
@@ -225,7 +231,7 @@ function findDuplicateCodexChatEventIndex(events, role, payload, ts) {
|
|
|
225
231
|
}
|
|
226
232
|
return -1;
|
|
227
233
|
}
|
|
228
|
-
function pushCodexEvent(events, filePath, lineOffset, kind, sourceSessionKey, ts, payload, title, modelId, workDir, role = 'agent', terminal = false) {
|
|
234
|
+
function pushCodexEvent(events, filePath, lineOffset, kind, sourceSessionKey, ts, payload, title, modelId, workDir, role = 'agent', terminal = false, repairHint) {
|
|
229
235
|
if (!payload)
|
|
230
236
|
return;
|
|
231
237
|
events.push({
|
|
@@ -241,6 +247,7 @@ function pushCodexEvent(events, filePath, lineOffset, kind, sourceSessionKey, ts
|
|
|
241
247
|
modelId,
|
|
242
248
|
workDir,
|
|
243
249
|
terminal,
|
|
250
|
+
repairHint,
|
|
244
251
|
});
|
|
245
252
|
}
|
|
246
253
|
function pushCodexToolEvent(events, filePath, lineOffset, kind, sourceSessionKey, ts, toolName, title, modelId, workDir, args, result) {
|
|
@@ -257,7 +264,7 @@ function parseCodexResponseItem(events, filePath, lineOffset, payload, sourceSes
|
|
|
257
264
|
const duplicateIndex = findDuplicateCodexChatEventIndex(events, parsedMessage.role, parsedMessage.payload, ts);
|
|
258
265
|
if (duplicateIndex >= 0)
|
|
259
266
|
return;
|
|
260
|
-
pushCodexEvent(events, filePath, lineOffset, `${itemType}:${parsedMessage.role}`, sourceSessionKey, ts, parsedMessage.payload, title, modelId, workDir, parsedMessage.role);
|
|
267
|
+
pushCodexEvent(events, filePath, lineOffset, `${itemType}:${parsedMessage.role}`, sourceSessionKey, ts, parsedMessage.payload, title, modelId, workDir, parsedMessage.role, false, parsedMessage.repairHint);
|
|
261
268
|
return;
|
|
262
269
|
}
|
|
263
270
|
if (itemType === 'function_call') {
|
|
@@ -297,7 +304,7 @@ function parseCodexEventMessage(events, filePath, lineOffset, payload, sourceSes
|
|
|
297
304
|
const parsedUser = parseCodexUserMessage(payload);
|
|
298
305
|
if (!parsedUser)
|
|
299
306
|
return;
|
|
300
|
-
pushCodexEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, parsedUser.payload, title, modelId, workDir, 'user');
|
|
307
|
+
pushCodexEvent(events, filePath, lineOffset, eventType, sourceSessionKey, ts, parsedUser.payload, title, modelId, workDir, 'user', false, parsedUser.repairHint);
|
|
301
308
|
return;
|
|
302
309
|
}
|
|
303
310
|
if (eventType === 'agent_message') {
|
|
@@ -17,6 +17,9 @@ function isCodexRolloutPath(filePath) {
|
|
|
17
17
|
function shouldBackfillWindowsCodex(state) {
|
|
18
18
|
return process.platform === 'win32' && state.codexWindowsPathParserFixed !== true;
|
|
19
19
|
}
|
|
20
|
+
function shouldBackfillCodexAppContextWrapper(state) {
|
|
21
|
+
return state.codexAppContextWrapperFixed !== true;
|
|
22
|
+
}
|
|
20
23
|
export class NativeSessionFusionService {
|
|
21
24
|
client;
|
|
22
25
|
timer = null;
|
|
@@ -97,6 +100,7 @@ export class NativeSessionFusionService {
|
|
|
97
100
|
const state = loadNativeScannerState();
|
|
98
101
|
const nextFilesState = { ...state.files };
|
|
99
102
|
const backfillWindowsCodex = shouldBackfillWindowsCodex(state);
|
|
103
|
+
const backfillCodexAppContextWrapper = shouldBackfillCodexAppContextWrapper(state);
|
|
100
104
|
const batches = [];
|
|
101
105
|
const files = [...listCodexRolloutFiles(), ...listClaudeTranscriptFiles(), ...listOpenCodeSessionFiles()];
|
|
102
106
|
for (const filePath of files) {
|
|
@@ -104,9 +108,10 @@ export class NativeSessionFusionService {
|
|
|
104
108
|
const current = state.files[filePath];
|
|
105
109
|
const isCodex = isCodexRolloutPath(filePath);
|
|
106
110
|
const isOpenCode = filePath.endsWith('.opencode-session.json');
|
|
111
|
+
const isCodexRepairBackfillFile = backfillCodexAppContextWrapper && isCodex && current != null;
|
|
107
112
|
const startOffset = isOpenCode && current?.mtimeMs !== stat.mtimeMs
|
|
108
113
|
? 0
|
|
109
|
-
: backfillWindowsCodex && isCodex
|
|
114
|
+
: (backfillWindowsCodex || isCodexRepairBackfillFile) && isCodex
|
|
110
115
|
? 0
|
|
111
116
|
: current?.offset ?? 0;
|
|
112
117
|
const parsed = isOpenCode
|
|
@@ -118,7 +123,10 @@ export class NativeSessionFusionService {
|
|
|
118
123
|
offset: parsed.nextOffset,
|
|
119
124
|
mtimeMs: stat.mtimeMs,
|
|
120
125
|
};
|
|
121
|
-
|
|
126
|
+
const parsedEvents = isCodexRepairBackfillFile && startOffset === 0
|
|
127
|
+
? parsed.events.filter((event) => event.repairHint === 'codex_app_context_wrapper')
|
|
128
|
+
: parsed.events;
|
|
129
|
+
for (const event of parsedEvents) {
|
|
122
130
|
const claimed = this.tryClaimManagedEcho(event);
|
|
123
131
|
if (claimed) {
|
|
124
132
|
batches.push(claimed);
|
|
@@ -133,17 +141,21 @@ export class NativeSessionFusionService {
|
|
|
133
141
|
const chunk = batches.slice(index, index + MAX_BATCH_SIZE);
|
|
134
142
|
if (chunk.length === 0)
|
|
135
143
|
continue;
|
|
144
|
+
const wireEvents = chunk.map(({ repairHint: _repairHint, ...event }) => event);
|
|
136
145
|
await this.client.sendBufferedEvent({
|
|
137
146
|
type: 'event',
|
|
138
147
|
event: 'native.session.events',
|
|
139
148
|
id: `native-fusion-${randomUUID()}`,
|
|
140
|
-
payload: { events:
|
|
149
|
+
payload: { events: wireEvents },
|
|
141
150
|
}, 60_000);
|
|
142
151
|
}
|
|
143
152
|
state.files = nextFilesState;
|
|
144
153
|
if (backfillWindowsCodex) {
|
|
145
154
|
state.codexWindowsPathParserFixed = true;
|
|
146
155
|
}
|
|
156
|
+
if (backfillCodexAppContextWrapper) {
|
|
157
|
+
state.codexAppContextWrapperFixed = true;
|
|
158
|
+
}
|
|
147
159
|
saveNativeScannerState(state);
|
|
148
160
|
}
|
|
149
161
|
tryClaimManagedEcho(event) {
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import type { NativeSessionEventPayload } from '@shennian/wire';
|
|
2
|
+
export type NativeRepairHint = 'codex_app_context_wrapper';
|
|
2
3
|
export type NativeScannerState = {
|
|
3
4
|
files: Record<string, {
|
|
4
5
|
offset: number;
|
|
5
6
|
mtimeMs: number;
|
|
6
7
|
}>;
|
|
7
8
|
codexWindowsPathParserFixed?: boolean;
|
|
9
|
+
codexAppContextWrapperFixed?: boolean;
|
|
10
|
+
};
|
|
11
|
+
export type ParsedNativeEvent = NativeSessionEventPayload & {
|
|
12
|
+
repairHint?: NativeRepairHint;
|
|
8
13
|
};
|
|
9
|
-
export type ParsedNativeEvent = NativeSessionEventPayload;
|
|
10
14
|
export type PendingManagedClaim = {
|
|
11
15
|
sessionId: string;
|
|
12
16
|
agentType: string;
|