@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 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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxden-app/foxclaw",
3
- "version": "0.6.7",
3
+ "version": "0.6.8",
4
4
  "description": "Foxden local execution claw for controlling Codex and OpenCode from trusted chat interfaces.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",