@ccpocket/bridge 1.67.3 → 1.67.4

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.
@@ -124,6 +124,10 @@ export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
124
124
  private lastPlanItemText;
125
125
  /** Last assistant text message — used as `result` in completion notification. */
126
126
  private lastResultText;
127
+ /** Agent text received as deltas but not yet confirmed by item/completed. */
128
+ private readonly pendingAgentTextByItemId;
129
+ /** Suppresses late item/completed events after synthetic fallbacks. */
130
+ private readonly syntheticAgentTextByItemId;
127
131
  private pendingPlanCompletion;
128
132
  /** Queued plan execution text when inputResolve wasn't ready at approval time. */
129
133
  private _pendingPlanInput;
@@ -304,6 +308,7 @@ export declare class CodexProcess extends EventEmitter<CodexProcessEvents> {
304
308
  private handleNotification;
305
309
  private isForeignThreadNotification;
306
310
  private handleTurnCompleted;
311
+ private finalizePendingAgentText;
307
312
  private cleanupSteerTempPaths;
308
313
  private processItemStarted;
309
314
  private processItemCompleted;
@@ -9,6 +9,7 @@ import { resolvePlatformPath } from "./path-utils.js";
9
9
  export { buildCodexSpawnSpec };
10
10
  const DEFAULT_CODEX_MODEL = "gpt-5.5";
11
11
  const COMPLETION_FETCH_COOLDOWN_MS = 1000;
12
+ const UNKNOWN_AGENT_ITEM_ID = "__unknown_agent_message__";
12
13
  const CODEX_CLI_NOT_FOUND_MESSAGE = "Codex CLI is not installed or not available on PATH on the Bridge machine. Install it with `curl -fsSL https://chatgpt.com/codex/install.sh | sh`, then restart Bridge.";
13
14
  function isCodexCliNotFoundError(err) {
14
15
  const code = err.code;
@@ -74,6 +75,10 @@ export class CodexProcess extends EventEmitter {
74
75
  lastPlanItemText = null;
75
76
  /** Last assistant text message — used as `result` in completion notification. */
76
77
  lastResultText = null;
78
+ /** Agent text received as deltas but not yet confirmed by item/completed. */
79
+ pendingAgentTextByItemId = new Map();
80
+ /** Suppresses late item/completed events after synthetic fallbacks. */
81
+ syntheticAgentTextByItemId = new Map();
77
82
  pendingPlanCompletion = null;
78
83
  /** Queued plan execution text when inputResolve wasn't ready at approval time. */
79
84
  _pendingPlanInput = null;
@@ -420,6 +425,8 @@ export class CodexProcess extends EventEmitter {
420
425
  this._collaborationMode = options?.collaborationMode ?? "default";
421
426
  this.lastPlanItemText = null;
422
427
  this.lastResultText = null;
428
+ this.pendingAgentTextByItemId.clear();
429
+ this.syntheticAgentTextByItemId.clear();
423
430
  this.pendingPlanCompletion = null;
424
431
  this._pendingPlanInput = null;
425
432
  this._projectPath = projectPath;
@@ -1575,6 +1582,9 @@ export class CodexProcess extends EventEmitter {
1575
1582
  if (typeof turn?.id === "string") {
1576
1583
  this.pendingTurnId = turn.id;
1577
1584
  }
1585
+ this.lastResultText = null;
1586
+ this.pendingAgentTextByItemId.clear();
1587
+ this.syntheticAgentTextByItemId.clear();
1578
1588
  this.setStatus("running");
1579
1589
  break;
1580
1590
  }
@@ -1628,6 +1638,8 @@ export class CodexProcess extends EventEmitter {
1628
1638
  ? params.textDelta
1629
1639
  : "";
1630
1640
  if (delta) {
1641
+ const itemId = stringOrNull(params.itemId) ?? UNKNOWN_AGENT_ITEM_ID;
1642
+ this.pendingAgentTextByItemId.set(itemId, (this.pendingAgentTextByItemId.get(itemId) ?? "") + delta);
1631
1643
  this.emitMessage({ type: "stream_delta", text: delta });
1632
1644
  }
1633
1645
  break;
@@ -1757,6 +1769,7 @@ export class CodexProcess extends EventEmitter {
1757
1769
  }
1758
1770
  handleTurnCompleted(turn) {
1759
1771
  const status = String(turn?.status ?? "completed");
1772
+ this.finalizePendingAgentText();
1760
1773
  const usage = this.lastTokenUsage;
1761
1774
  this.lastTokenUsage = null;
1762
1775
  if (status === "failed") {
@@ -1822,6 +1835,26 @@ export class CodexProcess extends EventEmitter {
1822
1835
  }
1823
1836
  this.cleanupSteerTempPaths();
1824
1837
  }
1838
+ finalizePendingAgentText() {
1839
+ const pendingItems = [...this.pendingAgentTextByItemId.entries()];
1840
+ this.pendingAgentTextByItemId.clear();
1841
+ for (const [pendingItemId, text] of pendingItems) {
1842
+ if (!text.trim())
1843
+ continue;
1844
+ const itemId = pendingItemId === UNKNOWN_AGENT_ITEM_ID ? randomUUID() : pendingItemId;
1845
+ this.lastResultText = text;
1846
+ this.syntheticAgentTextByItemId.set(itemId, text);
1847
+ this.emitMessage({
1848
+ type: "assistant",
1849
+ message: {
1850
+ id: itemId,
1851
+ role: "assistant",
1852
+ content: [{ type: "text", text }],
1853
+ model: this.getMessageModel(),
1854
+ },
1855
+ });
1856
+ }
1857
+ }
1825
1858
  cleanupSteerTempPaths() {
1826
1859
  const tempPaths = this.steerTempPaths.splice(0);
1827
1860
  void Promise.all(tempPaths.map((path) => rm(path, { force: true }).catch(() => { })));
@@ -1963,9 +1996,35 @@ export class CodexProcess extends EventEmitter {
1963
1996
  const itemType = normalizeItemType(item.type);
1964
1997
  switch (itemType) {
1965
1998
  case "agentmessage": {
1966
- const text = extractAgentText(item);
1967
- if (!text)
1999
+ const completedText = extractAgentText(item);
2000
+ const hasCompletedText = completedText?.trim().length > 0;
2001
+ const directPendingText = this.pendingAgentTextByItemId.get(itemId);
2002
+ const usedUnknownPendingText = !hasCompletedText && directPendingText === undefined;
2003
+ const pendingText = usedUnknownPendingText
2004
+ ? (this.pendingAgentTextByItemId.get(UNKNOWN_AGENT_ITEM_ID) ?? "")
2005
+ : (directPendingText ?? "");
2006
+ const text = hasCompletedText ? completedText : pendingText;
2007
+ this.pendingAgentTextByItemId.delete(itemId);
2008
+ if (usedUnknownPendingText) {
2009
+ this.pendingAgentTextByItemId.delete(UNKNOWN_AGENT_ITEM_ID);
2010
+ }
2011
+ if (!text.trim())
1968
2012
  return;
2013
+ if (this.pendingTurnId === null) {
2014
+ const syntheticText = this.syntheticAgentTextByItemId.get(itemId);
2015
+ if (syntheticText === text) {
2016
+ this.syntheticAgentTextByItemId.delete(itemId);
2017
+ return;
2018
+ }
2019
+ const syntheticEntry = [
2020
+ ...this.syntheticAgentTextByItemId.entries(),
2021
+ ].find(([, candidate]) => candidate === text);
2022
+ if (syntheticEntry) {
2023
+ this.syntheticAgentTextByItemId.delete(syntheticEntry[0]);
2024
+ return;
2025
+ }
2026
+ }
2027
+ this.syntheticAgentTextByItemId.delete(itemId);
1969
2028
  this.lastResultText = text;
1970
2029
  this.emitMessage({
1971
2030
  type: "assistant",