shennian 0.2.71 → 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
- return normalized;
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 textFromMessage = typeof payload.message === 'string'
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 text = stripCodexUserMessageWrapper(codexMessageContentText(payload.content, 'input_text'));
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') {
@@ -108,9 +108,10 @@ export class NativeSessionFusionService {
108
108
  const current = state.files[filePath];
109
109
  const isCodex = isCodexRolloutPath(filePath);
110
110
  const isOpenCode = filePath.endsWith('.opencode-session.json');
111
+ const isCodexRepairBackfillFile = backfillCodexAppContextWrapper && isCodex && current != null;
111
112
  const startOffset = isOpenCode && current?.mtimeMs !== stat.mtimeMs
112
113
  ? 0
113
- : (backfillWindowsCodex || backfillCodexAppContextWrapper) && isCodex
114
+ : (backfillWindowsCodex || isCodexRepairBackfillFile) && isCodex
114
115
  ? 0
115
116
  : current?.offset ?? 0;
116
117
  const parsed = isOpenCode
@@ -122,7 +123,10 @@ export class NativeSessionFusionService {
122
123
  offset: parsed.nextOffset,
123
124
  mtimeMs: stat.mtimeMs,
124
125
  };
125
- for (const event of parsed.events) {
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) {
126
130
  const claimed = this.tryClaimManagedEcho(event);
127
131
  if (claimed) {
128
132
  batches.push(claimed);
@@ -137,11 +141,12 @@ export class NativeSessionFusionService {
137
141
  const chunk = batches.slice(index, index + MAX_BATCH_SIZE);
138
142
  if (chunk.length === 0)
139
143
  continue;
144
+ const wireEvents = chunk.map(({ repairHint: _repairHint, ...event }) => event);
140
145
  await this.client.sendBufferedEvent({
141
146
  type: 'event',
142
147
  event: 'native.session.events',
143
148
  id: `native-fusion-${randomUUID()}`,
144
- payload: { events: chunk },
149
+ payload: { events: wireEvents },
145
150
  }, 60_000);
146
151
  }
147
152
  state.files = nextFilesState;
@@ -1,4 +1,5 @@
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;
@@ -7,7 +8,9 @@ export type NativeScannerState = {
7
8
  codexWindowsPathParserFixed?: boolean;
8
9
  codexAppContextWrapperFixed?: boolean;
9
10
  };
10
- export type ParsedNativeEvent = NativeSessionEventPayload;
11
+ export type ParsedNativeEvent = NativeSessionEventPayload & {
12
+ repairHint?: NativeRepairHint;
13
+ };
11
14
  export type PendingManagedClaim = {
12
15
  sessionId: string;
13
16
  agentType: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.2.71",
3
+ "version": "0.2.72",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {