@nyssance/codex-acp-v2 0.7.10 → 0.7.11

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
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.11 — 2026-10-12
4
+
5
+ ### Added
6
+
7
+ - `_codex/session_list` returns all non-archived sessions across projects when called
8
+ without params, walking native pages and deduplicating session IDs. Requests with
9
+ defined params retain the filtering and pagination behavior of `session/list`.
10
+
11
+ ### Fixed
12
+
13
+ - Suppress duplicate review results in live updates and full-turn history when the
14
+ review exit repeats the assistant's answer, while preserving standalone or different
15
+ review results regardless of native event order.
16
+
3
17
  ## 0.7.10 — 2026-10-11
4
18
 
5
19
  ### Fixed
package/dist/index.js CHANGED
@@ -22168,7 +22168,7 @@ var package_default = {
22168
22168
  publishConfig: {
22169
22169
  access: "public"
22170
22170
  },
22171
- version: "0.7.10",
22171
+ version: "0.7.11",
22172
22172
  description: "Native ACP v2 agent for the OpenAI Codex app-server",
22173
22173
  packageManager: "bun@1.4.0",
22174
22174
  engines: {
@@ -24273,6 +24273,12 @@ function itemSnapshot(item, command) {
24273
24273
  return [];
24274
24274
  }
24275
24275
  }
24276
+ function isDuplicateReview(item, items) {
24277
+ if (item.type !== "exitedReviewMode")
24278
+ return false;
24279
+ const text = item.review.trim();
24280
+ return text.length > 0 && Array.from(items).some((candidate) => candidate.type === "agentMessage" && candidate.phase !== "commentary" && candidate.text.trim() === text);
24281
+ }
24276
24282
 
24277
24283
  // src/bridge/turnMetadata.ts
24278
24284
  function withTurnId(update, turnId, startedAt) {
@@ -24314,6 +24320,8 @@ class EventBridge {
24314
24320
  turnPrompt = null;
24315
24321
  openToolCalls = new Set;
24316
24322
  announcedTools = new Set;
24323
+ pendingReviews = new Map;
24324
+ reviewMessages = new Map;
24317
24325
  messagePhases = new Map;
24318
24326
  promptMeta = new Map;
24319
24327
  reasoningWithDeltas = new Set;
@@ -24377,6 +24385,8 @@ class EventBridge {
24377
24385
  this.openToolCalls.clear();
24378
24386
  this.announcedTools.clear();
24379
24387
  this.messagePhases.clear();
24388
+ this.reviewMessages.clear();
24389
+ this.pendingReviews.clear();
24380
24390
  this.reasoningWithDeltas.clear();
24381
24391
  this.terminalItems.clear();
24382
24392
  this.imageGenerations.clear();
@@ -24399,6 +24409,8 @@ class EventBridge {
24399
24409
  for (const item of items) {
24400
24410
  if (item.type === "userMessage")
24401
24411
  this.unresolvedUserItems.add(item.id);
24412
+ if (item.type === "agentMessage")
24413
+ this.reviewMessages.set(item.id, item);
24402
24414
  if (!replay && itemSnapshot(item).some((update) => update.sessionUpdate === "tool_call_update")) {
24403
24415
  this.unannouncedItems.set(item.id, { item, turnId });
24404
24416
  }
@@ -24411,8 +24423,12 @@ class EventBridge {
24411
24423
  if (receipt)
24412
24424
  this.restoredCommands.set(item.id, receipt);
24413
24425
  this.unresolvedUserItems.delete(item.id);
24414
- if (!replay)
24426
+ if (!replay || isDuplicateReview(item, this.reviewMessages.values()))
24415
24427
  continue;
24428
+ if (item.type === "exitedReviewMode") {
24429
+ this.pendingReviews.set(item.id, { item, turnId });
24430
+ continue;
24431
+ }
24416
24432
  if (item.type === "commandExecution" && item.status === "inProgress" && usesTerminal(item))
24417
24433
  this.terminalItems.add(item.id);
24418
24434
  for (const update of itemSnapshot(item, receipt ?? undefined)) {
@@ -24532,6 +24548,7 @@ class EventBridge {
24532
24548
  }
24533
24549
  }
24534
24550
  async finishOpenToolCalls(status) {
24551
+ await this.flushReviews();
24535
24552
  this.unannouncedItems.clear();
24536
24553
  const ids = [...this.openToolCalls];
24537
24554
  const terminals = [...this.terminalItems];
@@ -24556,6 +24573,18 @@ class EventBridge {
24556
24573
  }
24557
24574
  async flush() {
24558
24575
  await this.flushPlans();
24576
+ await this.flushReviews();
24577
+ }
24578
+ async flushReviews() {
24579
+ const pending = [...this.pendingReviews.values()];
24580
+ this.pendingReviews.clear();
24581
+ for (const { item, turnId } of pending) {
24582
+ if (isDuplicateReview(item, this.reviewMessages.values()))
24583
+ continue;
24584
+ for (const update of itemSnapshot(item)) {
24585
+ await this.client.update(this.withTiming(withTurnId(update, turnId, this.turnTime?.startedAt), turnId));
24586
+ }
24587
+ }
24559
24588
  }
24560
24589
  dispose() {
24561
24590
  this.beginTurn();
@@ -24578,7 +24607,7 @@ class EventBridge {
24578
24607
  case "item/started":
24579
24608
  return this.itemStarted(notification.params.item);
24580
24609
  case "item/completed":
24581
- return this.itemCompleted(notification.params.item);
24610
+ return this.itemCompleted(notification.params.item, notification.params.turnId);
24582
24611
  case "item/commandExecution/outputDelta": {
24583
24612
  const { itemId, delta } = notification.params;
24584
24613
  return this.terminalItems.has(itemId) && delta.length > 0 ? [terminalOutputChunk(itemId, delta)] : [];
@@ -24792,9 +24821,10 @@ ${details}` : summary)];
24792
24821
  return [];
24793
24822
  }
24794
24823
  }
24795
- async itemCompleted(item) {
24824
+ async itemCompleted(item, turnId) {
24796
24825
  switch (item.type) {
24797
24826
  case "agentMessage":
24827
+ this.reviewMessages.set(item.id, item);
24798
24828
  this.messagePhases.delete(item.id);
24799
24829
  return itemSnapshot(item);
24800
24830
  case "reasoning": {
@@ -24834,8 +24864,8 @@ ${details}` : summary)];
24834
24864
  case "plan":
24835
24865
  return await this.planCompleted(item);
24836
24866
  case "exitedReviewMode": {
24837
- const text = item.review.trim();
24838
- return text.length > 0 ? [this.agentMessage(item.id, text)] : [];
24867
+ this.pendingReviews.set(item.id, { item, turnId });
24868
+ return [];
24839
24869
  }
24840
24870
  case "userMessage":
24841
24871
  case "hookPrompt":
@@ -27959,6 +27989,8 @@ function historyUpdates(turns, commandReceipts = new Map) {
27959
27989
  for (const turn of turns) {
27960
27990
  const openingUser = turn.items.find((item) => item.type === "userMessage");
27961
27991
  for (const item of turn.items) {
27992
+ if (isDuplicateReview(item, turn.items))
27993
+ continue;
27962
27994
  const command = item.type === "userMessage" ? commandReceipts.get(item.id) : undefined;
27963
27995
  updates.push(...itemSnapshot(item, command).map((update) => {
27964
27996
  const replay = withTurnId(update, turn.id, turn.startedAt);
@@ -28222,7 +28254,7 @@ async function readSessionHistory(codex, raw, signal, commandReceipts) {
28222
28254
  if (view === "full")
28223
28255
  await read.call(() => assertCommandHistory(codex, start.thread, commandReceipts, signal));
28224
28256
  const turns = await read.call(() => Promise.all(page.data.map(async (turn) => {
28225
- const projections = view === "full" ? await Promise.all(turn.items.map(async (item) => projectHistoryItem(item, turn.id, turn.startedAt, item.type === "userMessage" ? await commandReceipts?.read(item.id) : null))) : [];
28257
+ const projections = view === "full" ? await Promise.all(turn.items.filter((item) => !isDuplicateReview(item, turn.items)).map(async (item) => projectHistoryItem(item, turn.id, turn.startedAt, item.type === "userMessage" ? await commandReceipts?.read(item.id) : null))) : [];
28226
28258
  return {
28227
28259
  turnId: turn.id,
28228
28260
  status: turn.status,
@@ -28501,7 +28533,7 @@ class CodexAgent {
28501
28533
  additionalDirectories: {}
28502
28534
  },
28503
28535
  providers: {},
28504
- _meta: { alwith: { tools: { version: 1 }, turns: { version: 2 } }, codex: { forkAtTurn: true, sessionLineage: true, archive: true, sessionHistory: { version: 2, items: true, modes: ["browse", "export"] }, seedHistory: true, skills: true, plugins: true, providerCatalog: true, rename: true, account: true, fuzzyFileSearch: true } }
28536
+ _meta: { alwith: { tools: { version: 1 }, turns: { version: 2 } }, codex: { forkAtTurn: true, sessionLineage: true, archive: true, sessionList: { version: 1 }, sessionHistory: { version: 2, items: true, modes: ["browse", "export"] }, seedHistory: true, skills: true, plugins: true, providerCatalog: true, rename: true, account: true, fuzzyFileSearch: true } }
28505
28537
  },
28506
28538
  authMethods: authMethods(this.capabilities, this.env)
28507
28539
  };
@@ -29067,16 +29099,41 @@ class CodexAgent {
29067
29099
  }
29068
29100
  async listSessions(params) {
29069
29101
  this.requireInitialized("session/list");
29102
+ return this.readSessionList(params, false);
29103
+ }
29104
+ async sessionList(params = {}) {
29105
+ this.requireInitialized("_codex/session_list");
29106
+ if (Object.values(params).some((value) => value !== undefined))
29107
+ return this.listSessions(params);
29108
+ return this.readSessionList({}, true);
29109
+ }
29110
+ async readSessionList(params, all) {
29070
29111
  const cwd = params.cwd?.trim() || null;
29071
29112
  const archived = archivedFilter(params._meta);
29072
29113
  const list = (cursor) => this.withCodex(() => this.codex.threadList({
29073
29114
  cursor,
29115
+ ...all ? { limit: 100 } : {},
29074
29116
  ...cwd ? { cwd } : {},
29075
29117
  sourceKinds: ["cli", "vscode", "exec", "appServer", "unknown"],
29076
29118
  modelProviders: [],
29077
29119
  ...archived ? { archived: true } : {}
29078
29120
  }));
29079
29121
  const response = await list(params.cursor ?? null);
29122
+ if (all) {
29123
+ const threads = new Map(response.data.map((thread) => [thread.id, thread]));
29124
+ const cursors = new Set;
29125
+ while (response.nextCursor !== null) {
29126
+ const cursor = response.nextCursor;
29127
+ if (cursors.has(cursor))
29128
+ throw RequestError.internalError(undefined, "Codex thread list repeated a cursor");
29129
+ cursors.add(cursor);
29130
+ const page = await list(cursor);
29131
+ for (const thread of page.data)
29132
+ threads.set(thread.id, thread);
29133
+ response.nextCursor = page.nextCursor;
29134
+ }
29135
+ response.data = [...threads.values()];
29136
+ }
29080
29137
  const home = this.codexHome;
29081
29138
  if (home === null)
29082
29139
  throw RequestError.internalError(undefined, "Codex home is unavailable after initialize");
@@ -29100,16 +29157,18 @@ class CodexAgent {
29100
29157
  }
29101
29158
  response.data.push(...missing2.values());
29102
29159
  }
29103
- return {
29104
- sessions: await Promise.all(response.data.map(async (thread) => ({
29160
+ const sessions = [];
29161
+ const batchSize = all ? 8 : response.data.length;
29162
+ for (let offset = 0;offset < response.data.length; offset += batchSize) {
29163
+ sessions.push(...await Promise.all(response.data.slice(offset, offset + batchSize).map(async (thread) => ({
29105
29164
  sessionId: thread.id,
29106
29165
  cwd: thread.cwd,
29107
29166
  title: !thread.name?.trim() && /^(?:\[@|# Files mentioned by the user:|<send_user_message_question_reply>)/.test(thread.preview.trimStart()) ? historyTitle(thread, (await this.turnPage(thread.id, null)).data) : thread.name?.trim() || thread.preview.trim() || null,
29108
29167
  updatedAt: new Date(thread.updatedAt * 1000).toISOString(),
29109
29168
  _meta: { codex: { archived, ...threadLineage(thread) } }
29110
- }))),
29111
- nextCursor: response.nextCursor
29112
- };
29169
+ }))));
29170
+ }
29171
+ return all ? { sessions } : { sessions, nextCursor: response.nextCursor };
29113
29172
  }
29114
29173
  async closeSession(params) {
29115
29174
  return await this.admission(() => this.closeSessionInternal(params), params.sessionId);
@@ -29960,6 +30019,20 @@ function interruptedTurn(threadId) {
29960
30019
  turn: { id: "", items: [], itemsView: "notLoaded", status: "interrupted", error: null, startedAt: null, completedAt: null, durationMs: null }
29961
30020
  };
29962
30021
  }
30022
+ function parseSessionListParams(raw) {
30023
+ const params = objectParams()(raw);
30024
+ for (const key of ["cwd", "cursor"]) {
30025
+ const value = params[key];
30026
+ if (value !== undefined && value !== null && typeof value !== "string") {
30027
+ throw RequestError.invalidParams({ [key]: value }, `${key} must be a string when given`);
30028
+ }
30029
+ }
30030
+ const meta = params["_meta"];
30031
+ if (meta !== undefined && meta !== null && (typeof meta !== "object" || Array.isArray(meta))) {
30032
+ throw RequestError.invalidParams({ _meta: meta }, "_meta must be an object when given");
30033
+ }
30034
+ return params;
30035
+ }
29963
30036
  function parseSessionRenameParams(raw) {
29964
30037
  const params = objectParams()(raw);
29965
30038
  if (typeof params.sessionId !== "string" || params.sessionId.length === 0) {
@@ -30106,7 +30179,7 @@ function createAgentApp(options) {
30106
30179
  };
30107
30180
  return agent({ name: options.info.name }).onConnect((connection) => {
30108
30181
  agent2 = new CodexAgent(connection.client, options);
30109
- }).onRequest(methods.agent.initialize, (ctx) => current().initialize(ctx.params)).onRequest(methods.agent.auth.login, (ctx) => current().login(ctx.params, ctx.requestId)).onRequest(methods.agent.auth.logout, (ctx) => current().logout(ctx.params)).onRequest(methods.agent.providers.list, (ctx) => current().listProviders(ctx.params)).onRequest(methods.agent.providers.set, (ctx) => current().setProvider(ctx.params)).onRequest(methods.agent.providers.disable, (ctx) => current().disableProvider(ctx.params)).onRequest(methods.agent.session.new, (ctx) => current().newSession(ctx.params, ctx.signal)).onRequest(methods.agent.session.resume, (ctx) => current().resumeSession(ctx.params, ctx.signal)).onRequest(methods.agent.session.fork, (ctx) => current().forkSession(ctx.params, ctx.signal)).onRequest(methods.agent.session.list, (ctx) => current().listSessions(ctx.params)).onRequest(methods.agent.session.close, (ctx) => current().closeSession(ctx.params)).onRequest(methods.agent.session.delete, (ctx) => current().deleteSession(ctx.params)).onRequest("_codex/session_history", { parse: parseSessionHistoryParams }, (ctx) => current().sessionHistory(ctx.params, ctx.signal)).onRequest("_codex/session_history_items", { parse: parseSessionHistoryItemsParams }, (ctx) => current().sessionHistoryItems(ctx.params, ctx.signal)).onRequest("_codex/session_archive", { parse: parseSessionIdParams }, (ctx) => current().archiveSession(ctx.params)).onRequest("_codex/session_unarchive", { parse: parseSessionIdParams }, (ctx) => current().unarchiveSession(ctx.params)).onRequest("_codex/skills_list", { parse: objectParams() }, (ctx) => current().skillsList(ctx.params)).onRequest("_codex/skills_config_write", { parse: objectParams() }, (ctx) => current().skillsConfigWrite(ctx.params)).onRequest("_codex/plugin_list", { parse: objectParams() }, (ctx) => current().pluginList(ctx.params)).onRequest("_codex/plugin_installed", { parse: objectParams() }, (ctx) => current().pluginInstalled(ctx.params)).onRequest("_codex/plugin_install", { parse: objectParams() }, (ctx) => current().pluginInstall(ctx.params)).onRequest("_codex/plugin_uninstall", { parse: objectParams() }, (ctx) => current().pluginUninstall(ctx.params)).onRequest("_codex/plugin_read", { parse: objectParams() }, (ctx) => current().pluginRead(ctx.params)).onRequest("_codex/marketplace_add", { parse: objectParams() }, (ctx) => current().marketplaceAdd(ctx.params)).onRequest("_codex/marketplace_remove", { parse: objectParams() }, (ctx) => current().marketplaceRemove(ctx.params)).onRequest("_codex/marketplace_upgrade", { parse: objectParams() }, (ctx) => current().marketplaceUpgrade(ctx.params)).onRequest("_codex/session_rename", { parse: parseSessionRenameParams }, (ctx) => current().sessionRename(ctx.params)).onRequest("_codex/account_read", { parse: objectParams() }, () => current().accountRead()).onRequest("_codex/rate_limits", { parse: objectParams() }, () => current().rateLimits()).onRequest("_codex/fuzzy_file_search", { parse: parseFuzzyFileSearchParams }, (ctx) => current().fuzzyFileSearch(ctx.params)).onRequest(methods.agent.session.setConfigOption, (ctx) => current().setSessionConfigOption(ctx.params)).onRequest(methods.agent.session.prompt, (ctx) => current().prompt(ctx.params)).onNotification(methods.agent.session.cancel, (ctx) => current().cancel(ctx.params));
30182
+ }).onRequest(methods.agent.initialize, (ctx) => current().initialize(ctx.params)).onRequest(methods.agent.auth.login, (ctx) => current().login(ctx.params, ctx.requestId)).onRequest(methods.agent.auth.logout, (ctx) => current().logout(ctx.params)).onRequest(methods.agent.providers.list, (ctx) => current().listProviders(ctx.params)).onRequest(methods.agent.providers.set, (ctx) => current().setProvider(ctx.params)).onRequest(methods.agent.providers.disable, (ctx) => current().disableProvider(ctx.params)).onRequest(methods.agent.session.new, (ctx) => current().newSession(ctx.params, ctx.signal)).onRequest(methods.agent.session.resume, (ctx) => current().resumeSession(ctx.params, ctx.signal)).onRequest(methods.agent.session.fork, (ctx) => current().forkSession(ctx.params, ctx.signal)).onRequest(methods.agent.session.list, (ctx) => current().listSessions(ctx.params)).onRequest("_codex/session_list", { parse: parseSessionListParams }, (ctx) => current().sessionList(ctx.params)).onRequest(methods.agent.session.close, (ctx) => current().closeSession(ctx.params)).onRequest(methods.agent.session.delete, (ctx) => current().deleteSession(ctx.params)).onRequest("_codex/session_history", { parse: parseSessionHistoryParams }, (ctx) => current().sessionHistory(ctx.params, ctx.signal)).onRequest("_codex/session_history_items", { parse: parseSessionHistoryItemsParams }, (ctx) => current().sessionHistoryItems(ctx.params, ctx.signal)).onRequest("_codex/session_archive", { parse: parseSessionIdParams }, (ctx) => current().archiveSession(ctx.params)).onRequest("_codex/session_unarchive", { parse: parseSessionIdParams }, (ctx) => current().unarchiveSession(ctx.params)).onRequest("_codex/skills_list", { parse: objectParams() }, (ctx) => current().skillsList(ctx.params)).onRequest("_codex/skills_config_write", { parse: objectParams() }, (ctx) => current().skillsConfigWrite(ctx.params)).onRequest("_codex/plugin_list", { parse: objectParams() }, (ctx) => current().pluginList(ctx.params)).onRequest("_codex/plugin_installed", { parse: objectParams() }, (ctx) => current().pluginInstalled(ctx.params)).onRequest("_codex/plugin_install", { parse: objectParams() }, (ctx) => current().pluginInstall(ctx.params)).onRequest("_codex/plugin_uninstall", { parse: objectParams() }, (ctx) => current().pluginUninstall(ctx.params)).onRequest("_codex/plugin_read", { parse: objectParams() }, (ctx) => current().pluginRead(ctx.params)).onRequest("_codex/marketplace_add", { parse: objectParams() }, (ctx) => current().marketplaceAdd(ctx.params)).onRequest("_codex/marketplace_remove", { parse: objectParams() }, (ctx) => current().marketplaceRemove(ctx.params)).onRequest("_codex/marketplace_upgrade", { parse: objectParams() }, (ctx) => current().marketplaceUpgrade(ctx.params)).onRequest("_codex/session_rename", { parse: parseSessionRenameParams }, (ctx) => current().sessionRename(ctx.params)).onRequest("_codex/account_read", { parse: objectParams() }, () => current().accountRead()).onRequest("_codex/rate_limits", { parse: objectParams() }, () => current().rateLimits()).onRequest("_codex/fuzzy_file_search", { parse: parseFuzzyFileSearchParams }, (ctx) => current().fuzzyFileSearch(ctx.params)).onRequest(methods.agent.session.setConfigOption, (ctx) => current().setSessionConfigOption(ctx.params)).onRequest(methods.agent.session.prompt, (ctx) => current().prompt(ctx.params)).onNotification(methods.agent.session.cancel, (ctx) => current().cancel(ctx.params));
30110
30183
  }
30111
30184
 
30112
30185
  // src/codex/process.ts
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.7.10",
6
+ "version": "0.7.11",
7
7
  "description": "Native ACP v2 agent for the OpenAI Codex app-server",
8
8
  "packageManager": "bun@1.4.0",
9
9
  "engines": {