@foxden-app/foxclaw 0.6.7 → 0.6.8
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/CHANGELOG.md +10 -0
- package/dist/controller/session_observer.js +44 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable FoxClaw changes are listed here. Each release note is bilingual so GitHub Releases and the npm package are useful to both Chinese and English readers.
|
|
4
4
|
|
|
5
|
+
## 0.6.8 - 2026-08-15
|
|
6
|
+
|
|
7
|
+
### 中文
|
|
8
|
+
- 修复只读观察 Codex CLI 0.147.0 会话时只显示“正在思考...”和“已完成。”、遗漏实际回复的问题。会话日志观察器现在识别新的 `item_completed` / `AgentMessage` 结构,并沿用 Codex 消息 ID,避免同一正文与 `response_item` 镜像重复发送。
|
|
9
|
+
- 识别新日志中的 `turn_aborted` 和 `turn_interrupted`,让被中断的外部 CLI 轮次明确结束为“已中断”,不再残留为进行中状态。
|
|
10
|
+
|
|
11
|
+
### English
|
|
12
|
+
- Fixed read-only observation of Codex CLI 0.147.0 sessions showing only “Thinking...” and “Completed.” while dropping the actual response. The session-log observer now recognizes the new `item_completed` / `AgentMessage` shape and preserves Codex message IDs without duplicating the mirrored `response_item`.
|
|
13
|
+
- Recognizes `turn_aborted` and `turn_interrupted` in new session logs so interrupted external CLI turns finish explicitly instead of remaining active.
|
|
14
|
+
|
|
5
15
|
## 0.6.7 - 2026-08-12
|
|
6
16
|
|
|
7
17
|
### 中文
|
|
@@ -104,6 +104,16 @@ function applySessionRecord(record, cursor) {
|
|
|
104
104
|
if (type === 'event_msg' && payload?.type === 'agent_message' && typeof payload.message === 'string') {
|
|
105
105
|
return createSessionTextEvents(activeTurnId, cursor, payload.message, typeof payload.phase === 'string' ? payload.phase : null, false);
|
|
106
106
|
}
|
|
107
|
+
if (type === 'event_msg'
|
|
108
|
+
&& payload?.type === 'item_completed'
|
|
109
|
+
&& payload?.turn_id === activeTurnId
|
|
110
|
+
&& normalizeSessionItemType(payload?.item) === 'agentmessage') {
|
|
111
|
+
const text = extractSessionItemText(payload.item);
|
|
112
|
+
if (text === null) {
|
|
113
|
+
return { cursor, events: [], startedTurnId: null, turnCompleted: false };
|
|
114
|
+
}
|
|
115
|
+
return createSessionTextEvents(activeTurnId, cursor, text, typeof payload.item.phase === 'string' ? payload.item.phase : null, false, false, typeof payload.item.id === 'string' ? payload.item.id : null);
|
|
116
|
+
}
|
|
107
117
|
if (type === 'response_item' && payload?.type === 'plan' && typeof payload.text === 'string') {
|
|
108
118
|
return createSessionTextEvents(activeTurnId, cursor, payload.text, 'commentary', true, true);
|
|
109
119
|
}
|
|
@@ -169,6 +179,20 @@ function applySessionRecord(record, cursor) {
|
|
|
169
179
|
turnCompleted: true,
|
|
170
180
|
};
|
|
171
181
|
}
|
|
182
|
+
if (type === 'event_msg'
|
|
183
|
+
&& (payload?.type === 'turn_aborted' || payload?.type === 'turn_interrupted')
|
|
184
|
+
&& payload?.turn_id === activeTurnId) {
|
|
185
|
+
return {
|
|
186
|
+
cursor: { activeTurnId: null, nextMessageIndex: 0 },
|
|
187
|
+
events: [{
|
|
188
|
+
kind: 'turn_completed',
|
|
189
|
+
turnId: activeTurnId,
|
|
190
|
+
state: 'interrupted',
|
|
191
|
+
}],
|
|
192
|
+
startedTurnId: null,
|
|
193
|
+
turnCompleted: true,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
172
196
|
return {
|
|
173
197
|
cursor,
|
|
174
198
|
events: [],
|
|
@@ -179,8 +203,8 @@ function applySessionRecord(record, cursor) {
|
|
|
179
203
|
function buildSessionItemId(turnId, index) {
|
|
180
204
|
return `${turnId}:session:${index}`;
|
|
181
205
|
}
|
|
182
|
-
function createSessionTextEvents(activeTurnId, cursor, text, phase, forceCommentary, isPlan = false) {
|
|
183
|
-
const itemId = buildSessionItemId(activeTurnId, cursor.nextMessageIndex + 1);
|
|
206
|
+
function createSessionTextEvents(activeTurnId, cursor, text, phase, forceCommentary, isPlan = false, sourceItemId = null) {
|
|
207
|
+
const itemId = sourceItemId ?? buildSessionItemId(activeTurnId, cursor.nextMessageIndex + 1);
|
|
184
208
|
const outputKind = forceCommentary ? 'commentary' : classifyAgentOutput(phase, true);
|
|
185
209
|
const streamOutputKind = forceCommentary ? 'commentary' : classifyAgentOutput(phase, false);
|
|
186
210
|
return {
|
|
@@ -219,6 +243,24 @@ function createSessionTextEvents(activeTurnId, cursor, text, phase, forceComment
|
|
|
219
243
|
turnCompleted: false,
|
|
220
244
|
};
|
|
221
245
|
}
|
|
246
|
+
function normalizeSessionItemType(item) {
|
|
247
|
+
if (typeof item?.type !== 'string') {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
return item.type.replace(/[^a-z]/gi, '').toLowerCase();
|
|
251
|
+
}
|
|
252
|
+
function extractSessionItemText(item) {
|
|
253
|
+
if (typeof item?.text === 'string') {
|
|
254
|
+
return item.text;
|
|
255
|
+
}
|
|
256
|
+
if (!Array.isArray(item?.content)) {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
const parts = item.content
|
|
260
|
+
.map((entry) => typeof entry?.text === 'string' ? entry.text : '')
|
|
261
|
+
.filter(Boolean);
|
|
262
|
+
return parts.length > 0 ? parts.join('') : null;
|
|
263
|
+
}
|
|
222
264
|
function createExecStartEvent(turnId, payload) {
|
|
223
265
|
const callId = typeof payload.call_id === 'string' ? payload.call_id : null;
|
|
224
266
|
if (!callId) {
|