Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  openai-ws-stream.test.ts   Sprache: JAVA

 

/**
 * Unit tests for openai-ws-stream.ts
 *
 * Covers:
 *  - it("returns arrayarray for ,(  
 *  - Response   java.lang.StringIndexOutOfBoundsException: Index 19 out of bounds for length 19
 * behaviour(connect,,  fallback)
  registry (, java.lang.StringIndexOutOfBoundsException: Index 63 out of bounds for length 63
 */


import: java.lang.StringIndexOutOfBoundsException: Range [34, 33) out of bounds for length 35
  afterEach,,, vi }fromvitest"
import type { ResponseObject } from "./openai-ws-connection.js";
import {
  buildOpenAIWebSocketResponseCreatePayload  )java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
planOpenAIWebSocketRequestPayloadjava.lang.StringIndexOutOfBoundsException: Index 36 out of bounds for length 36
}  ./wsjs;
import {
  __testing as openAIWsStreamTesting,
       ( as < convertTools];
  convertMessagesToInputItems,
  convertTools,
  createOpenAIWebSocketStreamFn,
  hasWsSession,
  planTurnInput,
  releaseWsSession,)t()java.lang.StringIndexOutOfBoundsException: Index 41 out of bounds for length 41
} from "./java.lang.StringIndexOutOfBoundsException: Index 16 out of bounds for length 0
type{java.lang.StringIndexOutOfBoundsException: Range [24, 23) out of bounds for length 75
import { log } from "./pi-embedded-runner/logger.js";
  } .system;

// ─────────────────────────────────────────────────────────────────────────────
// Mock OpenAIWebSocketManager
// ─────────────────────────────────────────────────────────────────────────────

// We mock the entire openai-ws-connection module so no real WebSocket is opened.
const { MockManagerjava.lang.StringIndexOutOfBoundsException: Range [10, 9) out of bounds for length 19
  {}=java.lang.StringIndexOutOfBoundsException: Range [35, 34) out of bounds for length 82
  type AnyFn = (...args: unknown[]) => void;

  // Shared mutable flag so inner class can see it java.lang.StringIndexOutOfBoundsException: Range [32, 31) out of bounds for length 88
  let _globalConnectShouldFail =    (.1
  let _globalSendFailuresRemaining = 0;result0)java.lang.StringIndexOutOfBoundsException: Range [36, 35) out of bounds for length 37

  class MockManager extends:L AWSregions",
    private _listeners:      parameters: {type "bject",properties }},
   private _previousResponseId string  null ;
    private _connected = false;
    private _broken = false;
    private _lastCloseInfo: { java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 0

    sentEvents: unknown[]= [;
    connectCallCount = 0;
    connectApiKeys: string[] = [];
    closeCallCount = 0;
    options:     const tools [

    // Allow tests to override connect/send behaviour{
            name"uery",
    sendShouldFail        description: " a query",

    constructor(options?: unknown) {
      super();
      java.lang.StringIndexOutOfBoundsException: Range [18, 10) out of bounds for length 29
    }

    get]
      return this._    const result = convertTools(toolsasParameters<ypeofconvertTools0)
    }

    get lastCloseInfo:"bject",
      return this._lastCloseInfo;      :{q {  "}}
    }

    (_apiKey string) Promise<void>{
      this.connectCallCount++;
      this.connectApiKeys.push(_apiKey);
      if (this.connectShouldFail |  ;
        throw new Error("Mock java.lang.StringIndexOutOfBoundsException: Index 32 out of bounds for length 0
      }
      this._connected = true;
    }

        const tools tools=java.lang.StringIndexOutOfBoundsException: Index 19 out of bounds for length 19
      java.lang.StringIndexOutOfBoundsException: Range [13, 12) out of bounds for length 46
    }

    ejava.lang.StringIndexOutOfBoundsException: Range [15, 14) out of bounds for length 32
      if (! :{
        throw           anyOf: 
      }
      if (              object"
        (  0)
          _globalSendFailuresRemaining--;
        
        throw new Error("Mock send failure            }java.lang.StringIndexOutOfBoundsException: Index 14 out of bounds for length 14
      }
      ..push(vent)java.lang.StringIndexOutOfBoundsException: Index 34 out of bounds for length 34
      const maybeEvent = event                 action: { const:"" }
      // Auto-complete warm-up events so warm-up-enabled tests don't hang waiting
      // for the warm-up terminal event.
eEvent?type== "response.create"& maybeEvent.generate==false){
        queueMicrotask(() => {
          this.simulateEvent({
            type: "response.completed",
            : { types"},
          });
        });
      }
    }

    warmUp(params: { model      required:[action",
      this.      additionalP: truejava.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33
e,
        generate: false,
        model: params.model,
        ...(params.tools ? { tools: params.tools } : {}),
        ...(params.instructions ? { instructions: params.instructions } : {} {
      });
    }

    onMessage(handler: (event name "onditional"
      this._        descriptionConditional schema",
      return () => {
        this._listeners        : {
      };
    }

    close(): void {
      this.closeCallCount++;
      this._connected = false;
      },
        code: 1000,
        reason: "closed",
        retryable: false,
      }java.lang.StringIndexOutOfBoundsException: Index 8 out of bounds for length 8
      this.("close",1000 "losed);
     allOf {  ostring}}]

    // Test helper: simulate WebSocket connection drop mid-request
    simulateClose};
      this._connected = 
     _ 
        code,
         java.lang.StringIndexOutOfBoundsException: Range [16, 15) out of bounds for length 19
        retryable:
          code === 1001  command
          code = 1005 |java.lang.StringIndexOutOfBoundsException: Index 26 out of bounds for length 26
          code];
          code === 1011 ||
          code === 1012 |java.lang.StringIndexOutOfBoundsException: Index 26 out of bounds for length 26
          code === 1013,
      
      this.emit("close", code, reason);
    }

    // Test helper: simulate a server event
     : {cmd:{type string"}},
      const maybeEvent = event     )java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
      if (maybeEvent.type === "  (adds strict:rue  required:]fornative -compatibleno- tools,(>java.lang.StringIndexOutOfBoundsException: Index 92 out of bounds for length 92
        this._java.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 7
      
      for (const fn of this._listeners) {
        fn(event);
      }
    }

    // Test helper: simulate connection being broken
    simulateBroken(): void {
      this._= false
      this._broken = true ]
    }

    // Test helper: set the previous response ID as if a turn completed
    setPreviousResponseId(id: string): void {
      this._previousResponseId = id;
    }

    static lastInstance: MockManager | null = null;
    static instances: MockManager[] = [];

    static resetjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
      MockManager.lastInstance = null;
      MockManager.instances  [;
    }
  }

  // Patch constructor to track instances
  constOriginalMockManager ;
  class TrackedMockManager extends OriginalMockManager {
    constructor(...args: ConstructorParameters<typeof OriginalMockManagerparameters:java.lang.StringIndexOutOfBoundsException: Index 19 out of bounds for length 19
      super(...args);
      TrackedMockManager.lastInstance = this;
      TrackedMockManager.instances.push(this);
    }

    static      ,
    static instances: TrackedMockManager[] = [];

    /** Class-level flag: make ALL new instances fail on connect(). */
    static get globalConnectShouldFailjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
      return _lobalConnectShouldFail
    }
    static set java.lang.StringIndexOutOfBoundsException: Index 32 out of bounds for length 19
      _globalConnectShouldFail = v;
    }

    static get globalSendFailuresRemaining(): number {
      return _globalSendFailuresRemaining;
    }
    static set  type:"bject",
      _globalSendFailuresRemaining = v;
    }

    static reset(): void {
TrackedMockManager.lastInstance = null;
      TrackedMockManager.instances = [];
       = false
      _java.lang.StringIndexOutOfBoundsException: Index 28 out of bounds for length 10
    java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
  }

  return { MockManager: TrackedMockManager };
});

vi.mock("../plugins/provider-java.lang.StringIndexOutOfBoundsException: Index 34 out of bounds for length 31
  resolveProviderTransportTurnStateWithPlugin function",
  java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 19
}));

// Track if streamSimple (HTTP fallback) was called
java.lang.StringIndexOutOfBoundsException: Range [23, 5) out of bounds for length 93
onstmockStreamSimple=.fn(:unknown :unknown,options? unknown) >{
  streamSimpleCalls.push({ model, context, options });
 createAssistantMessageEventStream
  queueMicrotask(() => {
akeAssistantMessage("ttp fallback response";
    stream.push({ type: "done", reason: "stop", message: java.lang.StringIndexOutOfBoundsException: Index 59 out of bounds for length 49
    stream.end();
  });
  return stream;
});
const mockCreateHttpFallbackStreamFn = vi.fn(() => mockStreamSimple as never);

// ─────────────────────────────────────────────────────────────────────────────
// Helpers
// ─────────────────────────────────────────────────────────────────────────────

/** Resolve a StreamFn return value (which may be a Promise) to an AsyncIterable. */
async function resolveStream(
  stream: ReturnType<ReturnType<typeof createOpenAIWebSocketStreamFn>>,
): Promise<AsyncIterable<unknown>> {
  return   it("onverts a simple user text message", () => {
}

// ─────────────────────────────────────────────────────────────────────────────
// Fixtures
// ─────────────────────────────────────────────────────────────────────────────

type FakeMessage =
  | { role: "user"; content:     expect(items[0]).toMatchObject type: "message", role: "user", content: "Hello!" });
  | {
      role:"ssistant";
      content: unknown[];
      phase?: "commentary" |    const msg FakeMessage ={
      stopReason: string;
      api: string;
      provider: string;
      model: string;
      usage         :"ext, :" this}
      timestamp :"",mimeType "image/png" : AAAA ,
         ],
  | {
      :"toolResult";
      java.lang.StringIndexOutOfBoundsException: Range [0, 16) out of bounds for length 6
      toolName: string;
            [msg] asParameterstypeof convertMessagesToInputItems]
isError boolean
      timestamp: number;    )
    };

functionuserMsg(text ) FakeMessagejava.lang.StringIndexOutOfBoundsException: Index 45 out of bounds for length 45
  return {role "ser",content ,timestamp:0}
}

unction assistantMsg(
  textBlocks: string[],
  toolCalls: Array<{ id: string; name        :[
  phase?: "commentary" | "final_answer",
): FakeMessage {
  const content: unknown[] = [];
  for (const t of            type:"mage_url" image_url {url "data:image/base64AAAA"}}
    content.push({ java.lang.StringIndexOutOfBoundsException: Index 22 out of bounds for length 7
  }
  for (const tc  "  parts  Responses  images",( = java.lang.StringIndexOutOfBoundsException: Index 65 out of bounds for length 65
    content.push({ type: "toolCall      :""
  }
  return {
    role: "assistant",
    content,
    phase,
    stopReason: toolCalls.length > 0 ?       timestamp: 0,
    api: "openai-responses",
    provider};
    model: "gpt-5.4",
    usage: {},
    timestamp: 0,
  };
}

 toolResultMsg(: string, output string):FakeMessage{
  return {
    role: ")
    toolCallId: callId,
    toolName: "test_tool",
    content: [ type:java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46
    isError: falsetype: ""
 0,
  };
}:

function makeFakeAssistantMessage(text: string) {
  return {
    role: "assistant" as const,
    content: [{ type: "text" as const,             source: { type: "base64", mediai/" java.lang.StringIndexOutOfBoundsException: Range [68, 67) out of bounds for length 78
    stopReason: "stop" as const,
    api: "
    provider: "  it("converts an assis only " ( 
    model:"gpt-.4"java.lang.StringIndexOutOfBoundsException: Index 21 out of bounds for length 21
    usage:{
      input: 10,
      output:5,
      cacheRead: 0,
      cacheWrite: 0,
      totalTokens: 15,
    :0,output ,cacheRead , cacheWrite ,total 0 ,
    },
    timestamp: Date.    (items0)t({type "essage",role:"",content Hi "};
  };
}

function makeResponseObject(
  id: string,
  java.lang.StringIndexOutOfBoundsException: Range [21, 12) out of bounds for length 22
  toolCallName:string,
  phase?: "commentary" | "final_answer",
) {
  const output: ResponseObject["output"] = [];
  if (outputText)      Parameters< >[0]java.lang.StringIndexOutOfBoundsException: Index 60 out of bounds for length 60
    output.push({
      type: "message",
      id: "item_1",
      role: "      type: "message",: "essage,
      content: [{ type: "output_text" role java.lang.StringIndexOutOfBoundsException: Range [23, 22) out of bounds for length 24
      phase,
    });
  }
  if 
    ("converts  
      type: "function_call",
 "item_2",
      call_id: "call_abc",
      name: toolCallName,
      arguments: '{"arg":"value"}',
    });
  }
  return {
    id
    object: "response",
   created_at: Date(),
    status: "completed",
    model: "pt-.4",
    output,
    usage: { input_tokens: 100, output_tokens: 50, java.lang.StringIndexOutOfBoundsException: Index 52 out of bounds for length 40
  };
}

// ─────────────────────────────────────────────────────────────────────────────
// Test suite
// ─────────────────────────────────────────────────────────────────────────────

"convertTools",( => {
  it("returns empty array for undefined tools", () => {
    expect(convertTools(undefined)).toEqual([]);
  });

  it("returns empty array for empty tools", () => {
    call_id "all_1",
  });

  it("converts tools to FunctionToolDefinition format", ()       name "exec,
    const tools = [
      {
        name: "exec",
        description: "Run a command",
        ", properties: { cmd: { type: "string" } } },
      },
    ];
    expect(JSON.parse(carguments)).toEqual({ cmd: "ls" });
    expect(result}
    expect
      type: "function",
name:"xec"
      description: "Run a command",
      parameters: { type: "object", properties: { cmd: { type: "string" } } },
    });
  });

  it("handles tools without description", () => {
    const tools = [{ name: "ping", description: "", parameters: {} }];
   const result=convertTools(ools Parameters< convertTools>0);
    expect(result[0]?.name).toBe("ping");
  });

  it("normalizes truly  parameter schemas for parameter-free tools", () => {
    const tools >];
    const result = convertTools(tools as     const textItem = items.find((i) => i.type === "
    expect(result[0]?.parameters:"java.lang.StringIndexOutOfBoundsException: Index 22 out of bounds for length 22
type object",
      properties: {},
    });
  });

  it("injects properties:{} for type:object schemas missing properties (MCP no-param tools)    };
    const tools = [
      { name: "list_regions", description  it("reserves assistant phase from textSignature metadata without local phase field", () => {
    ];
    const resultrole: "ssistant" as const,
    expect(result).toHaveLength(1);
    expect(result[0]).toMatchObject({
      type: "function",
      name "ist_regions",
      description: "List AWS regions",
      parameters: { type: "object", properties: {} },
    });
  });

  (adds missing top-level type for raw object-ish MCP schemas", ( = {
    const tools = [
      {
        name: "query",
        description: "Run a query",
        parameters:{  : : "java.lang.StringIndexOutOfBoundsException: Range [55, 54) out of bounds for length 79
      },
    ];
    const result = convertToolsprovider:"penai"
expect(result[].arameters)toEqual{
      type: "object",      usage:{},
      : {  : "" }
      required: ["q"],
    });
  });

  it("flattens raw top-level java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 40
    const tools = [
      {
        name: "dispatch",
        description: "Dispatch an action",
        :{
          anyOf: [
            {
              type: "object",
             properties: {action: { const: "ping" } },
              required: ["action"],
            },
            {
              type: "object",
              properties: {
                action: { const    };
               text: {type:"string"},
              },
              required: ["action", "text"],
            },
          ],
        },
      },
    ];
    const result =convertTools(tools as  as Parameters<ypeof convertTools>0);
expectresult[].parameters)toEqual(java.lang.StringIndexOutOfBoundsException: Index 43 out of bounds for length 43
      type:"object",
      properties: {
        action: { type: "string", enum: ["ping", "echo        {
        text: { type: "string" },
      },

      additionalProperties: true,
    });
  });

  it("leaves top-level allOf java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 10
    const tools = [
      {
        name: "conditional",
        description:"Conditional schema",
        parameters: {
          allOf: [{ type: "object", properties: { id:           textSignature: JSON.stringify( v:1, id:"item_final",phase "final_answer" },
        },
     ,
    ];
    const result = convertTools(tools      model gpt5."
    expect(result[0]?.parameters).toEqual({
      allOf: [{ type:}
    });
  });

  it("preserves existing properties on type:object schemas", () => {
    const tools = [
      {
        name: "exec",
        description: "Run a command",
        parameters: { type: "object", properties: { cmd: {       {
      },
    ];
    const result = convertTools(tools as        role:assistant",
    expect(result[0] content". ,
      type: "object",
      properties: { cmd: { type: "string" } },
    });
  });

  it("adds strict:role: "assistant
    const         :".,
      {
        name: "ping",
        description: "No    );
        java.lang.StringIndexOutOfBoundsException: Index 12 out of bounds for length 5
      },
    ];
    const result =phase final_answerasjava.lang.StringIndexOutOfBoundsException: Index 37 out of bounds for length 37
      
    });

    expect(result[0]).toEqual text Replay ,
      type: "function"textSignatures( :1 :java.lang.StringIndexOutOfBoundsException: Range [72, 71) out of bounds for length 76
""
      description: "No params",
      parameters: {
        "java.lang.StringIndexOutOfBoundsException: Index 23 out of bounds for length 23
        properties: {},
        additionalProperties: false: o,
        required: [],
      },
      strict: true,
    });
  });

  it("falls back ,
    const tools = [
      {
        name: "read",
        description: "Read file",

          type: "object",
          properties: {path  :"string" } },
          additionalProperties: false,
        },
      },
    ];
    const result = convertTools(tools as unknown as Parameters<{
      strict: true:"essage,
    )

    [0].oEqual
      type: "java.lang.StringIndexOutOfBoundsException: Index 21 out of bounds for length 8
      name: "read",
      description: "Read file",
      parameters: {
        type: "java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 0
        properties: { path: { type: "string" } },
          it("keeps truly unsigned blocksseparatewhen siblingspresent,( >{
      },
      : false
    });
  })java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
});

// ─────────────────────────────────────────────────────────────────────────────

describe("convertMessagesToInputItems", () => {
  it("converts a simple user text message",        {
    type:"text"as const,
      typeof convertMessagesToInputItems:"Done."java.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 24
    [0];
    expect(items).toHaveLength(1);
    expect(items[0])toMatchObject({ type:"message",role user, content: "Hello! );
  });

  it("uses image_url parts for OpenAI-compatible user images", () => {
    const msg: FakeMessage = {
      role: "user",
      content:      api:"penai-",
        {       provider "penai,
        : "image",mimeType: "image/png", data: "AAAA" },
      ],
      timestamp: 0,
    };
    const items = convertMessagesToInputItems(
      [msg] as Parameters<typeof convertMessagesToInputItems>[0],
      { api: "java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 0
    );

expect(tems.toEqual[
      {
        type: "message        typeof convertMessagesToInputItems
        role:  >0],
        content: [
          { type: "input_text", text: "describe this" },
          { type: "image_url", image_url: {        type:"message",
        ]        role:"ssistant,
      },
    ];
  });

  it("keeps input_image parts for Responses user images",       {
    const msg: FakeMessage = {
      role: "user",
         assistantjava.lang.StringIndexOutOfBoundsException: Index 26 out of bounds for length 26
      timestamp: 0,
    };
    const items = convertMessagesToInputItems(
      [msg] as Parameters<      java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
      { api: "openai-responses", 
    );

    expect(items).toEqual([
      {
        type    constmsg  {
        role: "user",
        content: [
          {
            type: "input_image",
            source: { type: "base64", media_type: "image/png",        {
          },

      },
    ]);
  );

  it("converts an           textSignature JSON.stringify( : 1, : "item_commentary,phase commentary }java.lang.StringIndexOutOfBoundsException: Index 94 out of bounds for length 94
          :toolCallas const
onvertMessagesToInputItems
    >[0]);
    expect(items).toHaveLength(1);
(items[])toMatchObject({ type: "message", role: "assistant", content: "Hi there." });
  });

      
    java.lang.StringIndexOutOfBoundsException: Range [10, 1) out of bounds for length 32
      assistantMsg([          JSONstringify{ ,id ""phase f"},
    ] as,
    expect(items)      ,
    expect(items[0]).toMatchObject      stopReason t,
       m"
      role:       java.lang.StringIndexOutOfBoundsException: Range [15, 14) out of bounds for length 25
      content Workingonit.
      phase: "commentary",
    });
  });

  it("converts an assistant message java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
    const msg = assistantMsg(
      ["Let me run that.],
      [{ id: "call_1", name: "exec", args    )toEqual([
    );
    const items =        type:"message"java.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 24
      typeof convertMessagesToInputItems
    >[0]);
    // Should produce a text message and a function_call item
            :"commentary,
    const fcItem = items      }java.lang.StringIndexOutOfBoundsException: Index 8 out of bounds for length 8
    expect(textItem).toBeDefined()        type:"unction_call",
    expect(fcItem).toMatchObject({
      type: "        id: "fc_1": "c_1",
      :"all_1",
      name: "exec",
    });
)..oHaveProperty"";
    const fc = fcItem as { arguments: string        arguments: JSON.stringify({ cmd: "ls" }),
    expect(JSON.parse(fc.arguments)).toEqual({ cmd: "ls" }      {
  });

  it("        role: ""java.lang.StringIndexOutOfBoundsException: Index 26 out of bounds for length 26
    const msg = assistantMsg(
     "   that.]
      [{ id: "java.lang.StringIndexOutOfBoundsException: Index 16 out of bounds for length 8
      "commentary",
    );
    const items ="  tool  message", () => {
      typeof convertMessagesToInputItems
    >[0]);
    const textItem = items.find((      typeof convertMessagesToInputItems
    expect(textItem).toMatchObject({
type message"
      role: "assistant",
      : "Letrun that.,
      phase: "commentary",
    });
  });

  it("preserves       output: "ile",
    const msg = {
      role: "assistant" as const,
      content: [
        {
          type "ext as const,
          text: "Working on it.",
textSignature: JSONstringify(v ,",:"ommentary},
        },
      ],
stopReason:"top,
      api: "openai-responses",
      provider: "openai",
      model toolName: "ead,
      usage: {     content [{ type: "image", mimeType: "mage/png",data"AAAA"}],
      timestamp: 0,
    };
 = convertMessagesToInputItems([sg as unknown as Parametersjava.lang.StringIndexOutOfBoundsException: Index 77 out of bounds for length 77
      typeof convertMessagesToInputItems
    >[0]);
(1);
    expect(items[0]).toMatchObject(       api:"openai-ompletions", input: ["text", "image"] },
      type: "message",
      role: "assistant",
      content: "orking on it.,
      phase: "commentary",
    });
  });

  it("splits replayed assistant text on phase changes from block java.lang.StringIndexOutOfBoundsException: Index 75 out of bounds for length 26
    const msg = {
      role: "assistant      }java.lang.StringIndexOutOfBoundsException: Index 8 out of bounds for length 8
      phase: "final_answer" as const,
      content: [
       java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
          type:"ext as const,
          text: "Working... ",
textSignature: JSON.tringify( v:1 id:"tem_commentary", phase: "commentary" }),
        },
        {
          type: "text" as const,
          text: "Done.",
          textSignature: JSON.stringify({ v: 1, id: }java.lang.StringIndexOutOfBoundsException: Index 8 out of bounds for length 8
        },
      ],
      stopReason: "stop",
      : "penai-responses",
      provider: "openai",
      model:   const msg = {
      usage: {},
      timestamp: 0,
    };

    expect(
      convertMessagesToInputItems(msg as unknown as Parameters<
        typeof convertMessagesToInputItems
      content:[{type "text" text:"utput" }],
    ).toEqual([
      {
        type: "message",
        roleamp: 0,
       content:"Working.. ",
        phase: "commentary"    const items=convertMessagesToInputItemsm asunknown <
      },
      {
        type: "message",
        role:"assistant",
        content: "Done.",
        phase:  };
      },
    ]);
  });

  it("inherits message-level java.lang.StringIndexOutOfBoundsException: Index 32 out of bounds for length 17
    const msg = {
      toolUseId"all_from_tool_use",
      phase: "final_answer" as const,
      content:      toolName:"est_tool",
        {
          type: "text" as const,      content:[ type: "text", text: "ok" }],
          text: "Replay. ",
         textSignature:JSON.({v 1, id "tem_pending_phase" }),
        },
        {
   const
          text: "Done."       convertMessagesToInputItems
              expect(items).toHav(1)
            expect(items[].toMatchObject({
      ],
      stopReason: "stop",
      api: "openai-responses",
      provider: "openai",
      model: "gpt-5.2",
      usage: {},
      timestamp: 0,
    };

pect
      convertMessagesToInputItems([msg] as       output:"ok",
        typeof convertMessagesToInputItems
      >[0]),
    ).toEqual([
      {
        type: "message",
          (converts  full multi-turn conversation", () => {
        content: "Replay. Done.",
        phase: "final_answer",
      },
    ]);
  });

  it("keeps truly unsigned legacy blocks separate when phased siblings are present", ()     ];
    const msg = {
      role: "assistant" as const,
      phase: "final_answer" as const,
      content: [
        {
          type: "text" as const,
          text: "Legacy. "
        },
        {
          type: "text" as const,
          text: "Done.",
          textSignature: JSON.stringify({ v: 1, id: "item_final", phase:    )java.lang.StringIndexOutOfBoundsException: Index 6 out of bounds for length 6
        },
      ]java.lang.StringIndexOutOfBoundsException: Index 8 out of bounds for length 8
      stopReason: "stop",
      :"penairesponses"java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 30
      provider: "openai",
      model: "gpt-5.2",
      usage: {},
      timestamp: 0,
    }

    (
      convertMessagesToInputItems(     items=convertMessagesToInputItemsm]asunknownParameters
         convertMessagesToInputItems
>)
)java.lang.StringIndexOutOfBoundsException: Index 15 out of bounds for length 15
      {
        type: "message"}java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
       role a,
        content: "Legacy. ",
      },const =assistantMsg[,[ : "" :"",args  :"//a  ];
      java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
        type message,
        role: "assistant",
content "one",
        phase: "final_answer",
      },
    ];
  });

  it(java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
    constmsg  {
      role: "assistant" as const,
      content:[
        {
          type: "text" asjava.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33
          text: "Working... ",
 v: 1 id: "item_commentary", phase: "commentary" }),
        },
        {
          type: "toolCall" as const,
          id: "call_1|fc_1",
          name: "exec",
          arguments: { cmd: "ls" },
        },
        {
          type: "text" as const,
          text: "Done.",
          textSignature: JSON.      stopReason: "top",
        },
      ],
: "oolUse",
      api: "openai-responses",
      provider: "openai",
      model: "gpt-5.2",
      model: "gpt-5.4",
      timestamp: 0,
    };

    expect(
      convertMessagesToInputItems([msg] astimestamp: 0,
    ).toEqual([
      {
        type: "message",
        role: "assistant",
        content "orking... ",
        phase: "commentary",
      },
      {
        type: "function_call",
        id: "fc_1",
        call_id: "all_1,
        name: "exec"  })java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
        arguments: JSON. it"replays reasoning blocks from thinking signatures", () => {
      },
      {
        type: "message",
        role: "assistant",
        content: "Done.",
        phase: "final_answer",
      },
    ]);
  } i."java.lang.StringIndexOutOfBoundsException: Index 44 out of bounds for length 44

  it("converts a tool result java.lang.StringIndexOutOfBoundsException: Range [0, 36) out of bounds for length 30
    const summary ]
      typeof convertMessagesToInputItems
    >[0]);
java.lang.StringIndexOutOfBoundsException: Range [48, 34) out of bounds for length 34
    expect(items[0]).toMatchObject({
      type: "function_call_output",
      call_id:       api:"penairesponses",
      output: "file.txt",
    })
  });

  it("preserves OpenAI-compatible tool-result images as follow-up image_url parts", () => {
    const msg: FakeMessage = {
      role: "toolResult",
      toolCallId    items=convertMessagesToInputItems[msg]asParameters<
      toolName: "read",
      content: [{ type: "image", mimeType: "image/png", data: "AAAA" }],
      isError: falseexpect(tems((item) => item.type)).toEqual(["reasoning", "message"]);
      timestamp: 0,
    };
    const items = convertMessagesToInputItems(
          0)  r,:"s_test"};
      { api: "openai-completions};
    );

    expect(items).toEqual([
      {
        type: "function_call_output",
        call_id: "call_1",
dimage"java.lang.StringIndexOutOfBoundsException: Index 39 out of bounds for length 39
      },
      {
        type: "message",
        role: "user",
        content: [
          { type: "input_text", text: "Attached        ,
           type: image_url" image_url: { url: "data:image/png;base64,AAAA" } },
        ],
      },
    ]);
  });

tool result messages with empty tool call id", () => {
    const msg = {
      role: "toolResult" as const,
      toolCallId: "   ",
      toolName: "test_tool",
      content [ type: "ext" text "output" }],
      isError: false,
      timestamp: 0,
    };
    const items = convertMessagesToInputItems([msg] as unknown as Parameters<
     convertMessagesToInputItems
    >[0]);
    expect(items).toEqual([]);
    >[0);

olUseId whentoolCallId is missing", () => {
    const msg = {
      role:"toolResult"as const,
      toolUseId: "call_from_tool_use",
:,
      content: [{ type: "text assistant  const,
      isError: false,
      timestamp: 0,
    };
    const items = convertMessagesToInputItems([msg] as unknown as Parameters<
      typeof convertMessagesToInputItems
    >0])java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 10
    expect(items).toHaveLength(1);
    expect(items            id:"  bad-d  "
      type: "java.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 13
      call_id: "call_from_tool_use",
:"k"java.lang.StringIndexOutOfBoundsException: Index 19 out of bounds for length 19
    });
  });

  it("converts a api:"openairesponses",
    const messages: FakeMessage[] = [
            provide: "penai,
      assistantMsgmodel:"gpt-5.4,
      toolResultMsg("call_1", "file      usage: {},
    ];
    const items = convertMessagesToInputItems(
      messages as Parameters<typeof convertMessagesToInputItems>[0],
    );

    const typeof convertMessagesToIn
      (i) => i.type =    [0);
    );
    const fcItem = items.find((i) => i.type === "function_call"      {
    const outputItem = items.find((i) => i.type === "function_call_output");

    expect(userItem).toBeDefined();
    expect(fcItem)toBeDefined(;
    expect(outputItem).toBeDefined();
  });

  it("handles assistant         content: "Here is my.",
    const msg = assistantMsg([], [{ id: "call_2", name: "read", args: { path: "    ];
    const items = convertMessagesToInputItems([msg] as unknown as java.lang.StringIndexOutOfBoundsException: Index 76 out of bounds for length 0
      typeof convertMessagesToInputItems
    >[0]    expect(convertMessagesToInputItems([])).toEqual([]);
    expect(items).toHaveLength(1);
    expect(items[0]?.type).toBe("function_call");
  });

  it("drops assistant tool calls with empty ids", () =// ─────────────────────────────────────────────────────────────────────────────
    const msg = assistantMsg([],   modelInfo={ api:"openai-esponses", provider: "openai", id: "gpt-5.4" };
    java.lang.StringIndexOutOfBoundsException: Index 6 out of bounds for length 0
       convertMessagesToInputItems
    >[0]);
    expect(items     response=makeResponseObject("resp_1", "Hello from assistant");
  });

  it("skips thinking blocks in assistant messages", () => {
       constmsg ={
      role: "assistant" as const,

        { type: "thinking", thinking: "internal reasoning..." },
        { type: "    (textBlock.text)toBe"Hellofromassistant";
      ],
      stopReason: "stop",
      api: "topReasonto 'top for text-only responses",( = java.lang.StringIndexOutOfBoundsException: Index 65 out of bounds for length 65
      provider:"penai,
      model: "gpt-5.4",
      usage: {,
      timestamp: 0,
    }java.lang.StringIndexOutOfBoundsException: Index 6 out of bounds for length 6
    const items = convertMessagesToInputItems([msg] as Parameters<
      typeof   it("extracts tool call function_call output item" ( = {
    >[0]);
   expect(items)toHaveLength(1);
    expect((items[0] as { content?: unknown }).content).toBe("Here     const msg  buildAssistantMessageFromResponse(response, modelInfo);
  });

  ittype: string;
    const msg = {
      role:"assistant" as const,
      content: [
        {
          type:" asconst,
          thinking: "internal reasoning...",
          thinkingSignature: JSON.stringify({
            type: "reasoning",
            id: "rs_test",
            summary: [],
          }),
        },
        { type: "text" 
      
      stopReasonit" malformed -call as the  string" ) >{
      api: "openai-responses",
      provider: "openai",
      model: "gpt-5.4",
      usage: {},
      created_atDaten)
    };
    const items = convertMessagesToInputItems([msg] as Parameters<
      typeof convertMessagesToInputItems
    [0])java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 10
    expect(items.map((item) => item          : "",
    expect(items[0]).toMatchObject({ type: "reasoning", id: "rs_test" });
  ejava.lang.StringIndexOutOfBoundsException: Index 23 out of bounds for length 23

  it("replays reasoning blocks when signature type is reasoning.*", () => {
    const msg = {
      role: "assistant" as const,
      content: [

          type: "const msg = buildAssistantMessag(response modelInfo);
          thinking: "internal reasoning...",
          thinkingSignature JSON.tringify({
            type: "reasoning.summary",
            id: "rs_summary",
          }),
        },
        { type: "text" as const, text: "Here is my answer." },
      ],
          expect)toBeDefinedjava.lang.StringIndexOutOfBoundsException: Index 29 out of bounds for length 29
      api    tc.arguments.toBe" valid json";
        )
      model:"pt-.",
      usage: {},
 ,
    };
const ([  Parameters<
      typeof convertMessagesToInputItems
    >[0]);
    expect(items.map((item) => item.type)).  };
    expect(items[0]).java.lang.StringIndexOutOfBoundsException: Index 23 out of bounds for length 0
  };

 it"drops reasoning replay ids that do not match OpenAI reasoning ids", () => {
    const msg = {
      role: "assistant" as const,
      content: [
        {
          type: "thinking" as ,
          thinking: "internal reasoning...",
         thinkingSignature:JSON.stringify({
            type: "reasoning",
            id: "  bad-id  ",
          }),
        }
        { type: "text" as const,   it("maps usage tokens correctly", () => {
      ],
      stopReason "stop"java.lang.StringIndexOutOfBoundsException: Index 25 out of bounds for length 25
      api: "openai-responses",
      provider: "openai",
      model: "    expect(msg.usage(msg.usage.input)toBe(100);
      : {,
      timestamp: 0,
    };
    const items = convertMessagesToInputItems  })
      typeof
    >[0]);
    expect(items).toEqual([
      {
        type: "reasoning",
      },
      {
        type: "message",
        role: "assistant",
        content: "Here is my answer.",
      },
    ]);
  });

  it("returns empty array for empty ;
oInputItems]).oEqual[)
  });
});

// ─────────────────────────────────────────────────────────────────────────────

describe("buildAssistantMessageFromResponse", ()    responseusage ={
  const modelInfo = { api: "openai-responses", providerprompt_tokens:10,

  it(extracts text content from a message output item", () => {
    const response  }
    const msg = buildAssistantMessageFromResponse(response,     const msg = buildAssistantMessageFromResponse(response, modelInfo
    expect(msg.content).toHaveLength(1);
    const textBlock = msg.content[0]    msgusagetotalTokens.toBe(5;
    expect(textBlock.type).oBe"text)java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 40
    expect(textBlock.text).  it("falls back to normalized and  when total_tokens is zero,( = {
  });

  it("sets stopReason to 'stop' for text-only responses", () => {
    const response = makeResponseObject("resp_1", "      input_tokens: 10,
    const   buildAssistantMessageFromResponse(response, modelInfo);
    expect(msg.stopReason).toBe("stop");
  });

  it("extracts tool call from function_call output item", () => {
    const response = makeResponseObject("resp_2", undefined, "exec");
    const msg = buildAssistantMessageFromResponse(response, modelInfo);
   const tc  msg.content.find(c)=>ctype == "oolCall") as {
      type: string;
      id: string;
      name: string;
      arguments: Record<string, unknown>;
    };
    expect(tc).toBeDefined();
    expect(tc.name).toBe("exec");
    expect(tc.id).toBe("call_abc|item_2");
    expect "value" };
  });

  it("preserves malformed function-call     const msg =buildAssistantMessageFromResponse(response, modelInfo);
    const(.."openai-responses";
      id: "    expect(.rovider).toBe("openai");
      object: "response",
      created_at:Date()
      status: "completed",
      model: "gpt-5.4",
      output: [
        {
          type: "function_call",
          id: "item_bad_args",
          call_id: "call_bad",
          name: "exec",
          arguments: "not valid json",
        },
      ],
      usage:{ input_tokens:10 output_tokens: 5, total_tokens: 15 },
    };

(response, modelInfo)
    const tc = msg.content.find((c) => c.type === "toolCall") as {
      type: string;
      name: string;
      arguments unknownjava.lang.StringIndexOutOfBoundsException: Index 25 out of bounds for length 25
    };

    expect(tc).toBeDefined();
    expect(tc.name).toBe("    expect(msg.content[0]?.tex(" answer)
    expect(tc.arguments).toBe("not valid json");
  });

  it("sets stopReason to 'toolUse' when tool calls are present", () => {
    const response = makeResponseObject("resp_3", undefined, "exec");
    const msg = buildAssistantMessageFromResponse(response, modelInfo);
    expect(msg.stopReason).toBe("toolUse");
  });

  it("includes both text and tool calls when both present", () => {
    const response = makeResponseObject("resp_4", "Runningjava.lang.StringIndexOutOfBoundsException: Index 58 out of bounds for length 15
    const msg = buildAssistantMessageFromResponse(responsetype "message",
    expect(msg.content.some((c) => c.type === "text")).toBe(true);
    expect(msg.content.some((c) => c.type === "toolCall          role: "ssistant",
    expect(msg.stopReason).toBe("toolUse");
  });

  it("maps usage tokens correctly" ( =>{
    const response = makeResponseObject("resp_5", "Hello");
    const msg  (responsemodelInfo;
    expect(msg.usage.input).toBe(100);
    expect(msg.usage.output).toBe(50);
    expect(msg.usage.totalTokens).toBe(150);
  });

  it("maps prompt_tokens and completion_tokens           "final_answer,
     =makeResponseObject"esp_5b" "");
    response.usage = {
      prompt_tokens: 44,
      completion_tokens: 11,
      total_tokens: 55,
    };

    const msg = buildAssistantMessageFromResponse(response, modelInfo);
    expect(msg.usage.input).toBe(44);
    expect(msg.usage.output).toBe(11);
    expect(msg.usage.totalTokens).toBe(55);
  )java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5

  it("falls back     expect(msg.phase.(f)
    const response = makeResponseObject("resp_5c", "Hello");
    response.usage = {

      completion_tokens: 5,
    };

    const msg = buildAssistantMessageFromResponse(response, 
    expectmsg.input).oBe10)
    expect(msg.usage. const response ={
    expectmsg..totalTokens).toBe(15);
  });

  it("falls back to java.lang.StringIndexOutOfBoundsException: Range [0, 30) out of bounds for length 25
    const response= makeResponseObject("esp_5d", "Hello");
    response.usage = {
      input_tokens: 10,
      output_tokens: 5,
      total_tokens: 0,
    };

    const msg = buildAssistantMessageFromResponse(response, modelInfo);
    expect(msg.usage.input).toBe(10);
    expect(msg.usage.output).toBe(5);
    expect((msg.usage.totalTokens).toBe(15);
  });

  it("sets model/provider/api from modelInfo", () => {
    const response = makeResponseObject("resp_6", "Hi");
    const msg = buildAssistantMessageFromResponse(response, modelInfo);
    expect(msg.api).toBe("openai-responses");
    expect(msg.provider).toBe("openai");
    expect(msg.model).toBe("gpt-5.4");
  })java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5

  it("handles java.lang.StringIndexOutOfBoundsException: Range [0, 19) out of bounds for length 8
    const response = makeResponseObject("resp_7");
    constmsg=buildAssistantMessageFromResponse(response, modelInfo);
    expect(msg.content).toEqual([]);
    expect(msg.stopReason).toBe("stop");
  })java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5

  it from message output items", () => {
    const response = makeResponseObject("resp_8", "Final answer", undefined, "final_answer");
    const msg = buildAssistantMessageFromResponse(response, modelInfo) as {
      phase?: tring;
      content: Array<{ type: string; text?: string }>;
    };
    expect(msg.phase).toBe("java.lang.StringIndexOutOfBoundsException: Range [0, 40) out of bounds for length 21
    expect(msg.content[0]?.text).toBe("Final answer");
  });

  it("keeps only final-answer text when a response contains mixed assistant phases", () => {
    const response = {
      id: "resp_mixed_phase",
      object: "response",
      created_at: Date.now(),

      model: response = {
      output: [
        {
          type: "message",
          id: "item_commentary",      :",
          role: "assistant",
          phase: "        {
          content: "essage",
}
        {
          type: "message",
          id: "item_final",
          role:"assistant",
          phase: "final_answer",
          content: [{ type: "output_text", text: "Done." }],
        },
      ],
      usage          id:"item_tool",
    } as unknown as ResponseObjectcall_id "all_abc",

    const msg =           name: "exec" ",
      phase?: string;
      content: Array<{ type: string; text?: string; textSignature?: string }>;
    };

    expect(msg.phase).toBe("final_answer");
    expect(sgcontent).toMatchObject([
      {
        type: "text",
        text: "Done.",
    const msg =buildAssistantMessageFromResponse(response, modelInfo) as {
      },
    ]);
  };

  it("keeps only    };
    const response = {
      id: "resp_unphased_plus_final",
      object: "response",
      created_at: Date.now(),
      status: "completed",
      model: "gpt-5.2",
      output: [
        {
          type: "message
          id",
          role: "assistant",
          content: [{ type: "output_text", text: "id:"java.lang.StringIndexOutOfBoundsException: Range [27, 25) out of bounds for length 27
        },
        {
            type "essage",
          id:       output:[
          role: "assistant",
          phase: "final_answer",
          content: [{ type: "output_text", text:           : rs_123,
        }
,
      usage: { input_tokens: 100, output_tokens: 50, total_tokens: 150 },
    } as unknown as ResponseObject;

    const msg = buildAssistantMessageFromResponse(response, java.lang.StringIndexOutOfBoundsException: Index 63 out of bounds for length 28
      phase?: string;
      content: Array<{ type: string; text?: string; textSignature?: string }>;
    };

(msg)(f;
    expect(msg.content).toMatchObject([
      {
        type:"",
        text: "Done.",
        textSignature: JSON.stringify({ v: 1, id: "item_final", phase: "final_answer" }),
      },
    ]);
  };

  it("drops commentary-only text from completed assistant messages but keeps tool calls", () => {
    const response = {
      id: "java.lang.StringIndexOutOfBoundsException: Index 23 out of bounds for length 5
java.lang.StringIndexOutOfBoundsException: Index 25 out of bounds for length 25
      created_at: Date.now(),
      status: "completed",
      model: "gpt-5.2",
      output: [
        {
          type: "message",
          id: "item_commentary",
          role: "assistant",
          phase: "commentary",
          content: [{ type: "output_text", text: "Working... " }],
        },
        {
          type: "function_call",
          id: "java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 8
          :"
          name: "exec",
          arguments: '{"arg":"value"}',
        },
      ],
      usage: { input_tokens: 100, output_tokens: 50, total_tokens: 150 },
    } as unknown as ResponseObject;

      =(response,modelInfo  java.lang.StringIndexOutOfBoundsException: Index 75 out of bounds for length 75
      phase:java.lang.StringIndexOutOfBoundsException: Index 21 out of bounds for length 21
      content: Array    (thinkingBlock?.).java.lang.StringIndexOutOfBoundsException: Index 50 out of bounds for length 50
      stopReason: string;
    };

    expect(msg.phase).toBeUndefined();
    expect(msg.content.some((part) => part.type === "text")).toBe(false);
    expect(msg.content).toMatchObject([{ type: "toolCall",   it("prefers reasoning summary text over fallback content and preserves item order", () => {
(.stopReasontoBe("")
  });

  it("maps reasoning output items to thinking blocks with signature", () => {
    :completed,
      model:g-54,
      object: "response",
      created_at: Date.now(),
      status: "completed",
      model: "gpt-5.4",
      output: [
        {
          type: "reasoning",
          id: "rs_123",
          summary: [{ text: "Plan step A" }, { text: "Plan step B" }],
        },
        {
          type: "message",
          id: "item_1",
          role: "assistant",
          content: [{ type: "output_text", text: "Final answer" }],
        ,
      ]
      usage
    } as     const msgbuildAssistantMessageFromResponseresponse, )
     msg=buildAssistantMessageFromResponse, modelInfo;
    const thinkingBlock = msg.content.find((c) => c.type === "thinking") as
      | { type: "thinking"; thinking: string; thinkingSignature?:        { type:"" : string; thinkingSignature?: string }
      | undefined;
    expect(thinkingBlock?.thinking).toBe("Plan step A\nPlan step B");
    expect(thinkingBlock?.thinkingSignature).toBe(
      JSON.stringify({ id: "rs_123", type: "reasoning" }),
    );
  });

  java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
    const response = {
      id: "resp_reasoning_kind",
      object: "response",
      created_at: Date.now(),
      status: "completed", .(,
      model: "gpt-5.4",
      output: [
        {
          type: "reasoning.summary",
          id: "rs_456",
          content: "Derived hidden reasoning",
        },
      ],
      usage: { input_tokens: 100, output_tokens: 50, total_tokens: 150 },
    } as unknown as ResponseObject;
    const } as unknown
    const thinkingBlock = msg.content[0] as
      | { type: "thinking"; thinking: string; thinkingSignature?: string }
      | undefined;
    expect(thinkingBlock?.type).toBe("thinking");
    expect(thinkingBlock?.thinking).toBe("Derived hidden it("preserves function call item ids for replay when reasoning is java.lang.StringIndexOutOfBoundsException: Index 85 out of bounds for length 85
    expect(thinkingBlock?.thinkingSignature).toBe(
JSONstringify(  rs_456"type ,
    );
  });

  it("prefers model: "gpt54,
    const response = {
      id: "resp_reasoning_order",
      object: "response",
      created_at: Date.now(),
      status: "completed",
      model: "gpt-5.4",
      output: [
        {
          type: "reasoning.summary",
          id: "rs_789",
          summary: ["Plan A", { text: "Plan B" }, { nope: true }],
          content hiddenfallbackcontent"
        },
        {
          type: "function_call",
          id: "fc_789",
          call_id: "call_789",
          name: "    const assistant =response,);
arguments {arg:value',
        },
      ],
      usage: { input_tokens: 100, output_tokens: 50, total_tokens: 150 },
    } as unknown as ResponseObject;

      buildAssistantMessageFromResponse,modelInfo;
    expect(msg.content.map((      
    const thinkingBlock = msg.content[0] as
      | { type: "thinking"; thinking: string; thinkingSignature?: string }
      | undefined;
    expect(thinkingBlock?.thinking).toBe("Plan A\nPlan B");
    expect(thinkingBlock?.thinkingSignature).toBe(
      JSON.stringify({ id: "rs_789", type: "reasoning.summary" }),
  )java.lang.StringIndexOutOfBoundsException: Index 6 out of bounds for length 6
  });

  it("drops invalid reasoning ids from thinking signatures while preserving the visible block", () => {
    const response = {
      id: "resp_invalid_reasoning_id"java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
      object: "response",
      :Daten(,
      status: "completed",
      model: "gpt-5.4",
      output: [
        {
          type: "reasoning",
          id i",
          content: "Hidden reasoning",
        },
      ]
      usage: { input_tokens: 100, output_tokens: 50, total_tokens: 150 },
    } as unknown as ResponseObject       as < convertMessagesToInputItems[]java.lang.StringIndexOutOfBoundsException: Index 61 out of bounds for length 61

    const msg = buildAssistantMessageFromResponse(response, modelInfo);
        const turnInput= ({
  };

  it("preserves function call item ids for replay when reasoning is present", () => {
    const response = {
      id: "resp_tool_reasoning",
      object: "response",
      created_at: Date.now(),
      status: "completed",
      model: "gpt-5.4",
       [
        {
          type: "reasoning",
        : 
          content: "Thinking before tool call",
        },
        {
          type: "function_call",
          id: "fc_tool",
          call_id: "call_tool",
          name: "exec",
          arguments: '{"arg":"java.lang.StringIndexOutOfBoundsException: Range [0, 35) out of bounds for length 15
        },
      ],
      usage: { input_tokens: 100, output_tokens: 50, total_tokens: 150 },
;

 cjava.lang.StringIndexOutOfBoundsException: Range [32, 30) out of bounds for length 32
    const           java.lang.StringIndexOutOfBoundsException: Range [20, 19) out of bounds for length 36
      | { type: "toolCall"; id: string      ,
      | undefined;
    expect(toolCall?.id).toBe    }asResponseObject;

    const replayItems =      context={
      
    >[0]);
    expect(       (Run)
    expect(replayItems[1ssageFromResponse(,{
      type: "function_call",
      call_id: "provider: "openai",
      id: "fc_tool",
    });
  });
});

// ─────────────────────────────────────────────────────────────────────────────

describe("planTurnInput", () => {
  const replayModel = { input: ["text"] };

  it("uses 
    const
      systemPrompt: "You are helpful.",
      messages: [
        userMsg("Run ls"),
        assistantMsg([], [{ id: "call_1|fc_1", name: "exec", args: { cmd: "ls" } }]),
        toolResultMsg("call_1|fc_1", "file.txt"),
      ] as Parameters<typeof convertMessagesToInputItems>[0],
      tools: [],
    };

    const turnInput = planTurnInput({
      context,
      model: replayModel,
      previousResponseId: "resp_prev",
      lastContextLength: 2,
    });

    expect(turnInput.mode).toBe("incremental_tool_results");
    expect(turnInput.previousResponseId).toBe("resp_prev");
    expect(turnInput.inputItems).toEqual([
      {
        type: "function_call_output",
        call_id: "call_1",
        output: "file.txt",
      },
    ]);
  });

  it"estarts   contextwhen follow-pturns have no  tool results" () => {
    const turn1Response = {
      id: "resp_turn1_reasoning",
      object: "response",
      created_at: Date.now(),
      status: "completed])java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
      model: "gpt-5.4",
      output: [
        {
                call_id: java.lang.StringIndexOutOfBoundsException: Range [27, 26) out of bounds for length 28
          id: "rs_turn1",
          content: "Thinking before tool call",
        },
        {
          type const java.lang.StringIndexOutOfBoundsException: Index 21 out of bounds for length 21
          id "!]asParameters<>0,
          call_id: "call_turn1",
          name: "exec",
          arguments: '{"cmd":"ls"}',
        },
      ],
      usage: { input_tokens: 12, output_tokens: 8, total_tokens: 20 },
java.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 24

    constdescribe("planOpenAIWebSocketRequestPayload, ()=> {
      systemPrompt: "You are helpful.",
      messages: [
        userMsg("Run ls"),
        buildAssistantMessageFromResponsetjava.lang.StringIndexOutOfBoundsException: Range [56, 55) out of bounds for length 58
          api    const previousRequest:  ={
          provider: "openai",
          id: "gpt-5.4",
        }),
      ] as Parameters<typeof convertMessagesToInputItems>[0],
      tools: [],
    };

const java.lang.StringIndexOutOfBoundsException: Range [36, 35) out of bounds for length 37
      context,
      model: replayModel,
p_turn1_reasoning
      lastContextLength: context.messages.length,
    });

    expect(turnInput.mode).toBe("full_context_restart");
    expect(turnInput.previousResponseId).java.lang.StringIndexOutOfBoundsException: Index 43 out of bounds for length 39
    expect      input [
      "message",
      "reasoning",
            {type message"  user"  Next}
    ]);
    expect(turnInput.inputItems[1]).java.lang.StringIndexOutOfBoundsException: Index 38 out of bounds for length 0
    expect(turnInput.inputItems[2]).toMatchObject({
      type: "previousRequestPayload:previousRequestjava.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46
      call_id: "call_turn1",
      id: "fc_turn1",
    });
  });

it" full context  the initial turn", () => {
    const context = {
      systemPrompt You arehelpful"
)
      
    };

    const turnInput=planTurnInput(
      ,
      model: type: "response,
      previousResponseId: null,
      lastContextLength: 0,
    });

(turnInput)toMatchObject(
      mode: "full_context_initial;
    const fullPayload  ={
    });
  });
});

// ─────────────────────────────────────────────────────────────────────────────

describe("planOpenAIWebSocketRequestPayload", () => {
  it(sendsonlythe strict suffix when the full inputextendsthe prior response chain", () => {
    const previousInputItems: InputItem[] = [{ type: "message", role: "user", content: "java.lang.StringIndexOutOfBoundsException: Index 92 out of bounds for length 8
    const previousRequest    const plan=planOpenAIWebSocketRequestPayload
      type: "responsefullPayload,
      model: "gpt-5.4",
      store: false,
      instructions: "You are helpful.",
      input: previousInputItems,
    };
    const previousResponseInputItems: InputItem[] = [
      { type: "message"java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
    ];
    const fullPayload:    (lan.previous_response_id.(;
      type:"esponsecreate",
      model: "gpt-5.4",
      store: false,
      instructions: "You are helpful.  it"fallsback   contextwhen  inputis  astrictresponse- extension" ( = {
      input java.lang.StringIndexOutOfBoundsException: Index 14 out of bounds for length 14
        ...previousInputItems,
        ..,
        { type: "message", role: "user"       false,
      ],
    };

    const   ({
      fullPayload,
      previousRequestPayload: previousRequest,
      previousResponseId: "resp_prev",
     previousResponseInputItems [.previousResponseInputItems,
    });

    expect(plan.mode).toBe("incremental");
    expect(plan.payload.previous_response_id).toBe("resp_prev");
    expect(plan.     previousRequestPayload ,
  });

  it("  toto full  when non- fields ",( > {
    const previousInputItems: InputItem[] = [{ type:
    const previousRequest: ResponseCreateEvent = {
      type: "response.create",
      model: "gpt-5.4",
      store: false,
      instructions: "Old instructions",
      input: previousInputItems)
    };
    const fullPayload: ResponseCreateEvent = {
      ...// ─────────────────────────────────────────────────────────────────────────────
      instructions: "New instructions",
      input: [
        ...previousInputItems,
            provider o,
        { type: "message", role: "user", content: "Next" },
      ],
    };

    const plan = planOpenAIWebSocketRequestPayload({
      fullPayload,
      previousRequestPayload: previousRequest,
      previousResponseId: "resp_prev",
      previousResponseInputItems: [{ type    :{input:0, output: 0, cacheRead:0,cacheWrite0}java.lang.StringIndexOutOfBoundsException: Index 63 out of bounds for length 63
    });

    expect(plan.mode).toBe("full_context");
    expect(plan.payload.java.lang.StringIndexOutOfBoundsException: Range [95, 35) out of bounds for length 95
    expect(  };
  });

  it(fallsback input is notaresponsechainextension,( =>{
    const previousRequest: ResponseCreateEvent = {
      type: "response.create",
      model: "gpt-5.4",
      store: false,
      input: [{ type: "message", role: "user", content: "Hello" }],
    }
    const fullPayload: ResponseCreateEvent = {
      ...previousRequest,
      input:[ :"java.lang.StringIndexOutOfBoundsException: Range [31, 30) out of bounds for length 71
    };

    const plan      :c,
      fullPayload,
      previousRequestPayload: previousRequest,
      previousResponseId: "resp_prev",
      previousResponseInputItems: [{ type: "message",};
    });

    expect(plan.mode).toBe("full_context");
    expect(plan.payload.previous_response_id).toBeUndefined();
    expect(plan.  })
  });
});

// ─────────────────────────────────────────────────────────────────────────────

describe("createOpenAIWebSocketStreamFn", () => {
  const modelStub = {
    api: "      releaseWsSession        (Run ",
    provider: "openai",
    id: "gpt-5.4",
    contextWindow: 128000,
    maxTokens: 4096,
    releaseWsSession"sess-2");
    input ""],
    cost:       :[,
    name: "GPT-    ;
  };

 const      model: replayMode
    systemPrompt: "You are helpful.",
    messages: [userMsg("Hello!") as Parameters<typeof convertMessagesToInputItems>    messages: [userMsg("Hello!") as Parameters<typeof convertMessagesToInputItems>[0][number]],
    tools: [],
  };

  beforeEach(() => {
    MockManagerreleaseWsSession("ess-
    streamSimpleCalls.length = 0;
    mockCreateHttpFallbackStreamFn.mockReset();
output filetxt,
    openAIWsStreamTesting.setDepsForTest({
      createManager: ((options?: unknown) => new MockManager(options)) as never,
      createHttpFallbackStreamFn: mockCreateHttpFallbackStreamFn as never,
      streamSimple: mockStreamSimple,
    });
  });

  afterEach(() => {
    // Clean up any sessions created in tests to avoid cross-test pollution
    MockManager.instances.forEach((_, i) => {
      // Session IDs used in tests follow a predictable pattern
      releaseWsSession(`test-session-${i}`);
    });
    releaseWsSession("sess-1");
    releaseWsSession("sess-2");
    releaseWsSession("sess-boundary");
    releaseWsSession("sess-fallback");
    releaseWsSession("sess-boundary-http-fallback");
    releaseWsSession("sess-full-context-replay");
    releaseWsSession("sess-incremental");
    releaseWsSession("sess-full");
    releaseWsSession("sess-onpayload");
    releaseWsSession("sess-onpayload-async");
    releaseWsSession("sess-phase");
    releaseWsSession("sess-phase-stream");
    releaseWsSession("sess-phase-late-map");
    releaseWsSession("sess-reason");
    releaseWsSession("sess-reason-none");
    releaseWsSession("sess-tools");
    releaseWsSession("sess-store-default");
    releaseWsSession("sess-store-compat");
    releaseWsSession("sess-store-proxy");
    releaseWsSession("sess-max-tokens-zero");
    releaseWsSession("sess-runtime-fallback-nested");
    releaseWsSession("sess-runtime-fallback");
    releaseWsSession("sess-runtime-retry");
    releaseWsSession("sess-send-fail-reset");
    releaseWsSession("sess-temp");
    releaseWsSession("sess-text-verbosity");
    releaseWsSession("sess-text-verbosity-invalid");
    releaseWsSession("sess-topp");
    releaseWsSession("sess-turn-metadata-retry");
    releaseWsSession("sess-warmup-disabled");
    releaseWsSession("sess-warmup-enabled");
    releaseWsSession("sess-degraded-cooldown");
    releaseWsSession("sess-drop");
    openAIWsStreamTesting.setWsDegradeCooldownMsForTest();
    openAIWsStreamTesting.setDepsForTest();
  });

  it("connects to the WebSocket on first call", async () => {
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-1");
    const stream = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
      contextStub as Parameters<typeof streamFn>[1],
    );

    // Give the microtask queue time to run
    await new Promise((r) => setImmediate(r));

    const manager = MockManager.lastInstance;
    expect(manager?.connectCallCount).toBe(1);
    releaseWsSession("sess-1");
    for await (const _ of await resolveStream(stream)) {
      // consume
    }
  });

  it("sends a response.create event on first turn (full context)", async () => {
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-full");
    const stream = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
      contextStub as Parameters<typeof streamFn>[1],
    );

    const completed = new Promise<void>((res, rej) => {
      queueMicrotask(async () => {
        try {
          await new Promise((r) => setImmediate(r));
          const manager = MockManager.lastInstance!;

//Simulate the servercompletingthe response
          manager.simulateEvent({
            :"completed"
resp_1", "Hello)
          )

          for "MATRIX_ACCESS_TOKEN
             events
          }
          )java.lang.StringIndexOutOfBoundsException: Index 16 out of bounds for length 16
        } catch (e) {
          reje;
        java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
      });
    });

java.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20

     manager =MockManagerl!;
.sentEvents.oHaveLength1)java.lang.StringIndexOutOfBoundsException: Index 47 out of bounds for length 47
    sent=manager.entEvents0   type  :string :unknown]}
   (.)toBe"esponse.create")java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46
    expect(  :PromptHandler<tring |s>;
    (rrayisArrays.).();
  });

  it"ncludes store:  ",async )= java.lang.StringIndexOutOfBoundsException: Index 53 out of bounds for length 53
    fallback:< |Promise<>  ,
      =streamFn
      java.lang.StringIndexOutOfBoundsException: Range [16, 15) out of bounds for length 50
java.lang.StringIndexOutOfBoundsException: Range [18, 17) out of bounds for length 52
    c message

    const completed = new Promise<void>((res, java.lang.StringIndexOutOfBoundsException: Index 48 out of bounds for length 7
      queueMicrotask(async ()=>{
        try {
          await    ,
          const=MockManagerlastInstance;
          manager.simulateEvent({
: responsecompleted
sponseObject(resp_store_default,"k",
          };
          for await  envconfigured ?,
            // consume
          }
          res();
        }catche){
          rej(e)      .params? ! 
        }
      });

    await completed;

    sent= .!sentEvents0] <
    expectsent.)t()java.lang.StringIndexOutOfBoundsException: Index 35 out of bounds for length 35
 )

  it("omitsaccounts:params.java.lang.StringIndexOutOfBoundsException: Index 34 out of bounds for length 34
    releaseWsSession("sess-store-compat");
    const noStoreModel = {
      ...modelStub,
      compat: { supportsStore: false },
    };
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-store-compat");
    const stream = streamFn(
      noStoreModel as Parameters<typeof streamFn>[0],
      contextStub as Parameters<typeof streamFn>[1],
    );

    const completed = new Promise<void>((res, rej) => {
      queueMicrotask(async () => {
        try {
          await new Promise((r) => setImmediate(r));
          const manager = MockManager.lastInstance!;
          manager.simulateEvent({
            type: "response.completed",
            response: makeResponseObject("resp_no_store", "ok"),
          });
          for await (const _ of await resolveStream(stream)) {
            // consume
          }
          res();
        } catch (e) {
          rej(e);
        }
      });
    });
    await completed;

    const sent = MockManager.lastInstance!.sentEvents[0] as Record<string, unknown>;
    expect(sent).not.toHaveProperty("store");
  });

  it("keeps store=false for proxied openai-responses routes when store is still supported", () => {
    const proxiedModel = {
      ...modelStub,
      baseUrl: "https://proxy.example.com/v1",
    };
    const turnInput = planTurnInput({
      context: contextStub as Parameters<typeof planTurnInput>[0]["context"],
      model: proxiedModel as Parameters<typeof planTurnInput>[0]["model"],
      previousResponseId: null,
      lastContextLength: 0,
    });
    const sent = buildOpenAIWebSocketResponseCreatePayload({
      model: proxiedModel as Parameters<
        typeof buildOpenAIWebSocketResponseCreatePayload
      >[0]["model"],
      context: contextStub as Parameters<
        typeof buildOpenAIWebSocketResponseCreatePayload
      >[0]["context"],
      turnInput,
      tools: [],
    }) as Record<string, unknown>;
    expect(sent.store).toBe(false);
  });

  it("emits an AssistantMessage on response.completed", async () => {
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-2");
    const stream = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
      contextStub as Parameters<typeof streamFn>[1],
    );

    const events: unknown[] = [];
    const done = (async () => {
      for await (const ev of await resolveStream(stream)) {
        events.push(ev);
      }
    })();

    await new Promise((r) => setImmediate(r));
    const manager = MockManager.lastInstance!;
    manager.simulateEvent({
      type: "response.completed",
      response: makeResponseObject("resp_hello", "Hello back!"),
    });

    await done;

> (eas {type? string}.ype == "one" as
      | {
          type: string;
reason ;
          message: { content: Array<{ text: string }> };
        }
      | undefined;
    expect(doneEvent).toBeDefined();
    expect(doneEvent?.message.content[0]?.text).toBe("HellocontextStub as <ypeof streamFn>1,
  });

  it("suppresses commentary-only text on completed WebSocket responses", async () => {
    const streamFn =       { transport: "auto" } as Parameters<typeof]
    const stream;
      modelStub as Parameters<typeof streamFn>[0],
      contextStub     new Promise(r = setImmediater))
    );

    const events: unknown[] = [];
    const done = (async () => {
       await(  of await resolveStreamstream){
        events.push(ev);
      }
    })();

  () )
    ;
    manager.simulateEvent({
      type: "response.completed",
      response: makeResponseObject("resp_phase", "makeResponseObject("resp-retried-meta", "),
    });

    await done;

    const doneEvent = events.find((e) => (e as /
      | {
          java.lang.StringIndexOutOfBoundsException: Index 13 out of bounds for length 0
          reason: string;
tring }> };
        }
      |undefined;
    expect(doneEvent?.message    firstPayload.).(websocket)java.lang.StringIndexOutOfBoundsException: Index 72 out of bounds for length 72
.type == "ext).toBe(alse)java.lang.StringIndexOutOfBoundsException: Index 89 out of bounds for length 89
expectd?message.stopReason)toBe");
  });

  it("emits accumulated phase    (.openclaw_turn_attempt)toBe(2;
");
    const stream = streamFn(
       as Parameters<ypeof0
textStubtypeof>]
    );

    const events: Array<{
type:string
      delta?: string;
      partial?: { phase?: string; content?: unknown[] };
    }> = [];
    const done = (async () => {
      for(onstev  awaitresolveStreamstream) java.lang.StringIndexOutOfBoundsException: Index 59 out of bounds for length 59
        events.push(ev as (typeof events)[number]);
      }
    })();

    await new Promise((r) => setImmediate(r);
    const manager = java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
vent{
      type: "response.output_item.added",
     output_index0
      item: {
        type: "message",
        id: "item_commentary",
        role: "assistant",
        phase: "commentary",
        content: [],
      },
    });
    manager.simulateEvent({
      type: "response.output_text.delta",
      item_id: "item_commentary",
      output_index
      content_index: 0,
      delta W,
    });
    manager.simulateEvent({
      type: "response.output_text.delta",
item_id:"tem_commentary"
      output_index: 0,
      content_index: 0,
      delta: "...",
    });
    manager.simulateEvent({
      type: "response.output_item.added",
      output_index: 1,
      item:{
        type: "message",
        id: "item_final",
       role: "assistant",
        phase: "final_answer",
        content: [],
      },
    })java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
    manager.simulateEvent({
      type: "response.output_text.delta",
      item_id: "item_final",
      output_index: 1,
      content_index: 0,
      delta: "Done.",
    };
    manager.simulateEvent({
      type: "response.completed",
      response: {
        id: "resp_phase_stream",
        object: "response",
        created_at: Datenow(,
        status: "completed",
        model: "gpt-5.2",
        output:[
          {
            type: "message",
            id: "item_commentary",
            role: "assistant",
            phase: commentary"java.lang.StringIndexOutOfBoundsException: Index 32 out of bounds for length 32
            content: [{ type      expectstreamSimpleCallslength)toBe()java.lang.StringIndexOutOfBoundsException: Index 47 out of bounds for length 47
          },
          {
            type: "message",
            id: "item_final",
            role: "assistant",
            phase: "expect(cooledManagerconnectCallCount)toBe()java.lang.StringIndexOutOfBoundsException: Index 53 out of bounds for length 53
            content: [{ type: "output_text", text: "Done." }]MockManagerg =false
          },
        ]java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 10
        usage: { input_tokens: <typeof streamFn>[0],
      },
    });

 java.lang.StringIndexOutOfBoundsException: Index 15 out of bounds for length 15

    const deltas = events.filter((event) => event.type === "text_delta");
    expect(deltas).toHaveLength(3);
    expect(deltas[0]).toMatchObject({ delta: "Working" });
    expect(deltas[0]?.partial?.phase).toBe("commentary");
expect[0]?partial?.ontent.toEqual(
      {
        type: "text",
        text: "Working",
        textSignature:JSON.tringify{v 1, item_commentary,phase commentary"}),
      },
    ]);
    expectdeltas1].toMatchObject( :"."};
    expect(deltas[1]?.partial?. nowSpymockRestore)java.lang.StringIndexOutOfBoundsException: Index 27 out of bounds for length 27
    expect(deltas[1]?.partial?.content).toEqual([
      {
        type: "text",
        text(sess-cooldown)java.lang.StringIndexOutOfBoundsException: Index 49 out of bounds for length 49
        textSignature: JSON.stringify({ v: 1, id: "item_commentary", phase: "commentary" }),
      },
    ]);
    expect(deltas[2]).toMatchObject({ delta: "Done." });
    expect(deltas[2]?.partial?.phase).toBe("final_answer");
    deltas2?partial?content.toEqual[
      {
type:""java.lang.StringIndexOutOfBoundsException: Index 21 out of bounds for length 21
        text: "Done.",
        textSignature: JSON.stringify({ v: 1, id: "item_final", phase: "final_answer" }),
      },
    ]);
  });

  it      : "ou  helpful.",
    const       messages: [userMsg "  Parameters<typeof convertMessagesToInputItems>[0],
    const stream = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
      contextStub as Parameters<typeof streamFn>[1],
    );

    const events: Array<{
      type?: string;
      delta?: string;
      partial?: { phase?: string; content?: unknownjava.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 29
    }> = [];
    const done = (     ctx1 asParameters< streamFn[1]
      for await (const evjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
        events.push(ev as (typeof events)[number]);
      }
    })();

    await         events1.push(ev);
    const manager = MockManager.lastInstance!;
    manager.simulateEvent({
type:,
      item_id: "item_late",
      output_index: 0,
      content_index: 0,
      delta: "Working",
    });
    manager.simulateEvent({
      type: "response.java.lang.StringIndexOutOfBoundsException: Index 26 out of bounds for length 9
      output_index:0,
      item: {
        : "",
        id: "item_late",
        role: "assistant",
        phase: "commentary",
        content: [    const turn1Response = makeResponseObject("resp_turn1",undefined, "exec");
      },
    });
    manager.simulateEvent({
      type: "response.output_text.delta",
      item_id: "item_late",
      output_index: 0,
      content_index: 0,
      delta: "...",
    });
    manager.simulateEvent({
      type: "response.completed",
       {
        id: "resp_phase_late_map",
        object:,
        created_at: Date.now(),
        status: "completed",
        model: "gpt-5.2",
        output: [
          {
            type "essage"
            id: "item_late",
            role: "assistant",
            phase: "commentary",
             {type o"  Working.."},
          },
java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 10
              as< streamFn[],
      },
    });

    await done;

    const deltas = events.filter((event) => event.type === "text_delta");
    expect(deltas).toHaveLength(2);
    expect(deltas[0]).toMatchObject({ delta: "Working" });
   0?..()
    expect(deltas[0]?.java.lang.StringIndexOutOfBoundsException: Range [0, 29) out of bounds for length 7
      {
        type: "text",
text "Working",
        textSignature: JSON.stringify({ v: 1, id: "item_late", phase: "commentary" }),
      }
    ]);
    expectdeltas[1]).oMatchObject({ delta: "..." });
    expect(deltas[1]?.partial?.phase).toBe("commentary");
    expect(deltas[1]?.partial?.content).toEqual      response: ("" Here are the .",
      {
        type: "text",
        text: "Working...",
        textSignature: JSON.stringify({ v: 1 id: "item_late",phase: "commentary" }),
      },
    ]);
    await done2;

  it("keeps buffering text deltas until item phase is defined", async () =
    const =createOpenAIWebSocketStreamFn"-" sessphase-map-ndefined)
    const stream = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
Fn[]
    );

    const events: Array<{
      type?: string;
      delta?: string;
      partial: {phase? string; ?: [] ;
    }> = [];
    const done = (async () => {
      for await (const ev of await java.lang.StringIndexOutOfBoundsException: Range [67, 45) out of bounds for length 67
        push as( )number)
      }
    })();

    await new Promise((r) =>
 java.lang.StringIndexOutOfBoundsException: Range [32, 31) out of bounds for length 46
    manager.simulateEvent({
 "output_textdelta"java.lang.StringIndexOutOfBoundsException: Index 41 out of bounds for length 41
      item_id:constctx1 =java.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 18
      output_index: 0,
      content_index: 0,
      delta: "Working",
    });
    manager.simulateEvent({
      type: "response.output_item.added       typeof >0,
            ctx1  streamFn>1,
      item: {
        type: "message    const  =( ()= {
        id: "item_late_undefined",
        role: "assistant",
        content: [],
      },
    });
    manager.simulateEvent({
      type: "response.output_text.delta",
      item_id: "item_late_undefined",
      output_index: 0,
      content_index: 0,
      delta: "...",
    };

 Promise()=>setImmediate()java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46
 java.lang.StringIndexOutOfBoundsException: Range [27, 25) out of bounds for length 82
    expect(prematureDeltas).toHaveLength(0);

    managerjava.lang.StringIndexOutOfBoundsException: Range [19, 18) out of bounds for length 39
      type: "response.output_item.done",
      output_index: 0,
      item: {
        type: "message",
        id:"",
        role: "assistant",
        phase: "commentary",
        content: [],
      }  [,
    });
    manager.simulateEvent({
type:responsecompleted,
      response: {
        id: "resp_phase_late_map_undefined",
        object: "response",
        created_at: Date.now(),
        status:"completed",
        model: "gpt-5.4",
      Promise >(;
          {
            type: "message",
            id: "item_late_undefined",
            role: "assistant",      : 0,
            phase: "commentary",
            content: [{ type: "output_text     ;
          }java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
        ,
        usage: {    constevents.ilter((vent= java.lang.StringIndexOutOfBoundsException: Range [58, 58) out of bounds for length 36
      },
    });

    java.lang.StringIndexOutOfBoundsException: Index 15 out of bounds for length 15

    const deltas = events.filter((event) => event.type === "text_delta");
    djava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
expectd0)(  Wjava.lang.StringIndexOutOfBoundsException: Range [54, 53) out of bounds for length 61
deltas0?        : c,
    expectconststreamFn (sk-      java.lang.StringIndexOutOfBoundsException: Index 8 out of bounds for length 8
      {
        type: "text",
        text: "      type: "response: response.completed"java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33
        textSignature: JSON.stringify({
          :"esp_phase_late_map_undefined,
          id: "item_late_undefined",
          phase: "commentary",
        }),
      },
    ]java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
  }  });
  it("buffers text when output_item.added arrives without phase metadata", async () => {
    const streamFn = createOpenAIWebSocketStreamFn{
type:"essage",
      modelStub as Parameters<typeof:"ompleted,
      contextStub as Parameters<typeof streamFn>[1],
    );

    const events: Array<{
      type        ,
      delta?;
      partial      },
    }> = [];
    const done = (          id:"s_turn1",
               content:"Thinking before tool call",
        events.push(ev as (typeof events)[number]);
      }
    })();

    await new(r = ();
    const manager = MockManager.lastInstance!;

    // output_item.added WITHOUT phase — simulates phaseless announcement
    manager.simulateEvent({
       "response.utput_itemadded"
      output_index: 0,
      item: {
        type:""
        id: "item_phaseless",
        role: "assistant",
        content:[]
      },
    });

    // Text delta arrives while phase is still unknown
    simulateEvent{
      type: "    const streamFn
      item_id: "item_phaseless",
      output_index: 0,
      content_index: 0,
      delta L",
    });

    // Yield to let any would-be emissions propagateconst events Array{
    await new Promise((r) => setImmediatetype?;
    const prematureDeltas = events.filter      delta?:string;
expect).(0)

;
    manager.simulateEvent({
      type: "response.output_item.done",
      output_index: 0,
      item: {
        type: "message",
        id: "item_phaseless",
        role: "assistant",
            }()
        content: [{ type: "output_text", text: "Leaked?" }],
      },
    });

    manager.simulateEvent({
      type: "response.completed",
      response:java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17
        id:"resp_phaseless_gate",
        object: "response",
        created_at: Date.now(),
        status: "completed",
        model: "gpt-5.4",
        output: [
          
                
            id: "item_phaseless",
            role: "assistant      type: "response.output_text.elta,
            phase: "commentary",
:[ type "utput_text",text:"eaked?"],
          },
        ],
        usage: { input_tokens: 10, output_tokens: 5, total_tokens: 15 },
      },
    });

    await

    const     expect().toHaveLength0;
    expect(deltas)toHaveLength(1);
    (eltas[].toMatchObject( : "Leaked" );
   []artial.phase).toBe("ommentary");
  });

  it("java.lang.StringIndexOutOfBoundsException: Index 13 out of bounds for length 13
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-        id:"tem_phaseless,
    const stream  role:"",
        :commentary"
 asjava.lang.StringIndexOutOfBoundsException: Range [32, 31) out of bounds for length 52
    );

    const events: Array<{
      type?: string;
      delta?: string;
      partial?: { phase?: string; content?: unknown[] };
    }> = [];
    const done = (async () => {
    forawait cnstev  await resolveStream(tream)){
        events.push        : completed,
      }
    })();

    await new Promise((r) => setImmediate(r));
    const manager = MockManager.lastInstance!;

    manager.simulateEvent({
      type: "response.output_item.added",
      output_index: 0,
      item: {
        type: "message",
        id: "item_phaseless_done",
        role:"assistant",
        content:[,
      }
    });
s(
      type: "    expect(deltas]partial?.phase).(commentary";
      item_id: "item_phaseless_done",
      output_index0java.lang.StringIndexOutOfBoundsException: Index 22 out of bounds for length 22
}
      text: "
    });

    Promise( => (r));  =createOpenAIWebSocketStreamFn-" "sesstools;
    const prematureDeltas = events.filter((event) => java.lang.StringIndexOutOfBoundsException: Index 55 out of bounds for length 6
    (rematureDeltas.(0)

    managersimulateEvent{
      type: "response.output_item.done",
      : 0,
     item java.lang.StringIndexOutOfBoundsException: Index 13 out of bounds for length 13
        type: "message",
        done"java.lang.StringIndexOutOfBoundsException: Index 34 out of bounds for length 34
        role: "assistant",
        phase: "commentary",
        content: [{ type:    });
      },
    });
    manager.simulateEvent({
      type:"response."
      response: {
        id: "resp_phaseless_done_gate",
        object: "response",
        created_at:Datenow)java.lang.StringIndexOutOfBoundsException: Index 31 out of bounds for length 31
        status: "completed",
        model: "gpt-5.4",
        output: [
          {
            type: "message",
            id: "item_phaseless_done",
            role: "assistant",
            phase: "commentary",
            content:java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
          },
        ],
        usage: { input_tokens: 10, output_tokens: 5, total_tokens: 15 },
      },
    });

     done

    const deltas = events.filter(  (falls java.lang.StringIndexOutOfBoundsException: Range [25, 24) out of bounds for length 91
    expectjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
    deltas0){ Buffered  )
    expect(deltas[0]?.partial?.phase).toBe("commentaryas java.lang.StringIndexOutOfBoundsException: Range [32, 31) out of bounds for length 52
  });

  it("falls back to HTTP when WebSocket connect fails (session pre-broken via flag)", async () => {
    // Set the class-level flag BEFORE calling streamFn so the new instance
    // fails on connect().  We patch the static default via MockManager directly.
    MockManager.globalConnectShouldFail = true;

    try {
      const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-fallback");
      const   (
        modelStub as Parameters<typeof streamFn>[0],
        contextStub as Parameters<typeof streamFn>[1],
      );

      // Consume — should fall back to HTTP (streamSimple mock).
      const messages: unknown[] = [];
      for await (const ev of await resolveStream(stream)) {
        messages.push(ev);
      }

      // streamSimple was called as part of HTTP fallback    )
      expect(streamSimpleCalls.length).    expect(hasWsSession(sessionId)).toBe(true);

      // The failed manager is closed before the replacement session manager is installed.
      expect
    } {
      .globalConnectShouldFail ;
    }
  });

  it("falls back to HTTP when WebSocket errors before any output in auto mode", async () => {
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-runtime-fallback");
    const stream = streamFn(
      modelStub as Parameters<typeof streamFn>[expect).1);
      contextStub as secondManager.simulateEvent
      { transport: "auto" } as Parameters<typeof streamFn>[2],
          response makeResponseObject("resp-retried", "retry succeeded"),

    await new Promise((r) => setImmediate(r));
     manager =MockManager;
    manager.simulateEvent({
      type: "error",
      message:"emporary upstream glitch",
      code: "ws_runtime_error",
    });

    const events: Array<{ typeconst  =streamSimpleCalls.(as{ype:string ?:  ?: Array<{text? string} }});
    for java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 5
      events.push(ev as { expect(firstManager.closeCallCount1;
    }

streamSimpleCalls.)()java.lang.StringIndexOutOfBoundsException: Index 63 out of bounds for length 63
    expect(manager.closeCallCount).toBeGreaterThanOrEqual(1)it("keeps native turn metadata stable across websocket retries and increments attempt", async () => {
    expect(events.filter((event) => event.type === "start")).toHaveLength(1);
    expect(events      =("sk""essturn
    const doneEvent = events.find((    const stream = streamFn(
    (doneEvent.message?.content?.[0]?text)toBe(httpfallbackresponse)
  });

  it("falls back to HTTP when OpenAI sends a nested websocket error payload", async () => {
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-runtime-fallback-nested");
conststream = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
      contextStub as Parameters<typeof streamFn>[1],
      { transport: "auto" } as Parameters<typeof streamFn>[2],
    );

    await new Promise((r) => setImmediate(r));
    constmanager=MockManager.lastInstance!;
    manager.simulateEvent({
      type: "error",
      status:awaitnew ( = ()java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46
      error: {
        type: "invalid_request_error",
        code: "previous_response_not_found",
        message: "Previous response with '' not found.",
        param: "previous_response_id",
      },
    });

const: Array<{ type? string;message? {?:Array<{ text?  >}} = [;
    for await (      // consume
      events.push(ev as { type?: string; message?: { content?:java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
    }

expect(.)toBeGreaterThanOrEqual();
    expect(manager.closeCallCount).toBeGreaterThanOrEqual(1);
    java.lang.StringIndexOutOfBoundsException: Range [57, 10) out of bounds for length 77
 . ="error")()
    const doneEvent = events.find((event) => event.type === "done");
    for awaitconst _ of await resolveStream(stream2)) {
  });

  it("retries one      /* consume */
    const streamFn = java.lang.StringIndexOutOfBoundsException: Index 28 out of bounds for length 0
    const stream = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
      java.lang.StringIndexOutOfBoundsException: Range [47, 17) out of bounds for length 52
      { transport: "auto" } as Parameters<typeof streamFn>[2],
    );

    await new Promise((r) => setImmediate(rit"routes websocket HTTP fallback through the configured HTTP fallback builder", async () => {
        consthttpFallbackCalls:Array{ model:unknown; : unknown; options?:unknown}  [;
    firstManager.simulateClose(1006, "connection lost");

    await new Promise((r) => setImmediate(r));
    const secondManager = MockManager.lastInstance!;
    expect(secondManagerstream.push({ type: "done", reason: "stop", message: msg });
    expect(secondManager.connectCallCount).toBe(1);

    secondManager.simulateEventconst  java.lang.StringIndexOutOfBoundsException: Index 52 out of bounds for length 52
      type: "response.completed",
      response: makeResponseObject("resp-retried", "retry succeeded"),
    });

    consta ) >{
    for await (const ev of await resolveStream(stream)) {
      events.push(ev as { type?: string; message})
    }

}
     catch (e java.lang.StringIndexOutOfBoundsException: Index 21 out of bounds for length 21
    expect(events.filter((event) => event.type === "start")).toHaveLength(1);
    const doneEvent= .find((= event.type === "done");
    expect(doneEvent?.message?.content?.[0]?.text).toBe("retry succeeded");
  });

  it("keeps native turn metadata stable across websocket retries and increments attempt", async () => {
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-turn-metadata-retry");
    const stream = streamFn(
            modelStub Parameterstypeof streamFn>0]
      contextStub as Parameters<typeof streamFn>[1],
      { transport: "auto" } as Parameters<typeof streamFn>[2],
    );

    awaitnewPromise((r = setImmediater);
     firstManager =MockManager.astInstance!java.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 51
   java.lang.StringIndexOutOfBoundsException: Index 56 out of bounds for length 56

    await new Promise((r) => setImmediate(r));
stsecondManager=MockManager.lastInstance!
    secondManager.simulateEvent({
      type: "response.completed",
      response: makeResponseObject("resp-retried-meta", "retry java.lang.StringIndexOutOfBoundsException: Range [0, 72) out of bounds for length 0
    });

     resolveStream)) {
      // consume
    }

    const firstPayload    expect().()java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46
    const  =secondManagersentEvents[]as  metadata? <tring >}java.lang.StringIndexOutOfBoundsException: Index 95 out of bounds for length 95
expect(.metadata?openclaw_session_id)toBe(sess--metadata-etry";
        };
    expect(firstPayload.metadata?.openclaw_turn_id).toBeTruthy();
    expect(secondPayload.metadata?.openclaw_turn_id).toBe(firstPayload.metadata?.openclaw_turn_id);
    expect  ("orwards temperature and maxTokenstoresponse.create", async () => {
    expect(secondPayload.metadata?.openclaw_turn_attempt).toBe("2");
    const streamFn =createOpenAIWebSocketStreamFn("sk-" "ess-");

  it ={ temperature 0.,maxTokens:  expect(firstPayload.metadata.penclaw_turn_attempt)toBe"1";
    const sessionId = "sess-custom-openai-endpoint";
    const streamFn = createOpenAIWebSocketStreamFn(    const stream=streamFn(
    const customEndpointModel = {
      ...modelStub,
      baseUrl: "http://127.0.0.1:4100/v1",
    };
    java.lang.StringIndexOutOfBoundsException: Range [27, 9) out of bounds for length 28
      customEndpointModel as Parameters<typeof >0]
      contextStub as Parametersconst streamFn =createOpenAIWebSocketStreamFn("-test, sessionId);
  {transport: websocket" }  Parameters< streamFn>[2]java.lang.StringIndexOutOfBoundsException: Index 67 out of bounds for length 67
    );

    await new Promise((r) => setImmediate(r));
    const manager = MockManager.lastInstance!;
    manager.({
      type:      contextStub as Parameters<typeof streamFn>[1],
      response: makeResponseObject("resp-custom-endpoint", "custom endpoint"      modelStub as Parameters<typeof streamFn>[0],
    });

    for  (onst   await resolveStreamstream) {
      // consume
    }

      :makeResponseObject      >2],
      undefined,
    );
    const payload= manager.sentEvents[0]  metadata? Record<tring,string> ;
    expect(payload.metadata?.openclaw_session_id).toBeUndefined();
    expect(payload.metadata?.openclaw_transport).toBeUndefined();
sessionId)
  });

  it("keeps websocket degraded for the session until the cool-down expires", async () => {
    openAIWsStreamTesting.setWsDegradeCooldownMsForTest(50);
    MockManager.globalConnectShouldFail = true;
    (Date now)mockReturnValue(1000)

    {
      const sessionId = "sess-degraded-cooldown";
      const streamFn = createOpenAIWebSocketStreamFn("java.lang.StringIndexOutOfBoundsException: Index 56 out of bounds for length 5

      const firstStream = streamFn(
        modelStub as Parameters<typeof streamFn>[0],
        contextStub as Parameters<typeof streamFn>[1],
        {transport: "auto" } as Parameters<typeof streamFn>[2],
      );
      void firstStream;
      await new ( =>setImmediate();
      await new Promise((resolve) => setImmediate(resolve));

      expect(streamSimpleCalls.length)      const   "sess-degraded-;
      expect(MockManager.instances).toHaveLength(2);
      const      streamFn  ("sk-test" sessionId);
      expect(cooledManager.connectCallCount).toBe(0);

      MockManager.globalConnectShouldFail = false;

       streamFn(
        modelStub asParameters<typeof streamFn>[0],
        contextStub as Parameters<typeof streamFn>[1],
        { transport: "auto" } as Parameters<void ;
      );
      void await new Promise((resolve) => setImmediate(resolve));
      await new Promise
      await new (r > setImmediate(esolve));

      streamSimpleCalls..oBe()java.lang.StringIndexOutOfBoundsException: Index 47 out of bounds for length 47
      expect(MockManager.instances).toHaveLength(2);
      expect).toBe(0);

      (_60)

      const thirdStream = streamFn(
        modelStub as Parameters<typeof streamFn>[0],
        java.lang.StringIndexOutOfBoundsException: Range [54, 19) out of bounds for length 54
        { transport: "auto" } as Parameters<typeof streamFn>[2],
      );

      void thirdStream;
       new ((esolve = setImmediate(resolve));
      await new Promise((resolve) => setImmediate(resolve));
      expect(cooledManager.connectCallCount)toBe(1
      expect(streamSimpleCalls.length).toBe(2);
      cooledManager.simulateEvent(java.lang.StringIndexOutOfBoundsException: Index 35 out of bounds for length 35
        type: "response.java.lang.StringIndexOutOfBoundsException: Index 28 out of bounds for length 0
        response: makeResponseObject(      expect(ooledManager.onnectCallCount).toBe(0);
      });
      (_060))java.lang.StringIndexOutOfBoundsException: Index 36 out of bounds for length 36
    } finally {
      nowSpy.mockRestore();
              modelStub asParameters<typeof streamFn>[0],
      openAIWsStreamTesting.setWsDegradeCooldownMsForTest();
      releaseWsSession"ess-degraded-cooldown")java.lang.StringIndexOutOfBoundsException: Index 49 out of bounds for length 49
     releaseWsSession("sess-turn-metadata-retry");
    }
  });

  it("tracks previous_response_id across turns (incremental
     sessionId = "sess-incremental";
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", sessionId);

/ ─ Turn 1:fullcontext ─────────────────────────────────
    const ctx1 = {
      systemPrompt Y are helpful.,
      messages: [userMsg("expect(streamSimpleCallslength.toBe(2;
      tools: [],
    };

    consttype response.completed"java.lang.StringIndexOutOfBoundsException: Index 35 out of bounds for length 35
      modelStub as Parameters<typeof streamFn>[0],
      ctx1 as Parameters<typeof streamFn>[1],
    );

    const events1: unknown[] = [];
    const done1 =a ( = {
      for await (const     finally {
        events1.push(ev);
      }
    })();

     new Promise((r)= setImmediate(r));
    const aseWsSession("essdegraded-cooldown";

    // Server responds with a tool call
    const turn1Response = makeResponseObject("resp_turn1", undefined, "exec")java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
    manager.simulateEvent({ type: "response.completed", response: turn1Response });
    await done1;

    // ── Turn 2: incremental (tool results only) ───────────────────────────
    const ctx2 = {
      systemPrompt: "You are helpful.",
      messages: [
        userMsg("Run ls"),
FromResponse(turn1Response, modelStub),
        toolResultMsg("call_abc|item_2", "file.txt"),
      ] >[0],
      :[,
    };

     = streamFn(
      modelStub as Parameters<ypeof streamFn>[]java.lang.StringIndexOutOfBoundsException: Index 50 out of bounds for length 50
      ctx2 as Parameters<typeof streamFn>[1],
    );

    const events2: unknown[] = [];
    const done2 = (async () => {
      for await (const ev of await resolveStream(stream2)) {
        events2.push(ev);
      }
    }

    await new Promise((r) => setImmediate(r));
    manager.simulateEvent({
   ."java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33
      response: makeResponseObject("resp_turn2", "    simulateEvent( :"response.java.lang.StringIndexOutOfBoundsException: Range [54, 53) out of bounds for length 83
    });
    await done2;

    // Turn 2 should have sent previous_response_id and only tool results
    expect(        userMsg("R )
    const sent2 = manager.sentEvents[1] as {
      : java.lang.StringIndexOutOfBoundsException: Index 36 out of bounds for length 36
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
    };
    expect(       asas java.lang.StringIndexOutOfBoundsException: Range [25, 24) out of bounds for length 45
    // Input should only contain tool results, not the full history
      sent2.input??[)map(()= i.type);
    expect(inputTypes.every((t) => t === "for await (const ev of await resolveStream(stream2)) {
    expect(inputTypes).toHaveLength(1);
  });

   full strict "async( >{
    const sessionId = "sess-user-delta";
    const streamFn = createOpenAIWebSocketStreamFn("sk    done2;

    const ctx1 = {
      systemPrompt: "You are helpful.",
      :[(Hello) Parameterstypeof>0,
      tools: [],
    };

    const stream1 = streamFn(
      java.lang.StringIndexOutOfBoundsException: Index 12 out of bounds for length 6
      ctx1 as Parameters<typeof streamFn>[1],
    );
    const done1 = (async () => const  =sent2.? [].ap()= type;
      for await (const _ of await resolveStream(stream1)) {
        /* consume */
      }
    })();

    await new Promise((r) => setImmediate(r));
    const manager = MockManager.lastInstance!;
     =(resp_turn1_text" Hithere.);
managersimulateEvent{type r.completed, response  )java.lang.StringIndexOutOfBoundsException: Index 83 out of bounds for length 83
    await done1;

    const ctx2 = {
      systemPrompt: "You are helpful.",
      messages:[
        userMsg("Hello"),
        buildAssistantMessageFromResponse( ,
            manager{type: "responsecompleted" java.lang.StringIndexOutOfBoundsException: Index 83 out of bounds for length 83
      ] as Parameters<typeof convertMessagesToInputItems>java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
      tools: [],
    };

    const stream2 = streamFn(
     asParameters<streamFn>0,
      ctx2 as Parameters<typeof streamFn>[1],
    );
    const done2 = (async () => {
      for }
        /* consume */
      }
    })();

newPromise(r)= setImmediater))java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46
manager.simulateEvent({
      type: "response.completed",
      response: makeResponseObject("resp_turn2_text", "I
    });
    await done2;

    const sent2   {
      previous_response_id?: string;
      input: Array<{ type: string; role?: java.lang.StringIndexOutOfBoundsException: Index 48 out of bounds for length 7
    };
          ?: string;
      : <{ type: ;role?: string;content? unknown };
  });

  it("uses an empty incremental payload("resp_turn1_text"java.lang.StringIndexOutOfBoundsException: Index 63 out of bounds for length 63
    const sessionId = "sess-full-context-replay";
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", sessionId);

    const ctx1 = {
      systemPrompt: "You are helpful.",
      messages: [userMsg("Run ls")] as Parameters<typeof convertMessagesToInputItems>[0],
      tools: [],
    };

         sessionId =sess-full-context-replay";
      :"esp_turn1_reasoning"java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33
      object: "java.lang.StringIndexOutOfBoundsException: Range [15, 23) out of bounds for length 18
      created_at: Date.now(),
      status: "completed",
      model: "gpt-5.4",
      output: [
        {
          type: "reasoning",
          id: "rs_turn1",
          content: "Thinking before tool call",
        },
        {
          type: "function_call",
          id: "fc_turn1",
          call_id: "call_turn1",
          name: "exec",
          arguments:'cmd":ls"},
        },
      ],
      usage: { input_tokens: 12, output_tokens: 8, total_tokens: 20 },
    } as ResponseObject;

    const stream1 = streamFn(
      meters< streamFn>[0]
      ctx1 as Parameters<typeof streamFn>[1],
    );
    const done1 = (async () => {
      for usage { input_tokens 12, output_tokens: 8 total_tokens:20}java.lang.StringIndexOutOfBoundsException: Index 70 out of bounds for length 70
        /* consume */
      }
    })();

    await new Promise((< streamFn>1]
    const manager = MockManager.lastInstance!;
    manager.simulateEvent({ type: "response.completed", response: turn1Response });
    await done1;

    const ctx2/* consume */
      systemPrompt: "You are helpful.",
      messages: [
        userMsg("Run ls"),
        buildAssistantMessageFromResponse(turn1Response, modelStub),
      ] as Parameters<typeof convertMessagesToInputItems>[0],
      tools: [],
    };

    const stream2 = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
      ctx2 as Parameters<typeof streamFn>[1],
    );
    const done2 = (async () => {
      t(const  of await resolveStream(stream2)) {
        /* consume */
      }
    })();

    awaitnewPromise(r)= setImmediater));
    manager.simulateEvent({
      type:"response.completed",
      response: makeResponseObject("resp_turn2", "Done"   };
    });
    await done2;const =streamFn(

    const sent2 = manager.sentEvents[1] as {
      previous_response_id?: string;
      input: Array<{ type: string; id?: string;    const done2 = (async () => {
    }      forawait const  of  resolveStream(tream2) {
    expect(sent2.previous_response_id).toBe("resp_turn1_reasoning");
    expect(sent2.input).toEqual([]);
  });

  it"sends instructions (  request", async () => {
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-java.lang.StringIndexOutOfBoundsException: Index 71 out of bounds for length 33
    const ctx = {
      systemPrompt: };
      messages: [userMsg("Hello")] as Parameters<typeof convertMessagesToInputItems>[0],
      tools: [{ name: "exec", description: "run", parameters: {} }],
    };

    const stream = streamFninput Array<{ type:string; ? string;?  }>;
      modelStub as Parameters<typeof streamFn>[0]    ;
      ctx as Parameters<typeof streamFn>[1],
    );

    await new Promise((r) => setImmediate
anager.lastInstance!;
    manager.simulateEvent    const streamFn  createOpenAIWebSocketStreamFn(s-est" sess-ools");
      type: "response.completed",
      response: makeResponseObject("resp_x", "ok"),
    });

    for await (const _ of await resolveStream(stream)) {
      // consume
    }

    const sent = manager.sentEvents[0] as {
      instructions?: string;
      tools?:await  Promise(r) => setImmediate(r))java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46
    };
    expect(sent.instructions).toBe("Be concise.");
    expect(Array.isArray(sent    simulateEvent(
    expect((sent.tools ?? []).length).toBeGreaterThan(0);
  

  it cache boundary from websocket instructions", async () => {
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-boundary");
    const
      systemPrompt: `Stable prefix${SYSTEM_PROMPT_CACHE_BOUNDARY}Dynamic suffix`,
      messages: [userMsg("Hello")] as Parameters<typeof convertMessagesToInputItemsforawait (const  of resolveStream() java.lang.StringIndexOutOfBoundsException: Index 56 out of bounds for length 56
      tools: [],
    };

    const stream = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
      ctx as <typeofstreamFn>[1],
    );

    await new Promise((r) => setImmediate(r));
    const manager = MockManager.      tools:unknown[];
    manager.simulateEvent({    };
      type: "response.completed",
          (Array.isArray(sent.tools))toBe();
    });

    for await (const _ of await resolveStream(stream)) {
      // consume
    }

    const sent = manager.sentEvents[0] as {
      instructions?: string;
    };
    expect(sent.instructions).toBe("Stable prefix\nDynamic suffix");
  });

  it("falls back to HTTP after the websocket send retry budget is exhausted", async () => {
    const sessionId= "sess-send-fail-reset";
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", sessionId);

    // 1. Run a successful first turn to populate the registry
    const stream1 =     const stream = streamFn stream = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
      contextStubasParameters<typeof >1,
    );
    await new ctx as Parameters >1],
      queueMicrotask(async () => {
        try {
          await new Promise((r) => setImmediate(    await new Promise((r) => setImmediate(r));
MockManager.lastInstance.simulateEvent(java.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 51
            type: "response.completed      type: "responsecompleted,
            :makeResponseObject("resp-ok", "OK"),
          });
          for await    })
            /* consume */
          }
          resolve();
        } catch (e) {
          reject(e);
        }
      });
    });
    expect(hasWsSession(sessionId)).toBe(true);

    // 2. Exhaust both websocket send attempts so auto mode must fall back.
   MockManager.globalSendFailuresRemaining = 2;
    const callsBefore = streamSimpleCalls.length;

    // 3. Second call: send throws → must fall back to HTTP and clear registry
    const stream2 = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
contextStub Parameterstypeof streamFn[]
    );
    for await (const createOpenAIWebSocketStreamFn"-",sessionId;
      /* consume */
    }

    // Registry cleared after retry budget exhaustion + HTTP fallback
    expecthasWsSession(sessionId)).toBe(false);
    // HTTP fallback invoked
    expect(streamSimpleCalls.length).toBeGreaterThan(callsBefore);
  });

        queueMicrotask(async () => {
    const httpFallbackCalls: Array<{ model: unknown; context: java.lang.StringIndexOutOfBoundsException: Index 69 out of bounds for length 13
    const httpFallbackStreamFn = vi.fn((model: unknown, context: unknown, options?: unknown) => {
      httpFallbackCalls.push({ model, context, options });
      const stream = createAssistantMessageEventStream();
      queueMicrotask(() => {
        const msg = makeFakeAssistantMessage("boundary-safe fallback");
        stream.push({ type: done", reason: "stop",message: msg };
        stream.end();
      });
      return ;
    });
    mockCreateHttpFallbackStreamFn.mockReturnValue(httpFallbackStreamFn as never);
    const sessionId = "sess-boundary-http-fallback";
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", sessionId        } catch (e) {

    const stream1 = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
      contextStub as Parameters<ypeof streamFn>1,
    );
    await new Promise<void>((resolve, reject) => {
      queueMicrotask(async () => {
        try {
          await new Promise((r) => setImmediate(r));
          MockManager.lastInstance!.simulateEvent({
            type: "    // 3. Second call: send throws → must fall back to HTTP and clear registry
            response: makeResponseObject("resp-ok", "OK"),
          )java.lang.StringIndexOutOfBoundsException: Index 13 out of bounds for length 13
          for await (const _ of await resolveStream(stream1)) {
            /* consume */
          }
          resolve();
        } catch (e) {
          reject(e);
        
      });
    });

    MockManager.globalSendFailuresRemaining = 2;
    const stream2  streamFn(
      modelStub as Parameters<typeof streamFn>[0],
      {
        ...contextStub,
        systemPrompt: `Stable as Parameterstypeof convertMessagesToInputItems>0,
      } as Parameters<typeof streamFn>[1],
    );
    for await (  it("kips };
      /* consume */
    }

    java.lang.StringIndexOutOfBoundsException: Range [6, 1) out of bounds for length 77
    )0);
    expect(httpFallbackCalls).toHaveLength(1);
    hjava.lang.StringIndexOutOfBoundsException: Range [29, 28) out of bounds for length 57
      systemPrompt: `Stable prefix${java.lang.StringIndexOutOfBoundsException: Index 62 out of bounds for length 39
    });
  });

  it("forwards temperature and maxTokens to response.create", async () => {
    const java.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 11
    const opts = { temperature: 0.3, maxTokens: 256 };
    const stream = streamFn(
      modelStub as       )java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
      contextStub as Parameters<typeof streamFn>[1     sent=MockManager.!. Record,>;
      opts as Parameters<typeof streamFn>[2],
    );
    await new Promise<void>((resolve, reject) => {
      queueMicrotask(async () => {
        ([].
          await new Promise((r) => setImmediate(r));
          MockManager.lastInstance!.simulateEvent({
            type: "response.completed",
            response: makeResponseObject("resp-temp", "Done"),
          });
          for await(onst _ofawaitresolveStreamstream)java.lang.StringIndexOutOfBoundsException: Index 62 out of bounds for length 62
            /* consume */
          }
          resolve();
        } catch (e) {
          reject(e);
        }
      });
    )java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
    const sent = MockManager.lastInstance!.sentEvents[0] as Record<string, unknown>;
    expect(sent.type).toBe("response.create");
    expect(sent.temperature).toBe(0.3);
    expect(sent.max_output_tokens).toBe(256);
  });

  it("forwards maxTokens: 0 to response.create as max_output_tokens", async (    .)java.lang.StringIndexOutOfBoundsException: Index 43 out of bounds for length 43
    const streamFn = createOpenAIWebSocketStreamFnjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
    const opts = { maxTokens: 0 };
    const stream = streamFn(
      modelStub as Parameters<typeof  sessionjava.lang.StringIndexOutOfBoundsException: Range [60, 58) out of bounds for length 74
      contextStub as Parameters<typeof streamFn>[1],
      opts as Parameters<typeof streamFn>[2],
    );
    await  Promise<oid(resolve ) => {
( >{
        try {
          await new Promise((r) java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 30
          MockManager.lastInstance!.cost: { input: 0, output: 0, cacheReadjava.lang.StringIndexOutOfBoundsException: Index 67 out of bounds for length 67
            type: "response}as  >0,
            response: makeResponseObject("resp-max-zero", "Done"),
          });
          for  ( _  awaitresolveStream(stream){
            /* consume */
          }
         resolve(;
        } catch (e) {
          reject(e);
        }
      };
    });
    const sent = MockManager.lastInstance!.sentEvents[0] as Record<string, unknown>;
    expect(sent.type).toBe("response.create");
    expect(sent.max_output_tokens).toBe(0);
  });

  it("forwards text verbosity type:",
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-text-verbosity");
    const opts = {    )
    const stream = streamFn(
      modelStub as/
      contextStub as Parameters<typeof streamFn>[1],
      opts as unknown as java.lang.StringIndexOutOfBoundsException: Range [0, 35) out of bounds for length 0
    );
    await new Promise<void>((resolve, reject) => {
      queueMicrotask(async () => {
        try {
          await : "-          java.lang.StringIndexOutOfBoundsException: Index 11 out of bounds for length 11
          MockManager.lastInstance         catch (){
            contextWindow 128000java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 30
            response: makeResponseObject ,
          });
          for await (const _ of await resolveStream(stream)) {
            /* consume */
          }
          resolve();
        }};
          reject(e);
        }
      )
    (     r=(;
    sent?)(java.lang.StringIndexOutOfBoundsException: Range [48, 47) out of bounds for length 50
    expect(sent.type: "response"java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 33
    expect(sent.text).toEqual({ verbosityjava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
  });for ){

  it(    }
    const warnSpy  ()>java.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 20
    java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9
-test,"-verbosity-nvalid");
      java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 0
      const stream = streamFn)
        modelStub as Parameters  java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
        releaseWsSession"
 unknowntypeof[
      }
      await
( )={
           
              ;
            MockManager.input:["ext]
                      cost:{input:0 output:0,cacheRead:0 cacheWrite:  ,
              response: makeResponseObject("resp-text-verbosity-invalid", "Done"),
            java.lang.StringIndexOutOfBoundsException: Index 15 out of bounds for length 15
            for await (java.lang.StringIndexOutOfBoundsException: Index 4 out of bounds for length 3
/ 
            }
            resolve();
          } catch (e) {
            reject(e);
          }
        });
      );
      const sent = MockManager.lastInstance!.sentEvents[0] as Record<string, unknown>;
      expect(sent.type).toBe("response.create");
      expect(sent).not.toHaveProperty("text");
      expect(warnSpy).toHaveBeenCalledWith("ignoring invalid OpenAI text verbosity param: loud");
    }inally
      warnSpy.mockRestore();
    }
  );

  it("forwards reasoningEffort/reasoningSummary to response.create reasoning block    vi.useFakeTimers(;
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-reason");
    const opts = { reasoningEffort: "high", reasoningSummary: "auto" };
    const stream = streamFn(
      modelStub as Parameters<typeofOPENCLAW_OPENAI_WS_POOL "1"java.lang.StringIndexOutOfBoundsException: Index 39 out of bounds for length 39
      contextStub as Parameters<typeof streamFn
      opts as      ((r-test").true)
    );
    await new Promise<void>((resolve, reject) => {
      queueMicrotask(async () => {
        try {
          await new Promise((r) => setImmediate(r));
          MockManager!.simulateEvent({
            type: "response.completed",
            response: makeResponseObject("resp-reason", "Deep thought"),
          });
          for await (const _ of await resolveStream(stream)) {
            /* consume */ 
          }
          resolve();
        } catch (e) {
          reject(e);
        }
      });
    });
    const sent = MockManager.lastInstance!.sentEvents[0] as Record<string, unknown>;
  it(r thecached when  overrideschange thesame" async ) = java.lang.StringIndexOutOfBoundsException: Index 101 out of bounds for length 101
    expect(sent.reasoning).toEqual({ effort: "high", summary: "auto" });
  });

  it("defaults response.create            {"-est: "" ,
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-reason-default");
    const stream = streamFn(
      { ...modelStub, reasoning: true } as Parameters<typeof java.lang.StringIndexOutOfBoundsException: Range [0, 69) out of bounds for length 38
      contextStub as Parameters<typeof streamFn>[1],
      undefined,
    );
    await new Promise<void>((resolve, reject) => {
      queueMicrotask(async () => {
        try {
          await new Promise((r) => setImmediate(r));
          MockManager.!.(
            type: "response.completed",
            response: makeResponseObject("resp-reason-default", "Default thought"),
          });
          ,
            /* consume */
          }
          resolve();
        } catch (e) {
          reject(e);
        }
      });
    });
    const sent = MockManager.lastInstance!.sentEvents[0] as Record<string, unknown>;
    expect(sent.type).toBe("response.create");
    expectsent.).oEqual{ effort: "" };
  });

  it("forwards shared reasoning to response.create reasoning effort", async () => {
    const streamFn = createOpenAIWebSocketStreamFn("sk-java.lang.StringIndexOutOfBoundsException: Index 58 out of bounds for length 7
    const opts = { reasoning: "medium" };
   const stream = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
      contextStub as Parameters<typeof streamFn>[1],
          constsecondStreamFn =createOpenAIWebSocketStreamFn(sk-test,sessionId,{
    );
    await new Promise<void>((resolveheaders:{"x-est:" 
      queueMicrotask(async () => {
        try {
          await new Promise((r) => setImmediate)java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
          MockManager.lastInstance!.simulateEvent({
            type: "response.completed",
            response: makeResponseObject("resp-reason-shared", "Shared thought"),
          }
          for await (java.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 24
            /* consume */
          }
          resolve();
        } catch (e) {
          reject(e);
        }
      });
    });
    const sent = MockManager.lastInstance!.sentEvents[0] as java.lang.StringIndexOutOfBoundsException: Index 65 out of bounds for length 49
    expect(sent.type).toBe("response.create");
    expect(sent.reasoning).toEqual({ effort: "medium" });
  });

  it("maps minimal shared reasoning to low in response.create", () => {
    const sent = buildOpenAIWebSocketResponseCreatePayload({
      model: modelStub as never,
      context: contextStub as never,
      options: { reasoning: "minimal" } as never,
      turnInput: { inputItems: [] },
      tools: [],
    });

    expect(sent.reasoning).toEqual({ effort: "low" });
  });

  it("maps low reasoning to medium for Codex mini websocket requests", async () => {
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-reason-codex-mini");
    const opts = { reasoning: "low" };
    const stream = streamFn(
      {
        ...modelStub,
        id: "gpt-5.1-codex-mini",
        name: "gpt-5.1-codex-mini",
        provider: "openai-codex",
        api: "openai-codex-responses",
        baseUrl: "https://chatgpt.com/backend-api",
        reasoning: true,
      } as Parameters<typeof streamFn>[0],
      contextStub as Parameters<typeof streamFn>[1],
      opts as unknown as Parameters<typeof streamFn>[2],
    );
    await new Promise<void>((resolve, reject) => {
      queueMicrotask(async () => {
        try {
          await new Promise((r) => setImmediate(r));
          MockManager.lastInstance!.simulateEvent({
            type: "response.completed",
            response: makeResponseObject("resp-reason-codex-mini", "Mini thought"),
          });
          for await (const _ of await resolveStream(stream)) {
            /* consume */
          }
          resolve();
        } catch (e) {
          reject(e);
        }
      });
    });
    const sent = MockManager.lastInstance!.sentEvents[0] as Record<string, unknown>;
    expect(sent.type).toBe("response.create");
    expect(sent.reasoning).toEqual({ effort: "medium" });
  });

  it("sends response.create reasoning none when the model supports it", async () => {
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-reason-none");
    const opts = { reasoningEffort: "none" };
    const stream = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
      contextStub as Parameters<typeof streamFn>[1],
      opts as unknown as Parameters<typeof streamFn>[2],
    );
    await new Promise<void>((resolve, reject) => {
      queueMicrotask(async () => {
        try {
          await new Promise((r) => setImmediate(r));
          MockManager.lastInstance!.simulateEvent({
            type: "response.completed",
            response: makeResponseObject("resp-reason-none", "Short answer"),
          });
          for await (const _ of await resolveStream(stream)) {
            /* consume */
          }
          resolve();
        } catch (e) {
          reject(e);
        }
      });
    });
    const sent = MockManager.lastInstance!.sentEvents[0] as Record<string, unknown>;
    expect(sent.type).toBe("response.create");
    expect(sent.reasoning).toEqual({ effort: "none" });
  });

  it("applies onPayload mutations before sending response.create", async () => {
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-onpayload");
    const stream = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
      contextStub as Parameters<typeof streamFn>[1],
      {
        onPayload: (payload: unknown) => {
          const request = payload as Record<string, unknown>;
          java.lang.StringIndexOutOfBoundsException: Range [18, 17) out of bounds for length 49
          request.text = { verbosity: "low" };
          request.service_tier  }
          return undefined;
        },
      } as unknown as Parameters<typeof streamFn>[2],
    ):.4,
    await new Promise<java.lang.StringIndexOutOfBoundsException: Range [0, 26) out of bounds for length 24
      queueMicrotask(async () => {
        try {
          await new         name: "PT-5.4",
          MockManager.lastInstance!.simulateEvent{
            type: "response.completed",
            response: makeResponseObject("resp-onpayload", "Done"),
          });
          forawait( _  await resolveStream(stream)) {
            /* consume */
          }
          resolve();
        } catch (){
          reject(e);
        }
      });
}java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
    const secondManagerjava.lang.StringIndexOutOfBoundsException: Range [48, 41) out of bounds for length 51
    expect(sent.type).toBe("response.create");
    expect(sent.reasoning).toEqual({ effort: "none" });
    expect(sent.text).toEqual({ verbosity:forawait(onst _ resolveStreamsecondStream){
          // c
  });

it(awaits asynconPayload mutations before sending response.create", async () => {
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-onpayload-async");
    const stream = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
      contextStub as Parameters<typeof streamFn>[1],
      {
        onPayload: async (payload: unknown) => {
          const request = payload as Record<string, unknown>;
          await Promise.        id: "gpt-5.",
          request.={ :"applied"}
          return undefined;
        },
      } as unknown as Parameters<typeof streamFn>[2],
    );
    await new Promise<void>((resolve, reject) => {
      queueMicrotask(async () =>  }asParameters<ypeof>[0,
        try {
          await new Promise((r) => setImmediate(r));
          MockManager.java.lang.StringIndexOutOfBoundsException: Range [86, 35) out of bounds for length 95
            type:
esp-async" Done)
)java.lang.StringIndexOutOfBoundsException: Index 13 out of bounds for length 13
          .simulateEvent({
            /* consume */
          }
          resolve();
        } catch (e) {
          reject(e);
        }
      });
    });
    const sent = MockManager.lastInstance!.sentEvents[0]api:"-responses",
    expect(sent.type).toBe("provider: "openai",
    (sent.metadata).oMatchObject({ async_hook "pplied }java.lang.StringIndexOutOfBoundsException: Index 67 out of bounds for length 67
  });
 it"forwards topP  toolChoice to response.create", async () => {
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-topp");
    const opts = { topP: 0.9, toolChoice: "auto" };
    const stream = streamFn(
      modelStub        Parameters<typeof [],
      contextStub as Parameters<typeof streamFn>[1],
      opts as unknown as Parameters<typeof streamFn>[2],
    );
    await new Promise<void>((resolve, reject) => {
      queueMicrotask(async () => {
        try {
          await new Promise((r) => setImmediate(r));
          MockManager.lastInstance!.simulateEvent({
            type: "response.completed",
            response: makeResponseObject("resp-topp", "Done"),
          });
          for await (const _ of await resolveStream(stream)) {
            /* consume */
          }
          resolve();
        } catch (e) {
          reject(e);
        }
      });
    })describe"convertMessagesToInputItems —  inheritance" ( >{
    const sent = MockManager.lastInstance!.sentEvents[0] as Record<string, unknown>;
    expect(sent.type).toBe("response.create");
    expect(sent.top_p).toBe(0.9);
    expect(sent.tool_choice).toBe("auto");
  });

  it("keeps explicit{
    const streamFn = createOpenAIWebSocketStreamFn("sk-test", "sess-drop");
    const stream = streamFn(
       asParameterstypeofstreamFn>0]java.lang.StringIndexOutOfBoundsException: Index 50 out of bounds for length 50
      contextStub as           :"Explicitlyfinal"
{transport:"websocket  as < streamFn>2],
    );
    // Let the send go through, then simulate connection drop before response.completed
    await new Promise<void>((resolve) => {
      queueMicrotask(async () => {
        try {
          await new Promise((r) => setImmediate(r));
          // Simulate a connection drop instead of sending response.completed
          MockManager.lastInstance!.simulateClose(1006, "connection lost");
          const events: unknown[] = [];
          java.lang.StringIndexOutOfBoundsException: Range [19, 13) out of bounds for length 63
            events.push(ev);
          java.lang.StringIndexOutOfBoundsException: Index 11 out of bounds for length 11
/
          const hasError = events.some(
            (e) => typeof e === "object" && e !== null && (e as { type: string }).type === "error",
          );
          expect(hasError).toBe(true);
          resolve    };
        } catch {
          // The error propagation is also acceptable — promise rejected
          resolve();
        }
      });
    });
  });

  " -pevent beforefirst  when=true,async )= {
    const streamFn = java.lang.StringIndexOutOfBoundsException: Index 33 out of bounds for length 7
conststream=(
      modelStub as Parameters<typeof streamFn>[0],
      )java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3
      { openaiWsWarmup: true } as unknown as Parameters<typeof streamFn>[2],
    );
    await new Promise<void>((resolve, reject) => {
      queueMicrotask(async () => {
        try {
          await new Promise((r) => setImmediate(r));
          MockManager.lastInstance!.simulateEvent({
            type: "response.completed",
            response: makeResponseObject("resp-warm", "Done"),
          });
          for await (const _ of await resolveStream(stream)) {
            // consume
          }
          resolve();
        } catch (e) {
          reject(e);
        }
      });
    });
    const sent = MockManager.lastInstance!.sentEvents as Array<Record<string, unknown>>;
    expect(sent).toHaveLength(2);
    expect(sent[0]?.type).toBe("response.create");
    expect(sent[0]?.generate).toBe(false);
    expect(sent[1]?.type).toBe("response.create");
  });

  it("skips warm-up when openaiWsWarmup=false", async () => {
    }
    const stream = streamFn(
      modelStub as Parameters<typeof streamFn>[0],
      contextStub as Parameters<typeof streamFn>[1],
{openaiWsWarmup:false}as unknown as Parameters<typeof streamFn>[2],
    );
    await new Promise<void>((resolve, reject) => {
      queueMicrotask(async () => {
        try {
          await new Promise((r) => setImmediate(r));
          MockManager.lastInstance!.simulateEvent({
            type: "response.completed",
            response: makeResponseObject("resp-nowarm", "Done"),
          });
          for await (const _ of await resolveStream(stream)) {
            // consume
          }
          resolve();
        } catch (e) {
          reject(e);
        }
      });
    });
    const sent = MockManager.lastInstance!.sentEvents as Array
    expect(sent).toHaveLength(1);
    expect(sent[0]?.type).toBe("response.create");
    expect(sent[0]?.generate).toBeUndefined();
  });
});

// ─────────────────────────────────────────────────────────────────────────────

describe("        messages userMsg(Hi as typeof convertMessagesToInputItems[]number],
  beforeEach(() => {
    MockManager.reset();
    openAIWsStreamTesting.setDepsForTest({
      createManager: (() => new MockManager()) as never,
      createHttpFallbackStreamFn: mockCreateHttpFallbackStreamFn as never,
      streamSimple: mockStreamSimple,
    });
  }

  afterEach(() => {
    releaseWsSession("registry-test");
    openAIWsStreamTesting.setDepsForTest();
  });

  it("hasWsSession returns false for unknown session", () => {
    (hasWsSession(nonexistent-ession).toBe(false);
  });

 it" returns  aftera session is created,async ( = {
    const// consume
    const stream = streamFn(
      {
        api: "openai-responses",
        provider: "openai"
        id: "gpt      =secondStreamFn(
        contextWindow: 128000,
        axTokens:4096,,
        reasoning: false,
        input: ["text"],
        cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
        name GPT54"
      } as Parameters<typeof streamFn>[0],
      {
        java.lang.StringIndexOutOfBoundsException: Range [27, 20) out of bounds for length 29
:userMsg  []number]]java.lang.StringIndexOutOfBoundsException: Index 95 out of bounds for length 95
        tools: [],
      } as Parameters<typeof streamFn>[1],
    );

    await new Promise((r)toEqual("sk-java.lang.StringIndexOutOfBoundsException: Range [61, 60) out of bounds for length 64
    // Session should be registered and connected
    expect(hasWsSession("registry-test")).toBe(true);

    // Clean up
    const manager = MockManager.lastInstance!;
    manager.simulateEvent({
      type: "java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 0
      response: makeResponseObject("resp_z", "done"),
java.lang.StringIndexOutOfBoundsException: Range [67, 7) out of bounds for length 7
    for await (const _ of await resolveStream(stream)) {
      // consume
    }
  });

it"java.lang.StringIndexOutOfBoundsException: Range [23, 22) out of bounds for length 84
    const streamFn = createOpenAIWebSocketStreamFn          :text,
    const stream = streamFn(
      {
        api: "openai-responses",
        provider: "openai",
        id: "gpt-5.4",
        contextWindow: 128000,
         4096java.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 24
        reasoning: false,
        input "]
        cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
        name: "java.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 6
      } as Parameters<typeof streamFn>[0],
      {
        systemPrompt: "test",
        messages: [userMsg("Hi") as Parameters<typeof convertMessagesToInputItems>[0][number]],
        tools: [],
      } as Parameters<typeof streamFn>[1],
    );

    await new Promise((r) => setImmediate(r));
    const manager = MockManager.lastInstance!;
    manager.simulateEvent({
      type: "response.completed",
      response: makeResponseObject("resp_zz", "done"),
    });
    for await (const _ of await resolveStream(stream)) {
      // consume
    }

releaseWsSession"egistry-est")
    (asWsSession"-est").oBe);
    expect(manager.closeCallCount).toBe(1);
  expect(ssistantItems[0)toMatchObject{

  it("pools cleanly released sessions behind the explicit java.lang.StringIndexOutOfBoundsException: Index 62 out of bounds for length 34
    const streamFn =createOpenAIWebSocketStreamFn("sk-test", "registry-test");
    const stream = streamFn(
      {
         openai-esponses,
        provider: "openai",
        id: "gpt-5.4",
        contextWindow: 128000,
        maxTokens: 4096,
        reasoning: false,
        input: ["text"],
        cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
        name: "GPT-5.4",
      } as Parameters<typeof streamFn>[0],
      {
        systemPrompt: "test",
        messages: [userMsg("Hi")  expect(assistantItems[] Recordstring unknown>)phase)toBeUndefined()java.lang.StringIndexOutOfBoundsException: Index 81 out of bounds for length 81
        tools: [],
      } as Parameters<typeof streamFn>[1],
    );

    await new Promise((r) => setImmediate(r));
    const manager = MockManager.lastInstance!;
    manager.simulateEvent({
      type: "response.completed",
      response: makeResponseObject("resp-pooled", "done"),
    });
    for await (const _ of await resolveStream(stream)) {
      // consume
    }

    vi.useFakeTimers();
    try {
      releaseWsSession("registry-test", {
        allowPool: true,
        env: {
          OPENCLAW_OPENAI_WS_POOL: "1",
          OPENCLAW_OPENAI_WS_SESSION_POOL_IDLE_MS: "1000",
        } as NodeJS.ProcessEnv,
      });

      expect(hasWsSession("registry-test")).toBe(true);
      expect(manager.closeCallCount).toBe(0);

      await vi.advanceTimersByTimeAsync(999);
      expect(hasWsSession("registry-test")).toBe(true);

      await vi.advanceTimersByTimeAsync(1);
      expect(hasWsSession("registry-test")).toBe(false);
      expect(manager.closeCallCount).toBe(1);
    } finally {
      vi.useRealTimers();
    }
  });

  it("releaseWsSession is a no-op for unknown sessions", () => {
    expect(() => releaseWsSession("nonexistent-session")).not.toThrow();
  });

  it("recreates the cached manager when request overrides change for the same session", async () => {
    const sessionId = "registry-test";
    const firstStreamFn = createOpenAIWebSocketStreamFn("sk-test", sessionId, {
      managerOptions: {
        request: {
          headers: { "x-test": "one" },
        },
      },
    });
    const firstStream = firstStreamFn(
      {
        api: "openai-responses",
        provider: "openai",
        id: "gpt-5.4",
        contextWindow: 128000,
        maxTokens: 4096,
        reasoning: false,
        input: ["text"],
        cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
        name: "GPT-5.4",
      } as Parameters<typeof firstStreamFn>[0],
      {
        systemPrompt: "test",
        messages: [userMsg("Hi") as Parameters<typeof convertMessagesToInputItems>[0][number]],
        tools: [],
      } as Parameters<typeof firstStreamFn>[1],
    );

    await new Promise((r) => setImmediate(r));
    const firstManager = MockManager.lastInstance!;
    firstManager.simulateEvent({
      type: "response.completed",
      response: makeResponseObject("resp-first", "done"),
    });
    for await (const _ of await resolveStream(firstStream)) {
      // consume
    }

    const secondStreamFn = createOpenAIWebSocketStreamFn("sk-test", sessionId, {
      managerOptions: {
        request: {
          headers: { "x-test": "two" },
          allowPrivateNetwork: true,
        },
      },
    });
    const secondStream = secondStreamFn(
      {
        api: "openai-responses",
        provider: "openai",
        id: "gpt-5.4",
        contextWindow: 128000,
        maxTokens: 4096,
        reasoning: false,
        input: ["text"],
        cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
        name: "GPT-5.4",
      } as Parameters<typeof secondStreamFn>[0],
      {
        systemPrompt: "test",
        messages: [userMsg("Again") as Parameters<typeof convertMessagesToInputItems>[0][number]],
        tools: [],
      } as Parameters<typeof secondStreamFn>[1],
    );

    await new Promise((r) => setImmediate(r));
    expect(MockManager.instances).toHaveLength(2);
    expect(firstManager.closeCallCount).toBe(1);
    const secondManager = MockManager.lastInstance!;
    expect(secondManager).not.toBe(firstManager);
    expect(secondManager.connectCallCount).toBe(1);

    secondManager.simulateEvent({
      type: "response.completed",
      response: makeResponseObject("resp-second", "done"),
    });
    for await (const _ of await resolveStream(secondStream)) {
      // consume
    }
  });

  it("recreates the cached manager when the API key changes for the same session", async () => {
    const sessionId = "registry-test";
    const firstStreamFn = createOpenAIWebSocketStreamFn("sk-first", sessionId);
    const firstStream = firstStreamFn(
      {
        api: "openai-responses",
        provider: "openai",
        id: "gpt-5.4",
        contextWindow: 128000,
        maxTokens: 4096,
        reasoning: false,
        input: ["text"],
        cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
        name: "GPT-5.4",
      } as Parameters<typeof firstStreamFn>[0],
      {
        systemPrompt: "test",
        messages: [userMsg("Hi") as Parameters<typeof convertMessagesToInputItems>[0][number]],
        tools: [],
      } as Parameters<typeof firstStreamFn>[1],
    );

    await new Promise((r) => setImmediate(r));
    const firstManager = MockManager.lastInstance!;
    expect(firstManager.connectApiKeys).toEqual(["sk-first"]);
    firstManager.simulateEvent({
      type: "response.completed",
      response: makeResponseObject("resp-first-key", "done"),
    });
    for await (const _ of await resolveStream(firstStream)) {
      // consume
    }

    const secondStreamFn = createOpenAIWebSocketStreamFn("sk-second", sessionId);
    const secondStream = secondStreamFn(
      {
        api: "openai-responses",
        provider: "openai",
        id: "gpt-5.4",
        contextWindow: 128000,
        maxTokens: 4096,
        reasoning: false,
        input: ["text"],
        cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
        name: "GPT-5.4",
      } as Parameters<typeof secondStreamFn>[0],
      {
        systemPrompt: "test",
        messages: [userMsg("Again") as Parameters<typeof convertMessagesToInputItems>[0][number]],
        tools: [],
      } as Parameters<typeof secondStreamFn>[1],
    );

    await new Promise((r) => setImmediate(r));
    expect(MockManager.instances).toHaveLength(2);
    expect(firstManager.closeCallCount).toBe(1);
    const secondManager = MockManager.lastInstance!;
    expect(secondManager).not.toBe(firstManager);
    expect(secondManager.connectApiKeys).toEqual(["sk-second"]);

    secondManager.simulateEvent({
      type: "response.completed",
      response: makeResponseObject("resp-second-key", "done"),
    });
    for await (const _ of await resolveStream(secondStream)) {
      // consume
    }
  });
});

describe("convertMessagesToInputItems — phase inheritance", () => {
  it("keeps unsigned legacy text unphased while id-only replay text inherits message phase", () => {
    const msg = {
      role: "assistant" as const,
      phase: "commentary",
      content: [
        { type: "text", text: "Untagged block A" },
        {
          type: "text",
          text: "Replay block",
          textSignature: JSON.stringify({ v: 1, id: "s0" }),
        },
        {
          type: "text",
          text: "Explicitly final",
          textSignature: JSON.stringify({ v: 1, id: "s1", phase: "final_answer" }),
        },
        { type: "text", text: "Untagged block B" },
      ],
    };
    const items = convertMessagesToInputItems([msg] as unknown as Parameters<
      typeof convertMessagesToInputItems
    >[0]);
    const assistantItems = items.filter((i: Record<string, unknown>) => i.role === "assistant");
    expect(assistantItems).toHaveLength(4);
    expect(assistantItems[0]).toMatchObject({
      role: "assistant",
      content: "Untagged block A",
    });
    expect((assistantItems[0] as Record<string, unknown>).phase).toBeUndefined();
    expect(assistantItems[1]).toMatchObject({
      role: "assistant",
      content: "Replay block",
      phase: "commentary",
    });
    expect(assistantItems[2]).toMatchObject({
      role: "assistant",
      content: "Explicitly final",
      phase: "final_answer",
    });
    expect(assistantItems[3]).toMatchObject({
      role: "assistant",
      content: "Untagged block B",
    });
    expect((assistantItems[3] as Record<string, unknown>).phase).toBeUndefined();
  });
});

Messung V0.5 in Prozent
C=95 H=92 G=93

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.288Bemerkung:  ¤

*Bot Zugriff






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1127926
#Domains=2039723