@oh-my-pi/pi-agent-core 18.8.9 → 18.9.1

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,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.9.0] - 2026-10-11
6
+
7
+ ### Fixed
8
+
9
+ - Fixed stale queue updates when queued steering is adopted, withdrawn, or added to the transcript, ensuring queue listeners consistently receive the latest state.
10
+
5
11
  ## [18.8.8] - 2026-10-10
6
12
 
7
13
  ### Added
@@ -417,7 +417,8 @@ export declare class Agent {
417
417
  buildSideRequestContext(llmMessages: Message[], systemPrompt?: string[]): Promise<Context>;
418
418
  subscribe(fn: (e: AgentEvent) => void): () => void;
419
419
  /** Register a listener notified after any steering/follow-up queue mutator
420
- * (enqueue, dequeue-on-delivery, clear, restore) runs. Internal-only signal —
420
+ * (enqueue, dequeue-on-delivery, clear, restore, live-steering adoption,
421
+ * withdrawal, and landing in the transcript) runs. Internal-only signal —
421
422
  * the queue itself has no concept of display filtering — so listeners
422
423
  * recompute their own snapshot from `peekSteeringQueue()`/`peekFollowUpQueue()`
423
424
  * (or a higher-level view) on notification. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-agent-core",
4
- "version": "18.8.9",
4
+ "version": "18.9.1",
5
5
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": {
@@ -38,16 +38,16 @@
38
38
  "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
39
39
  },
40
40
  "dependencies": {
41
- "@oh-my-pi/pi-ai": "18.8.9",
42
- "@oh-my-pi/pi-catalog": "18.8.9",
43
- "@oh-my-pi/pi-natives": "18.8.9",
44
- "@oh-my-pi/pi-utils": "18.8.9",
45
- "@oh-my-pi/pi-wire": "18.8.9",
46
- "@oh-my-pi/snapcompact": "18.8.9",
41
+ "@oh-my-pi/pi-ai": "18.9.1",
42
+ "@oh-my-pi/pi-catalog": "18.9.1",
43
+ "@oh-my-pi/pi-natives": "18.9.1",
44
+ "@oh-my-pi/pi-utils": "18.9.1",
45
+ "@oh-my-pi/pi-wire": "18.9.1",
46
+ "@oh-my-pi/snapcompact": "18.9.1",
47
47
  "@opentelemetry/api": "^1.9.1"
48
48
  },
49
49
  "devDependencies": {
50
- "@oh-my-pi/omptype": "18.8.9",
50
+ "@oh-my-pi/omptype": "18.9.1",
51
51
  "@opentelemetry/context-async-hooks": "^2.9.0",
52
52
  "@opentelemetry/sdk-trace-base": "^2.9.0",
53
53
  "@types/bun": "^1.3.14"
package/src/agent.ts CHANGED
@@ -939,7 +939,8 @@ export class Agent {
939
939
  }
940
940
 
941
941
  /** Register a listener notified after any steering/follow-up queue mutator
942
- * (enqueue, dequeue-on-delivery, clear, restore) runs. Internal-only signal —
942
+ * (enqueue, dequeue-on-delivery, clear, restore, live-steering adoption,
943
+ * withdrawal, and landing in the transcript) runs. Internal-only signal —
943
944
  * the queue itself has no concept of display filtering — so listeners
944
945
  * recompute their own snapshot from `peekSteeringQueue()`/`peekFollowUpQueue()`
945
946
  * (or a higher-level view) on notification. */
@@ -1072,11 +1073,13 @@ export class Agent {
1072
1073
 
1073
1074
  /** Move steering live steering took out of the queue-delivery records into {@link #liveSteered}. */
1074
1075
  #adoptLiveSteering(taken: readonly AgentMessage[]): void {
1076
+ let adopted = false;
1075
1077
  for (const delivery of this.#queuedMessageDeliveries) {
1076
1078
  const pending = delivery.messages.slice(delivery.next);
1077
1079
  const kept = pending.filter(message => !taken.includes(message));
1078
1080
  if (kept.length === pending.length) continue;
1079
1081
  let last: LiveSteeredEntry | undefined;
1082
+ adopted = true;
1080
1083
  for (const message of pending) {
1081
1084
  if (!taken.includes(message)) continue;
1082
1085
  last = { message, controller: delivery.controller };
@@ -1091,6 +1094,10 @@ export class Agent {
1091
1094
  delivery.next = 0;
1092
1095
  }
1093
1096
  }
1097
+ // The adopted messages move into #liveSteered, which queue snapshots
1098
+ // (peekLiveSteeredMessages) count as still pending; listeners must hear
1099
+ // about the swap or their queue view goes stale for the rest of the turn.
1100
+ if (adopted) this.#emitQueueChanged();
1094
1101
  }
1095
1102
 
1096
1103
  /**
@@ -1109,6 +1116,9 @@ export class Agent {
1109
1116
  suppress(message, controller);
1110
1117
  for (const context of additional ?? []) suppress(context, controller);
1111
1118
  }
1119
+ // Every snapshot counts live-steered messages as pending, so clearing
1120
+ // them is a queue change even when the caller re-queues them right after.
1121
+ const hadLiveSteered = this.#liveSteered.length > 0;
1112
1122
  this.#liveSteered = [];
1113
1123
  for (const delivery of this.#queuedMessageDeliveries) {
1114
1124
  const pending = delivery.messages.slice(delivery.next);
@@ -1116,6 +1126,7 @@ export class Agent {
1116
1126
  for (const message of [...pending, ...delivery.additional]) suppress(message, delivery.controller);
1117
1127
  }
1118
1128
  this.#queuedMessageDeliveries.clear();
1129
+ if (hadLiveSteered) this.#emitQueueChanged();
1119
1130
  return withdrawn;
1120
1131
  }
1121
1132
 
@@ -1338,6 +1349,9 @@ export class Agent {
1338
1349
  const live = this.#liveSteered.findIndex(entry => entry.message === m);
1339
1350
  if (live >= 0) {
1340
1351
  this.#liveSteered.splice(live, 1);
1352
+ // The message landed in the transcript, so it stops counting as
1353
+ // queued pending input; snapshots shrink and listeners must hear it.
1354
+ this.#emitQueueChanged();
1341
1355
  return;
1342
1356
  }
1343
1357
  for (const delivery of this.#queuedMessageDeliveries) {
@@ -412,7 +412,16 @@ function buildCompactionV2Headers(
412
412
  : {
413
413
  "content-type": "application/json",
414
414
  ...resolveOpenAIRequestSetup(
415
- { provider: model.provider, id: model.id, baseUrl: model.baseUrl, headers: model.headers },
415
+ {
416
+ provider: model.provider,
417
+ id: model.id,
418
+ baseUrl: model.baseUrl,
419
+ headers: model.headers,
420
+ compat: {
421
+ sessionHeader:
422
+ model.compat && "sessionHeader" in model.compat ? model.compat.sessionHeader : undefined,
423
+ },
424
+ },
416
425
  { apiKey, messages: [], sessionId: request.sessionId ?? routingSessionId, promptCacheSessionId },
417
426
  ).headers,
418
427
  };
@@ -20,6 +20,7 @@ import { getCodexAttestationHeader } from "@oh-my-pi/pi-ai/providers/openai-code
20
20
  import { createOpenAICodexCompactionRequestContext } from "@oh-my-pi/pi-ai/providers/openai-codex-compaction";
21
21
  import { applyCodexResponsesLiteShape } from "@oh-my-pi/pi-ai/providers/openai-codex/request-transformer";
22
22
  import { createOpenAICodexCompatibilityMetadata } from "@oh-my-pi/pi-ai/providers/openai-codex-responses";
23
+ import { applySessionHeader } from "@oh-my-pi/pi-ai/providers/inference-headers";
23
24
  import {
24
25
  encodeResponsesToolResultOutput,
25
26
  hoistInterleavedResponsesToolBatchMessages,
@@ -862,6 +863,7 @@ export async function requestOpenAiRemoteCompaction(
862
863
  Authorization: `Bearer ${apiKey}`,
863
864
  ...model.headers,
864
865
  };
866
+ applySessionHeader(headers, model.compat, opts?.sessionId);
865
867
 
866
868
  // Codex endpoints require additional auth headers
867
869
  if (isCodexResponses) {