@foxden-app/foxclaw 0.5.15 → 0.5.16

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.5.16 - 2026-06-08
6
+
7
+ ### 中文
8
+ - FoxClaw 重启后会把仍在运行的桥接自有 Codex turn 恢复为可继续操作的活动态,状态卡继续刷新,后续 Telegram 输入会继续 steer 或按聊天设置排队,不再退化成需要用户重新发消息的只读观察态。
9
+ - `/watch` 产生的观察态 turn 会在状态卡中持久化只读标记,重启恢复后仍保持只读,避免把旁观线程误恢复成可操作任务。
10
+
11
+ ### English
12
+ - After a FoxClaw restart, bridge-owned live Codex turns are restored as actionable active turns: their status cards keep updating, and later Telegram messages keep steering or queueing according to the chat setting instead of degrading into read-only watch mode.
13
+ - `/watch`-created observed turns now persist their read-only marker, so restart recovery keeps watched threads read-only and does not accidentally promote them into actionable tasks.
14
+
5
15
  ## 0.5.15 - 2026-06-08
6
16
 
7
17
  ### 中文
@@ -2862,6 +2862,7 @@ export class BridgeSessionCore {
2862
2862
  scopeId,
2863
2863
  threadId,
2864
2864
  messageId: previewMessageId,
2865
+ isObserved: active.isObserved,
2865
2866
  });
2866
2867
  }
2867
2868
  this.updateStatus();
@@ -6946,7 +6947,7 @@ export class BridgeSessionCore {
6946
6947
  return this.interruptOrphanWaitingUserInput(preview);
6947
6948
  }
6948
6949
  await this.stopWatchingScopeThread(preview.scopeId, preview.threadId);
6949
- const active = this.createActiveTurnState(preview.scopeId, target.chatId, target.chatType, target.topicId, preview.threadId, preview.turnId, preview.messageId, true);
6950
+ const active = this.createActiveTurnState(preview.scopeId, target.chatId, target.chatType, target.topicId, preview.threadId, preview.turnId, preview.messageId, preview.isObserved);
6950
6951
  this.setActiveTurn(preview.scopeId, preview.turnId, active);
6951
6952
  const watcher = {
6952
6953
  scopeId: preview.scopeId,
@@ -6973,6 +6974,7 @@ export class BridgeSessionCore {
6973
6974
  scopeId: preview.scopeId,
6974
6975
  threadId: preview.threadId,
6975
6976
  turnId: preview.turnId,
6977
+ isObserved: preview.isObserved,
6976
6978
  });
6977
6979
  return true;
6978
6980
  }
@@ -7153,6 +7155,7 @@ export class BridgeSessionCore {
7153
7155
  scopeId: active.scopeId,
7154
7156
  threadId: active.threadId,
7155
7157
  messageId,
7158
+ isObserved: active.isObserved,
7156
7159
  });
7157
7160
  }
7158
7161
  catch (error) {
@@ -4,6 +4,7 @@ export interface ActiveTurnPreviewRecord {
4
4
  scopeId: string;
5
5
  threadId: string;
6
6
  messageId: number;
7
+ isObserved: boolean;
7
8
  createdAt: number;
8
9
  updatedAt: number;
9
10
  }
@@ -74,7 +75,9 @@ export declare class BridgeStore {
74
75
  getPendingApprovalByServerRequestId(serverRequestId: string): PendingApprovalRecord | null;
75
76
  markApprovalResolved(localId: string): void;
76
77
  countPendingApprovals(): number;
77
- saveActiveTurnPreview(record: Pick<ActiveTurnPreviewRecord, 'turnId' | 'scopeId' | 'threadId' | 'messageId'>): void;
78
+ saveActiveTurnPreview(record: Pick<ActiveTurnPreviewRecord, 'turnId' | 'scopeId' | 'threadId' | 'messageId'> & {
79
+ isObserved?: boolean;
80
+ }): void;
78
81
  listActiveTurnPreviews(): ActiveTurnPreviewRecord[];
79
82
  removeActiveTurnPreview(turnId: string): void;
80
83
  removeActiveTurnPreviewByMessage(scopeId: string, messageId: number): void;
@@ -70,6 +70,7 @@ export class BridgeStore {
70
70
  scope_id TEXT NOT NULL,
71
71
  thread_id TEXT NOT NULL,
72
72
  message_id INTEGER NOT NULL,
73
+ is_observed INTEGER NOT NULL DEFAULT 0,
73
74
  created_at INTEGER NOT NULL,
74
75
  updated_at INTEGER NOT NULL
75
76
  );
@@ -209,6 +210,7 @@ export class BridgeStore {
209
210
  this.ensureColumn('pending_approvals', 'payload_json', 'TEXT');
210
211
  this.ensureColumn('pending_user_inputs', 'status', "TEXT NOT NULL DEFAULT 'pending'");
211
212
  this.ensureColumn('pending_user_inputs', 'submitted_at', 'INTEGER');
213
+ this.ensureColumn('active_turn_previews', 'is_observed', 'INTEGER NOT NULL DEFAULT 0');
212
214
  this.ensureColumn('codex_auth_candidates', 'state', "TEXT NOT NULL DEFAULT 'active'");
213
215
  this.ensureColumn('codex_auth_candidate_runtime', 'state', "TEXT NOT NULL DEFAULT 'active'");
214
216
  this.ensureColumn('codex_auth_quota_snapshots', 'plan_type', 'TEXT');
@@ -414,22 +416,23 @@ export class BridgeStore {
414
416
  saveActiveTurnPreview(record) {
415
417
  const now = Date.now();
416
418
  this.db.prepare('DELETE FROM active_turn_previews WHERE turn_id = ? OR scope_id = ?').run(record.turnId, record.scopeId);
417
- this.db.prepare(`
418
- INSERT INTO active_turn_previews (turn_id, scope_id, thread_id, message_id, created_at, updated_at)
419
- VALUES (?, ?, ?, ?, ?, ?)
420
- `).run(record.turnId, record.scopeId, record.threadId, record.messageId, now, now);
419
+ this.db.prepare(`
420
+ INSERT INTO active_turn_previews (turn_id, scope_id, thread_id, message_id, is_observed, created_at, updated_at)
421
+ VALUES (?, ?, ?, ?, ?, ?, ?)
422
+ `).run(record.turnId, record.scopeId, record.threadId, record.messageId, record.isObserved ? 1 : 0, now, now);
421
423
  }
422
424
  listActiveTurnPreviews() {
423
- const rows = this.db.prepare(`
424
- SELECT turn_id, scope_id, thread_id, message_id, created_at, updated_at
425
- FROM active_turn_previews
426
- ORDER BY created_at ASC
425
+ const rows = this.db.prepare(`
426
+ SELECT turn_id, scope_id, thread_id, message_id, is_observed, created_at, updated_at
427
+ FROM active_turn_previews
428
+ ORDER BY created_at ASC
427
429
  `).all();
428
430
  return rows.map((row) => ({
429
431
  turnId: String(row.turn_id),
430
432
  scopeId: String(row.scope_id),
431
433
  threadId: String(row.thread_id),
432
434
  messageId: Number(row.message_id),
435
+ isObserved: Boolean(row.is_observed),
433
436
  createdAt: Number(row.created_at),
434
437
  updatedAt: Number(row.updated_at),
435
438
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxden-app/foxclaw",
3
- "version": "0.5.15",
3
+ "version": "0.5.16",
4
4
  "description": "Foxden local execution claw for controlling Codex from trusted chat interfaces.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",