@howaboua/pi-codex-conversion 1.0.23 → 1.0.25

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/README.md CHANGED
@@ -9,7 +9,7 @@ This package replaces Pi's default Codex/GPT experience with a narrower Codex-li
9
9
  - preserves Pi's composed system prompt and applies a narrow Codex-oriented delta on top
10
10
  - renders exec activity with Codex-style command and background-terminal labels
11
11
  - renders `apply_patch` calls with Codex-style `Added` / `Edited` / `Deleted` diff blocks and Pi-style colored diff lines
12
- - targets modern Pi tool/rendering APIs and is aligned with Pi `0.69.x`
12
+ - targets modern Pi tool/rendering APIs and is aligned with Pi `0.70.x`
13
13
 
14
14
  ![Available tools](./available-tools.png)
15
15
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@howaboua/pi-codex-conversion",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
4
4
  "description": "Codex-oriented tool and prompt adapter for pi coding agent",
5
5
  "type": "module",
6
6
  "repository": {
@@ -51,15 +51,15 @@
51
51
  "access": "public"
52
52
  },
53
53
  "peerDependencies": {
54
- "@mariozechner/pi-ai": "^0.69.0",
55
- "@mariozechner/pi-coding-agent": "^0.69.0",
56
- "@mariozechner/pi-tui": "^0.69.0",
54
+ "@mariozechner/pi-ai": "^0.70.0",
55
+ "@mariozechner/pi-coding-agent": "^0.70.0",
56
+ "@mariozechner/pi-tui": "^0.70.0",
57
57
  "typebox": "^1.1.24"
58
58
  },
59
59
  "devDependencies": {
60
- "@mariozechner/pi-ai": "^0.69.0",
61
- "@mariozechner/pi-coding-agent": "^0.69.0",
62
- "@mariozechner/pi-tui": "^0.69.0",
60
+ "@mariozechner/pi-ai": "^0.70.0",
61
+ "@mariozechner/pi-coding-agent": "^0.70.0",
62
+ "@mariozechner/pi-tui": "^0.70.0",
63
63
  "tsx": "^4.20.5",
64
64
  "typebox": "^1.1.24",
65
65
  "typescript": "^5.9.3"
@@ -480,19 +480,19 @@ function clampReasoningEffort(modelId: string, effort: string): string {
480
480
  return effort;
481
481
  }
482
482
 
483
- function getServiceTierCostMultiplier(serviceTier: ServiceTier): number {
483
+ function getServiceTierCostMultiplier(model: Model<Api>, serviceTier: ServiceTier): number {
484
484
  switch (serviceTier) {
485
485
  case "flex":
486
486
  return 0.5;
487
487
  case "priority":
488
- return 2;
488
+ return model.id === "gpt-5.5" ? 2.5 : 2;
489
489
  default:
490
490
  return 1;
491
491
  }
492
492
  }
493
493
 
494
- function applyServiceTierPricing(usage: AssistantMessage["usage"], serviceTier: ServiceTier): void {
495
- const multiplier = getServiceTierCostMultiplier(serviceTier);
494
+ function applyServiceTierPricing(usage: AssistantMessage["usage"], serviceTier: ServiceTier, model: Model<Api>): void {
495
+ const multiplier = getServiceTierCostMultiplier(model, serviceTier);
496
496
  if (multiplier === 1) return;
497
497
  usage.cost.input *= multiplier;
498
498
  usage.cost.output *= multiplier;
@@ -848,6 +848,7 @@ async function* parseWebSocket(socket: WebSocketLike, signal: AbortSignal | unde
848
848
  let pending: (() => void) | null = null;
849
849
  let done = false;
850
850
  let failed: Error | null = null;
851
+ let closeError: Error | null = null;
851
852
  let sawCompletion = false;
852
853
  let pendingMessages = 0;
853
854
  let messageChain = Promise.resolve();
@@ -871,6 +872,7 @@ async function* parseWebSocket(socket: WebSocketLike, signal: AbortSignal | unde
871
872
  const type = typeof parsed.type === "string" ? parsed.type : "";
872
873
  if (type === "response.completed" || type === "response.done" || type === "response.incomplete") {
873
874
  sawCompletion = true;
875
+ closeError = null;
874
876
  done = true;
875
877
  }
876
878
  queue.push(parsed);
@@ -900,8 +902,8 @@ async function* parseWebSocket(socket: WebSocketLike, signal: AbortSignal | unde
900
902
  wake();
901
903
  return;
902
904
  }
903
- if (!failed) {
904
- failed = extractWebSocketCloseError(event);
905
+ if (!closeError) {
906
+ closeError = extractWebSocketCloseError(event);
905
907
  }
906
908
  done = true;
907
909
  wake();
@@ -934,6 +936,7 @@ async function* parseWebSocket(socket: WebSocketLike, signal: AbortSignal | unde
934
936
  }
935
937
 
936
938
  if (failed) throw failed;
939
+ if (closeError && !sawCompletion) throw closeError;
937
940
  if (!sawCompletion) {
938
941
  throw new Error("WebSocket stream closed before response.completed");
939
942
  }
@@ -1077,7 +1080,7 @@ async function processCapturedResponsesStream<TApi extends Api>(
1077
1080
  await processResponsesStream(tappedEvents as AsyncIterable<never>, output, stream, model, {
1078
1081
  serviceTier: (options as { serviceTier?: ServiceTier } | undefined)?.serviceTier,
1079
1082
  resolveServiceTier: resolveCodexServiceTier,
1080
- applyServiceTierPricing,
1083
+ applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model as Model<Api>),
1081
1084
  });
1082
1085
  }
1083
1086
 
@@ -8,12 +8,10 @@ type Message = Context["messages"][number];
8
8
 
9
9
  interface ImageGenerationCallItem {
10
10
  type: "image_generation_call";
11
- id?: string;
12
- status?: string;
13
- result?: string | null;
14
- output_format?: string;
11
+ id: string;
12
+ status: string;
13
+ result: string | null;
15
14
  revised_prompt?: string;
16
- [key: string]: unknown;
17
15
  }
18
16
 
19
17
  interface ImageGenerationCallBlock {
@@ -76,6 +74,23 @@ function isImageGenerationCallBlock(block: InternalAssistantContent): block is I
76
74
  return block.type === "image_generation_call" && block.item?.type === "image_generation_call";
77
75
  }
78
76
 
77
+ function sanitizeImageGenerationCallItem(item: unknown): ImageGenerationCallItem | undefined {
78
+ if (!item || typeof item !== "object") return undefined;
79
+ const candidate = item as Record<string, unknown>;
80
+ if (candidate.type !== "image_generation_call") return undefined;
81
+ if (typeof candidate.id !== "string" || candidate.id === "") return undefined;
82
+ if (typeof candidate.status !== "string" || candidate.status === "") return undefined;
83
+ if (!(typeof candidate.result === "string" || candidate.result === null)) return undefined;
84
+
85
+ return {
86
+ type: "image_generation_call",
87
+ id: candidate.id,
88
+ status: candidate.status,
89
+ result: candidate.result,
90
+ ...(typeof candidate.revised_prompt === "string" ? { revised_prompt: candidate.revised_prompt } : {}),
91
+ };
92
+ }
93
+
79
94
  const NON_VISION_USER_IMAGE_PLACEHOLDER = "(image omitted: model does not support images)";
80
95
  const NON_VISION_TOOL_IMAGE_PLACEHOLDER = "(tool image omitted: model does not support images)";
81
96
 
@@ -287,7 +302,8 @@ export function convertResponsesMessages<TApi extends Api>(
287
302
  let assistantBlockIndex = 0;
288
303
  for (const block of msg.content as InternalAssistantContent[]) {
289
304
  if (isImageGenerationCallBlock(block)) {
290
- output.push(block.item as ResponseInput[number]);
305
+ const imageGenerationCall = sanitizeImageGenerationCallItem(block.item);
306
+ if (imageGenerationCall) output.push(imageGenerationCall as ResponseInput[number]);
291
307
  } else if (block.type === "thinking") {
292
308
  if (block.thinkingSignature) output.push(JSON.parse(block.thinkingSignature));
293
309
  } else if (block.type === "text") {
@@ -581,10 +597,13 @@ export async function processResponsesStream<TApi extends Api>(
581
597
  stream.push({ type: "toolcall_end", contentIndex: toolCallIndex, toolCall, partial: output });
582
598
  outputStates.delete(event.output_index);
583
599
  } else if (item.type === "image_generation_call") {
584
- (output.content as InternalAssistantContent[]).push({
585
- type: "image_generation_call",
586
- item: item as ImageGenerationCallItem,
587
- });
600
+ const imageGenerationCall = sanitizeImageGenerationCallItem(item);
601
+ if (imageGenerationCall) {
602
+ (output.content as InternalAssistantContent[]).push({
603
+ type: "image_generation_call",
604
+ item: imageGenerationCall,
605
+ });
606
+ }
588
607
  outputStates.delete(event.output_index);
589
608
  }
590
609
  } else if (event.type === "response.completed") {
@@ -613,7 +632,8 @@ export async function processResponsesStream<TApi extends Api>(
613
632
  output.stopReason = "toolUse";
614
633
  }
615
634
  } else if (event.type === "error") {
616
- throw new Error(`Error Code ${event.code}: ${event.message}` || "Unknown error");
635
+ const details = [event.code, event.message].filter(Boolean).join(": ");
636
+ throw new Error(details || "Unknown error");
617
637
  } else if (event.type === "response.failed") {
618
638
  const error = event.response?.error;
619
639
  const details = (event.response as { incomplete_details?: { reason?: string } } | undefined)?.incomplete_details;