@ccpocket/bridge 1.48.0 → 1.49.0

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/dist/websocket.js CHANGED
@@ -38,6 +38,10 @@ const CODEX_MODELS = [
38
38
  "gpt-5.3-codex",
39
39
  "gpt-5.3-codex-spark",
40
40
  ];
41
+ const OPT_IN_SERVER_MESSAGES = new Set([
42
+ "conversation_queue",
43
+ "prompt_history_status",
44
+ ]);
41
45
  // ---- Codex mode mapping helpers ----
42
46
  /** Map unified PermissionMode to Codex approval_policy.
43
47
  * Only "bypassPermissions" maps to "never"; all others use "on-request". */
@@ -166,6 +170,7 @@ export class BridgeWebSocketServer {
166
170
  worktreeStore;
167
171
  pushRelay;
168
172
  promptHistoryBackup;
173
+ promptHistoryStore;
169
174
  recentSessionsRequestId = 0;
170
175
  debugEvents = new Map();
171
176
  notifiedPermissionToolUses = new Map();
@@ -181,7 +186,7 @@ export class BridgeWebSocketServer {
181
186
  platform;
182
187
  clientSupportedServerMessages = new WeakMap();
183
188
  constructor(options) {
184
- const { server, apiKey, allowedDirs, imageStore, galleryStore, projectHistory, debugTraceStore, recordingStore, firebaseAuth, promptHistoryBackup, platform, } = options;
189
+ const { server, apiKey, allowedDirs, imageStore, galleryStore, projectHistory, debugTraceStore, recordingStore, firebaseAuth, promptHistoryBackup, promptHistoryStore, platform, } = options;
185
190
  this.apiKey = apiKey ?? null;
186
191
  this.allowedDirs = allowedDirs ?? [];
187
192
  this.imageStore = imageStore ?? null;
@@ -192,6 +197,7 @@ export class BridgeWebSocketServer {
192
197
  this.worktreeStore = new WorktreeStore();
193
198
  this.pushRelay = new PushRelayClient({ firebaseAuth });
194
199
  this.promptHistoryBackup = promptHistoryBackup ?? null;
200
+ this.promptHistoryStore = promptHistoryStore ?? null;
195
201
  this.platform = platform ?? process.platform;
196
202
  this.archiveStore = new ArchiveStore();
197
203
  void this.debugTraceStore.init().catch((err) => {
@@ -544,6 +550,7 @@ export class BridgeWebSocketServer {
544
550
  async handleClientMessage(msg, ws) {
545
551
  if (msg.type === "client_capabilities") {
546
552
  this.clientSupportedServerMessages.set(ws, new Set(msg.supportedServerMessages ?? []));
553
+ this.sendPromptHistoryStatus(ws);
547
554
  return;
548
555
  }
549
556
  const incomingSessionId = this.extractSessionIdFromClientMessage(msg);
@@ -3205,6 +3212,144 @@ export class BridgeWebSocketServer {
3205
3212
  });
3206
3213
  break;
3207
3214
  }
3215
+ case "record_prompt_history": {
3216
+ if (!this.promptHistoryStore) {
3217
+ this.send(ws, {
3218
+ type: "prompt_history_mutation_result",
3219
+ success: false,
3220
+ error: "Prompt history store not available",
3221
+ });
3222
+ break;
3223
+ }
3224
+ try {
3225
+ const entry = await this.promptHistoryStore.record({
3226
+ text: msg.text,
3227
+ projectPath: msg.projectPath,
3228
+ clientId: msg.clientId,
3229
+ clientName: msg.clientName,
3230
+ sessionId: msg.sessionId,
3231
+ usedAt: msg.usedAt,
3232
+ });
3233
+ this.send(ws, {
3234
+ type: "prompt_history_mutation_result",
3235
+ success: true,
3236
+ bridgeInstanceId: this.promptHistoryStore.bridgeInstanceId,
3237
+ revision: this.promptHistoryStore.revision,
3238
+ entry,
3239
+ });
3240
+ this.broadcastPromptHistoryStatus();
3241
+ }
3242
+ catch (err) {
3243
+ this.send(ws, {
3244
+ type: "prompt_history_mutation_result",
3245
+ success: false,
3246
+ error: err instanceof Error ? err.message : String(err),
3247
+ });
3248
+ }
3249
+ break;
3250
+ }
3251
+ case "sync_prompt_history": {
3252
+ if (!this.promptHistoryStore) {
3253
+ this.send(ws, {
3254
+ type: "prompt_history_sync_result",
3255
+ success: false,
3256
+ error: "Prompt history store not available",
3257
+ });
3258
+ break;
3259
+ }
3260
+ try {
3261
+ if (msg.entries?.length) {
3262
+ await this.promptHistoryStore.mergeClientEntries(msg.entries);
3263
+ }
3264
+ this.send(ws, {
3265
+ type: "prompt_history_sync_result",
3266
+ success: true,
3267
+ bridgeInstanceId: this.promptHistoryStore.bridgeInstanceId,
3268
+ revision: this.promptHistoryStore.revision,
3269
+ syncedAt: new Date().toISOString(),
3270
+ fullSnapshot: true,
3271
+ entries: this.promptHistoryStore.list(msg.includeDeleted ?? true),
3272
+ });
3273
+ this.broadcastPromptHistoryStatus();
3274
+ }
3275
+ catch (err) {
3276
+ this.send(ws, {
3277
+ type: "prompt_history_sync_result",
3278
+ success: false,
3279
+ error: err instanceof Error ? err.message : String(err),
3280
+ });
3281
+ }
3282
+ break;
3283
+ }
3284
+ case "mutate_prompt_history": {
3285
+ if (!this.promptHistoryStore) {
3286
+ this.send(ws, {
3287
+ type: "prompt_history_mutation_result",
3288
+ success: false,
3289
+ error: "Prompt history store not available",
3290
+ });
3291
+ break;
3292
+ }
3293
+ try {
3294
+ const entry = await this.promptHistoryStore.mutate({
3295
+ id: msg.id,
3296
+ text: msg.text,
3297
+ projectPath: msg.projectPath,
3298
+ action: msg.action,
3299
+ isFavorite: msg.isFavorite,
3300
+ updatedAt: msg.updatedAt,
3301
+ });
3302
+ this.send(ws, {
3303
+ type: "prompt_history_mutation_result",
3304
+ success: entry != null,
3305
+ bridgeInstanceId: this.promptHistoryStore.bridgeInstanceId,
3306
+ revision: this.promptHistoryStore.revision,
3307
+ entry: entry ?? undefined,
3308
+ error: entry == null ? "Prompt not found" : undefined,
3309
+ });
3310
+ if (entry)
3311
+ this.broadcastPromptHistoryStatus();
3312
+ }
3313
+ catch (err) {
3314
+ this.send(ws, {
3315
+ type: "prompt_history_mutation_result",
3316
+ success: false,
3317
+ error: err instanceof Error ? err.message : String(err),
3318
+ });
3319
+ }
3320
+ break;
3321
+ }
3322
+ case "import_prompt_history_v1": {
3323
+ if (!this.promptHistoryStore) {
3324
+ this.send(ws, {
3325
+ type: "prompt_history_sync_result",
3326
+ success: false,
3327
+ error: "Prompt history store not available",
3328
+ });
3329
+ break;
3330
+ }
3331
+ try {
3332
+ const result = await this.promptHistoryStore.importEntries(msg.entries, msg.clientId, msg.clientName);
3333
+ this.send(ws, {
3334
+ type: "prompt_history_sync_result",
3335
+ success: true,
3336
+ bridgeInstanceId: this.promptHistoryStore.bridgeInstanceId,
3337
+ revision: this.promptHistoryStore.revision,
3338
+ syncedAt: new Date().toISOString(),
3339
+ fullSnapshot: true,
3340
+ entries: result.entries,
3341
+ });
3342
+ this.broadcastPromptHistoryStatus();
3343
+ }
3344
+ catch (err) {
3345
+ this.send(ws, {
3346
+ type: "prompt_history_sync_result",
3347
+ success: false,
3348
+ error: err instanceof Error ? err.message : String(err),
3349
+ });
3350
+ }
3351
+ break;
3352
+ }
3208
3353
  case "rename_session": {
3209
3354
  const name = msg.name || null;
3210
3355
  await this.handleRenameSession(ws, msg.sessionId, name, msg);
@@ -3308,6 +3453,19 @@ export class BridgeWebSocketServer {
3308
3453
  bridgeVersion: getPackageVersion(),
3309
3454
  });
3310
3455
  }
3456
+ sendPromptHistoryStatus(ws) {
3457
+ if (!this.promptHistoryStore)
3458
+ return;
3459
+ const entries = this.promptHistoryStore.list(true);
3460
+ const updatedAt = entries.reduce((latest, entry) => !latest || entry.updatedAt > latest ? entry.updatedAt : latest, undefined);
3461
+ this.send(ws, {
3462
+ type: "prompt_history_status",
3463
+ bridgeInstanceId: this.promptHistoryStore.bridgeInstanceId,
3464
+ revision: this.promptHistoryStore.revision,
3465
+ entryCount: entries.filter((entry) => !entry.deletedAt).length,
3466
+ updatedAt,
3467
+ });
3468
+ }
3311
3469
  /** Broadcast session list to all connected clients. */
3312
3470
  broadcastSessionList() {
3313
3471
  this.pruneDebugEvents();
@@ -3323,6 +3481,19 @@ export class BridgeWebSocketServer {
3323
3481
  bridgeVersion: getPackageVersion(),
3324
3482
  });
3325
3483
  }
3484
+ broadcastPromptHistoryStatus() {
3485
+ if (!this.promptHistoryStore)
3486
+ return;
3487
+ const entries = this.promptHistoryStore.list(true);
3488
+ const updatedAt = entries.reduce((latest, entry) => !latest || entry.updatedAt > latest ? entry.updatedAt : latest, undefined);
3489
+ this.broadcast({
3490
+ type: "prompt_history_status",
3491
+ bridgeInstanceId: this.promptHistoryStore.bridgeInstanceId,
3492
+ revision: this.promptHistoryStore.revision,
3493
+ entryCount: entries.filter((entry) => !entry.deletedAt).length,
3494
+ updatedAt,
3495
+ });
3496
+ }
3326
3497
  broadcastSessionMessage(sessionId, msg) {
3327
3498
  this.maybeSendPushNotification(sessionId, msg);
3328
3499
  this.recordDebugEvent(sessionId, {
@@ -3681,11 +3852,10 @@ export class BridgeWebSocketServer {
3681
3852
  }
3682
3853
  }
3683
3854
  shouldSendToClient(ws, msg) {
3684
- if (msg.type !== "conversation_queue")
3855
+ const type = typeof msg.type === "string" ? msg.type : "";
3856
+ if (!OPT_IN_SERVER_MESSAGES.has(type))
3685
3857
  return true;
3686
- return (this.clientSupportedServerMessages
3687
- .get(ws)
3688
- ?.has("conversation_queue") ?? false);
3858
+ return (this.clientSupportedServerMessages.get(ws)?.has(type) ?? false);
3689
3859
  }
3690
3860
  hasInputConflictSince(sessionId, baseSeq) {
3691
3861
  const delta = this.sessionManager.getHistorySince(sessionId, baseSeq);