Compare commits

..

21 Commits

Author SHA1 Message Date
Aiden Cline 91dad62ffd feat(codemode): add Web extension with policy-bound fetch 2026-09-15 16:10:49 -05:00
Dax aaed6eaa45 fix(tui): clarify collapsed shell output (#49232) 2026-09-15 17:00:57 -04:00
Aiden Cline 01443d5e00 fix(core): preserve job failure details (#49226) 2026-09-15 15:30:53 -05:00
opencode-agent[bot] 70fe186df4 test(app): wait for summary readiness (#49217)
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
2026-09-15 15:26:28 -05:00
opencode-agent[bot] fc731d6013 chore: update nix node_modules hashes 2026-09-15 20:10:17 +00:00
Aiden Cline 4cd6093e31 fix(codemode): keep one RegExp lastIndex (#49220) 2026-09-15 15:01:08 -05:00
Aiden Cline b693c222da feat(core): configure MCP OAuth authorization server metadata (#49126) 2026-09-15 14:52:30 -05:00
Aiden Cline e31ad14c1d fix(core): surface authorization cause in error message (#49214) 2026-09-15 14:38:15 -05:00
James Long 6c9115eb75 docs(www): restore regular body font weight (#49219) 2026-09-15 15:18:40 -04:00
Aiden Cline d9dfc5c1d6 refactor(codemode): extensions are host functions only (#49196) 2026-09-15 13:42:51 -05:00
opencode-agent[bot] c921067eb8 fix(client): regenerate model compatibility type (#49204)
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
2026-09-15 13:31:18 -05:00
James Long b76e56eca3 docs(www): adjust text and background colors (#49202) 2026-09-15 13:34:54 -04:00
James Long 3c3ef00b32 docs(www): use variable IBM Plex Mono and refine body text (#49200) 2026-09-15 13:28:13 -04:00
Aiden Cline d54fc305db feat(core): append model identity to system prompts via plugin (#49129) 2026-09-15 11:46:07 -05:00
James Long a4aaf18855 docs(www): refine typography and background (#49191) 2026-09-15 12:43:18 -04:00
Aiden Cline d0a902815d feat(plugin): add experimental WebSocket handshake hook (#49131) 2026-09-15 01:41:00 -05:00
Aiden Cline 27aaa9ce0e feat(ai): allowlist prompt_cache_key sending per provider (#49121) 2026-09-15 01:23:49 -05:00
Aiden Cline 0b15abbf9a refactor(codemode): make the host boundary JSON.stringify plus a short table (#49128) 2026-09-15 01:23:20 -05:00
Aiden Cline 0f22b86670 refactor(core): replace websocket flags with a single transport preference (#48423) 2026-09-15 00:55:43 -05:00
Aiden Cline 30243ddff9 fix(ai): always send type message on Responses input messages (#49110) 2026-09-15 00:24:35 -05:00
Aiden Cline 9ae714d49d feat(codemode): add Uint8Array, drop the Program prefix, and give built-ins one context (#49076) 2026-09-14 23:35:48 -05:00
192 changed files with 4428 additions and 2616 deletions
+1
View File
@@ -353,6 +353,7 @@
"@ff-labs/fff-node": "0.10.5",
"@lydell/node-pty": "catalog:",
"@modelcontextprotocol/client": "2.0.0",
"@modelcontextprotocol/core": "2.0.0",
"@opencode-ai/pty": "0.1.13",
"@opencode/ai": "workspace:*",
"@opencode/codemode": "workspace:*",
+4 -4
View File
@@ -1,8 +1,8 @@
{
"nodeModules": {
"x86_64-linux": "sha256-E5T4o3wNivOg8q4wRV7yE4CUNaUq4QNPxZsnvLaGUcg=",
"aarch64-linux": "sha256-xQQi7LgxInZQVCznASfa0Pm+cNGBo5j4Tfp0E/bKnNw=",
"aarch64-darwin": "sha256-qxb371dCf7WG09VvQE+HZ/x3EURpFIr11bdnQwGTHhw=",
"x86_64-darwin": "sha256-xi1qiYr41hzbgAQl+I3xdtnkiPx2SIcA/0OMpN78qXw="
"x86_64-linux": "sha256-U9IuP/ev6w4urvogOwQyl3rdumY6W4YaY18NkFaOVHU=",
"aarch64-linux": "sha256-Wc8OT2DRZpVo56KaoGE0Hsj1NDknakbWXO9w2qy6j+0=",
"aarch64-darwin": "sha256-wAea8+jajnMDxZ6XJL+Hsrf0621hwtBtWyD1+dS45dE=",
"x86_64-darwin": "sha256-g8PCNBSV6rO+VQjKU9AtYqj+r18o+fhLDXEQq+X2EZ4="
}
}
@@ -168,6 +168,10 @@ export const transport = <Body>(options: Options): Transport<Body, Prepared, str
}
})
: undefined
if (input.webSocket && !channel)
yield* Effect.logWarning(`${options.name} does not offer WebSocket for this endpoint; using HTTP`, {
url: parts.url,
})
return {
http: {
request: ProviderShared.jsonPost({ url: parts.url, body: parts.bodyText, headers: parts.headers }),
+6 -5
View File
@@ -67,7 +67,6 @@ type MessagePhase = Schema.Schema.Type<typeof MessagePhase>
export const MessageMetadata = Schema.Struct({
itemId: Schema.optional(Schema.String),
type: Schema.optional(Schema.Literal("message")),
status: Schema.optional(Schema.String),
phase: Schema.optional(MessagePhase),
})
@@ -174,12 +173,12 @@ type ConfigurationUpdate = Schema.Schema.Type<typeof ConfigurationUpdate>
export const InputItem = Schema.Union([
CompactionItem,
Schema.Struct({ role: Schema.tag("system"), content: Schema.String }),
Schema.Struct({ role: Schema.tag("developer"), content: Schema.String }),
Schema.Struct({ type: Schema.tag("message"), role: Schema.tag("system"), content: Schema.String }),
Schema.Struct({ type: Schema.tag("message"), role: Schema.tag("developer"), content: Schema.String }),
Schema.Struct({
type: Schema.tag("message"),
role: Schema.tag("user"),
content: Schema.Array(OpenResponsesInputContent),
type: Schema.optional(Schema.Literal("message")),
id: Schema.optional(Schema.String),
status: Schema.optional(Schema.String),
}),
@@ -666,6 +665,7 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (
continue
}
input.push({
type: "message",
role: "developer",
content: ProviderShared.joinText(yield* ProviderShared.systemUpdateText(adapter.name, message)),
})
@@ -675,7 +675,7 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (
if (message.role === "user") {
const content = yield* Effect.forEach(message.content, (part) => lowerUserContent(part, request, adapter))
if (content.length > 0)
input.push({ role: "user", content, type: metadata?.type, id: metadata?.itemId, status: metadata?.status })
input.push({ type: "message", role: "user", content, id: metadata?.itemId, status: metadata?.status })
continue
}
@@ -766,6 +766,7 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (
? part.result.value
: [{ type: "text", text: ProviderShared.toolResultText(part) }]
input.push({
type: "message",
role: "user",
content: yield* Effect.forEach(content, (item) => lowerHostedToolResultContentItem(item, request, adapter)),
})
+4 -1
View File
@@ -715,7 +715,10 @@ const detectZaiToolStream = (provider: string, baseURL: string | undefined, mode
const lowerOptions = (request: LLMRequest, supportsStore: boolean) => {
const options = OpenAIOptions.resolve(request)
const cacheKey = ProviderShared.promptCacheKey(request)
// Default off: strict providers 400 on unknown body fields, so only send
// the key where compatibility explicitly allows it. Header-based affinity
// (x-session-affinity, x-grok-conv-id, ...) is unaffected.
const cacheKey = (request.model.compatibility?.supportsPromptCacheKey ?? false) ? ProviderShared.promptCacheKey(request) : undefined
return {
...(supportsStore && options.store !== undefined ? { store: options.store } : {}),
// For providers that support `store`, ensure stateless `store:false` is sent
+1 -7
View File
@@ -1,4 +1,3 @@
import { Headers } from "effect/unstable/http"
import { Auth } from "../route/auth.js"
import { type AtLeastOne, type ProviderAuthOption } from "../route/auth-options.js"
import type { Route, RouteDefaultsInput, CompactionOperations } from "../route/client.js"
@@ -61,11 +60,6 @@ const responsesRoute = OpenAIResponses.route.with({
url.searchParams.delete("api-version")
return url.toString()
},
headers: (headers) => {
const apiKey = headers["api-key"]
if (!apiKey) return headers
return Headers.remove(Headers.set(headers, "authorization", `Bearer ${apiKey}`), "api-key")
},
}),
})
@@ -135,7 +129,7 @@ export const configure = (input: Config) => {
const chat = (modelID: string | ModelID) =>
configuredRoute(chatRoute, input, modelID)
.with(withOpenAIOptions(modelID, modelDefaults))
.model<OpenAIProviderOptionsInput>({ id: modelID })
.model<OpenAIProviderOptionsInput>({ id: modelID, compatibility: { supportsPromptCacheKey: true } })
return {
id,
+1 -1
View File
@@ -44,7 +44,7 @@ export const configure = (input: LanguageModelOptions = {}) => {
model: (modelID: string | ModelID) =>
configured.model<OpenAIProviderOptionsInput>({
id: modelID,
compatibility: { maxTokensField: "max_tokens", reasoningField: "reasoning", supportsStore: false },
compatibility: { maxTokensField: "max_tokens", reasoningField: "reasoning", supportsStore: false, supportsPromptCacheKey: true },
}),
configure,
}
+1 -1
View File
@@ -47,7 +47,7 @@ export const configure = (input: LanguageModelOptions = {}) => {
model: (modelID: string | ModelID) =>
configured.model<OpenAIProviderOptionsInput>({
id: modelID,
compatibility: { maxTokensField: "max_tokens", reasoningField: "reasoning_content", supportsStore: false },
compatibility: { maxTokensField: "max_tokens", reasoningField: "reasoning_content", supportsStore: false, supportsPromptCacheKey: true },
}),
configure,
}
+2 -1
View File
@@ -41,7 +41,8 @@ export const configure = (input: LanguageModelOptions = {}) => {
})
return {
id,
model: (modelID: string | ModelID) => configured.model<OpenAIProviderOptionsInput>({ id: modelID }),
model: (modelID: string | ModelID) =>
configured.model<OpenAIProviderOptionsInput>({ id: modelID, compatibility: { supportsPromptCacheKey: true } }),
configure,
}
}
+1 -1
View File
@@ -132,7 +132,7 @@ export const configure = (input: LanguageModelOptions = {}) => {
const chat = (modelID: string | ModelID) =>
configuredChat.model<OpenResponsesProviderOptionsInput>({
id: modelID,
compatibility: { maxTokensField: "max_completion_tokens", supportsStore: false },
compatibility: { maxTokensField: "max_completion_tokens", supportsStore: false, supportsPromptCacheKey: true },
})
const messages = (modelID: string | ModelID) =>
configuredMessages.model<MessagesOptionsInput>({
+1
View File
@@ -112,6 +112,7 @@ export const configure = (input: Config = {}) => {
maxTokensField: "max_tokens",
supportsStore: false,
supportsStrictMode: false,
supportsPromptCacheKey: true,
toolSchema: "moonshot",
reasoningField: "reasoning_content",
},
+4 -1
View File
@@ -91,7 +91,10 @@ export const configure = (input: Config = {}) => {
.with(withOpenAIOptions(id, modelDefaults, { textVerbosity: true }))
.model<OpenAIProviderOptionsInput>({ id })
const chat = (id: string | ModelID) =>
chatRoute.with(withOpenAIOptions(id, modelDefaults)).model<OpenAIProviderOptionsInput>({ id })
chatRoute.with(withOpenAIOptions(id, modelDefaults)).model<OpenAIProviderOptionsInput>({
id,
compatibility: { supportsPromptCacheKey: true },
})
const image = (modelID: string | ModelID) =>
OpenAIImages.model({
id: modelID,
+2 -1
View File
@@ -183,7 +183,8 @@ export const configure = (input: LanguageModelOptions = {}) => {
const route = configuredRoute(input)
return {
id,
model: (modelID: string | ModelID) => route.model<OpenRouterProviderOptionsInput>({ id: modelID }),
model: (modelID: string | ModelID) =>
route.model<OpenRouterProviderOptionsInput>({ id: modelID, compatibility: { supportsPromptCacheKey: true } }),
configure,
}
}
+3
View File
@@ -168,6 +168,9 @@ export class LanguageModelCompatibility extends Schema.Class<LanguageModelCompat
supportsStore: Schema.optional(Schema.Boolean),
supportsUsageInStreaming: Schema.optional(Schema.Boolean),
supportsStrictMode: Schema.optional(Schema.Boolean),
// Accepts `prompt_cache_key` in the Chat Completions body. Chat omits the
// key unless this is set; session-affinity headers still flow regardless.
supportsPromptCacheKey: Schema.optional(Schema.Boolean),
zaiToolStream: Schema.optional(Schema.Boolean),
requireSignature: Schema.optional(Schema.Boolean),
/** Supports Anthropic's thinking-prefix mismatch controls. Overrides model-ID detection. */
+8 -8
View File
@@ -263,9 +263,9 @@ describe("OpenAI Responses effort updates", () => {
expect(prepared.body.reasoning).toEqual({ effort: "high" })
expect(prepared.body.input).toEqual([
{ role: "user", content: [{ type: "input_text", text: "Before." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Before." }] },
{ type: "configuration_update", reasoning: { effort: "low" } },
{ role: "user", content: [{ type: "input_text", text: "After." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "After." }] },
])
}),
)
@@ -287,9 +287,9 @@ describe("OpenAI Responses effort updates", () => {
expect(prepared.body.reasoning).toEqual({ effort: "medium" })
expect(prepared.body.input).toEqual([
{ role: "user", content: [{ type: "input_text", text: "Before." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Before." }] },
{ type: "configuration_update", reasoning: { effort: "xhigh" } },
{ role: "user", content: [{ type: "input_text", text: "After." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "After." }] },
])
}),
)
@@ -404,9 +404,9 @@ describe("OpenAI Responses effort updates", () => {
const body = JSON.parse(text)
expect(body.reasoning).toEqual({ effort: "high" })
expect(body.input).toEqual([
{ role: "user", content: [{ type: "input_text", text: "Before." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Before." }] },
{ type: "configuration_update", reasoning: { effort: "low" } },
{ role: "user", content: [{ type: "input_text", text: "After." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "After." }] },
{ type: "compaction_trigger" },
])
return respond(sseEvents({ type: "response.completed", response: { id: "resp_1", output: [checkpoint] } }), {
@@ -426,8 +426,8 @@ describe("OpenAI Responses effort updates", () => {
Effect.sync(() => {
expect(new URL(request.url).pathname).toEndWith("/responses/compact")
expect(JSON.parse(text).input).toEqual([
{ role: "user", content: [{ type: "input_text", text: "Before." }] },
{ role: "user", content: [{ type: "input_text", text: "After." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Before." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "After." }] },
])
return respond(JSON.stringify({ object: "response.compaction", output: [checkpoint] }))
}),
@@ -10,7 +10,7 @@
"usage"
],
"name": "alibaba-responses/qwen-3-7-plus-streams-thinking-disabled",
"recordedAt": "2026-09-08T03:11:07.495Z"
"recordedAt": "2026-09-15T04:23:38.279Z"
},
"interactions": [
{
@@ -21,14 +21,14 @@
"headers": {
"content-type": "application/json"
},
"body": "{\"model\":\"qwen3.7-plus\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is 173 multiplied by 219? Reply with only the final integer.\"}]}],\"max_output_tokens\":4096,\"enable_thinking\":false,\"stream\":true}"
"body": "{\"model\":\"qwen3.7-plus\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is 173 multiplied by 219? Reply with only the final integer.\"}]}],\"max_output_tokens\":4096,\"enable_thinking\":false,\"stream\":true}"
},
"response": {
"status": 200,
"headers": {
"content-type": "text/event-stream;charset=UTF-8"
},
"body": "id:1\nevent:response.created\n:HTTP_STATUS/200\ndata:{\"sequence_number\":0,\"type\":\"response.created\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"created_at\":1788837067,\"store\":true,\"tools\":[],\"output\":[],\"top_p\":1.0,\"completed_at\":1788837067,\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.7-plus\",\"service_tier\":\"default\",\"id\":\"resp_493607bf-baef-9ced-9e36-2e2a583b6177\",\"max_output_tokens\":4096,\"object\":\"response\",\"status\":\"queued\"}}\n\nid:2\nevent:response.in_progress\n:HTTP_STATUS/200\ndata:{\"sequence_number\":1,\"type\":\"response.in_progress\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"created_at\":1788837067,\"store\":true,\"tools\":[],\"output\":[],\"top_p\":1.0,\"completed_at\":1788837067,\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.7-plus\",\"service_tier\":\"default\",\"id\":\"resp_493607bf-baef-9ced-9e36-2e2a583b6177\",\"max_output_tokens\":4096,\"object\":\"response\",\"status\":\"in_progress\"}}\n\nid:3\nevent:response.output_item.added\n:HTTP_STATUS/200\ndata:{\"sequence_number\":2,\"item\":{\"id\":\"msg_509c6dec-e527-41b5-a9c9-7f02fecb7a3f\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[],\"status\":\"in_progress\"},\"output_index\":0,\"type\":\"response.output_item.added\"}\n\nid:4\nevent:response.content_part.added\n:HTTP_STATUS/200\ndata:{\"sequence_number\":3,\"output_index\":0,\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_509c6dec-e527-41b5-a9c9-7f02fecb7a3f\",\"part\":{\"type\":\"output_text\",\"annotations\":[],\"text\":\"\"}}\n\nid:5\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":4,\"content_index\":0,\"item_id\":\"msg_509c6dec-e527-41b5-a9c9-7f02fecb7a3f\",\"delta\":\"3\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:6\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":5,\"content_index\":0,\"item_id\":\"msg_509c6dec-e527-41b5-a9c9-7f02fecb7a3f\",\"delta\":\"7887\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:7\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":6,\"content_index\":0,\"item_id\":\"msg_509c6dec-e527-41b5-a9c9-7f02fecb7a3f\",\"delta\":\"\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:8\nevent:response.output_text.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":7,\"content_index\":0,\"item_id\":\"msg_509c6dec-e527-41b5-a9c9-7f02fecb7a3f\",\"text\":\"37887\",\"output_index\":0,\"type\":\"response.output_text.done\",\"logprobs\":[]}\n\nid:9\nevent:response.content_part.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":8,\"output_index\":0,\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_509c6dec-e527-41b5-a9c9-7f02fecb7a3f\",\"part\":{\"type\":\"output_text\",\"annotations\":[],\"text\":\"37887\"}}\n\nid:10\nevent:response.output_item.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":9,\"item\":{\"id\":\"msg_509c6dec-e527-41b5-a9c9-7f02fecb7a3f\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"text\":\"37887\"}],\"status\":\"completed\"},\"output_index\":0,\"type\":\"response.output_item.done\"}\n\nid:11\nevent:response.completed\n:HTTP_STATUS/200\ndata:{\"sequence_number\":10,\"type\":\"response.completed\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"usage\":{\"total_tokens\":73,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens\":5,\"input_tokens\":68,\"output_tokens_details\":{\"reasoning_tokens\":0},\"x_details\":[{\"total_tokens\":73,\"x_billing_type\":\"response_api\",\"output_tokens\":5,\"input_tokens\":68,\"prompt_tokens_details\":{\"cached_tokens\":0}}]},\"created_at\":1788837067,\"store\":true,\"tools\":[],\"output\":[{\"id\":\"msg_509c6dec-e527-41b5-a9c9-7f02fecb7a3f\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"text\":\"37887\"}],\"status\":\"completed\"}],\"top_p\":1.0,\"completed_at\":1788837067,\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.7-plus\",\"service_tier\":\"default\",\"id\":\"resp_493607bf-baef-9ced-9e36-2e2a583b6177\",\"max_output_tokens\":4096,\"object\":\"response\",\"status\":\"completed\"}}\n\n"
"body": "id:1\nevent:response.created\n:HTTP_STATUS/200\ndata:{\"sequence_number\":0,\"type\":\"response.created\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"created_at\":1789446218,\"store\":true,\"tools\":[],\"output\":[],\"top_p\":1.0,\"completed_at\":1789446218,\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.7-plus\",\"service_tier\":\"default\",\"id\":\"resp_d854ed07-da8a-96a3-bbef-73a033e388b1\",\"max_output_tokens\":4096,\"object\":\"response\",\"status\":\"queued\"}}\n\nid:2\nevent:response.in_progress\n:HTTP_STATUS/200\ndata:{\"sequence_number\":1,\"type\":\"response.in_progress\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"created_at\":1789446218,\"store\":true,\"tools\":[],\"output\":[],\"top_p\":1.0,\"completed_at\":1789446218,\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.7-plus\",\"service_tier\":\"default\",\"id\":\"resp_d854ed07-da8a-96a3-bbef-73a033e388b1\",\"max_output_tokens\":4096,\"object\":\"response\",\"status\":\"in_progress\"}}\n\nid:3\nevent:response.output_item.added\n:HTTP_STATUS/200\ndata:{\"sequence_number\":2,\"item\":{\"id\":\"msg_a65411dd-b721-4232-be0c-eba901cd2241\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[],\"status\":\"in_progress\"},\"output_index\":0,\"type\":\"response.output_item.added\"}\n\nid:4\nevent:response.content_part.added\n:HTTP_STATUS/200\ndata:{\"sequence_number\":3,\"output_index\":0,\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_a65411dd-b721-4232-be0c-eba901cd2241\",\"part\":{\"type\":\"output_text\",\"annotations\":[],\"text\":\"\"}}\n\nid:5\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":4,\"content_index\":0,\"item_id\":\"msg_a65411dd-b721-4232-be0c-eba901cd2241\",\"delta\":\"3\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:6\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":5,\"content_index\":0,\"item_id\":\"msg_a65411dd-b721-4232-be0c-eba901cd2241\",\"delta\":\"788\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:7\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":6,\"content_index\":0,\"item_id\":\"msg_a65411dd-b721-4232-be0c-eba901cd2241\",\"delta\":\"7\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:8\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":7,\"content_index\":0,\"item_id\":\"msg_a65411dd-b721-4232-be0c-eba901cd2241\",\"delta\":\"\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:9\nevent:response.output_text.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":8,\"content_index\":0,\"item_id\":\"msg_a65411dd-b721-4232-be0c-eba901cd2241\",\"text\":\"37887\",\"output_index\":0,\"type\":\"response.output_text.done\",\"logprobs\":[]}\n\nid:10\nevent:response.content_part.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":9,\"output_index\":0,\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_a65411dd-b721-4232-be0c-eba901cd2241\",\"part\":{\"type\":\"output_text\",\"annotations\":[],\"text\":\"37887\"}}\n\nid:11\nevent:response.output_item.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":10,\"item\":{\"id\":\"msg_a65411dd-b721-4232-be0c-eba901cd2241\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"text\":\"37887\"}],\"status\":\"completed\"},\"output_index\":0,\"type\":\"response.output_item.done\"}\n\nid:12\nevent:response.completed\n:HTTP_STATUS/200\ndata:{\"sequence_number\":11,\"type\":\"response.completed\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"usage\":{\"total_tokens\":73,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens\":5,\"input_tokens\":68,\"output_tokens_details\":{\"reasoning_tokens\":0},\"x_details\":[{\"total_tokens\":73,\"x_billing_type\":\"response_api\",\"output_tokens\":5,\"input_tokens\":68,\"prompt_tokens_details\":{\"cached_tokens\":0}}]},\"created_at\":1789446218,\"store\":true,\"tools\":[],\"output\":[{\"id\":\"msg_a65411dd-b721-4232-be0c-eba901cd2241\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"text\":\"37887\"}],\"status\":\"completed\"}],\"top_p\":1.0,\"completed_at\":1789446218,\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.7-plus\",\"service_tier\":\"default\",\"id\":\"resp_d854ed07-da8a-96a3-bbef-73a033e388b1\",\"max_output_tokens\":4096,\"object\":\"response\",\"status\":\"completed\"}}\n\n"
}
}
]
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -10,7 +10,7 @@
"storage"
],
"name": "alibaba-responses/qwen-3-8-max-continues-a-stored-response",
"recordedAt": "2026-09-08T03:12:42.429Z"
"recordedAt": "2026-09-15T04:23:50.347Z"
},
"interactions": [
{
@@ -21,14 +21,14 @@
"headers": {
"content-type": "application/json"
},
"body": "{\"model\":\"qwen3.8-max\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Remember the password word apricot. Reply OK.\"}]}],\"store\":true,\"reasoning\":{\"effort\":\"none\"},\"max_output_tokens\":1024,\"stream\":true}"
"body": "{\"model\":\"qwen3.8-max\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Remember the password word apricot. Reply OK.\"}]}],\"store\":true,\"reasoning\":{\"effort\":\"none\"},\"max_output_tokens\":1024,\"stream\":true}"
},
"response": {
"status": 200,
"headers": {
"content-type": "text/event-stream;charset=UTF-8"
},
"body": "id:1\nevent:response.created\n:HTTP_STATUS/200\ndata:{\"sequence_number\":0,\"type\":\"response.created\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"reasoning\":{\"effort\":\"none\"},\"created_at\":1788837161,\"store\":true,\"tools\":[],\"output\":[],\"top_p\":1.0,\"completed_at\":1788837161,\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.8-max\",\"service_tier\":\"default\",\"id\":\"resp_97f8cd28-c7ab-9d51-9c14-4fc531decdc8\",\"max_output_tokens\":1024,\"object\":\"response\",\"status\":\"queued\"}}\n\nid:2\nevent:response.in_progress\n:HTTP_STATUS/200\ndata:{\"sequence_number\":1,\"type\":\"response.in_progress\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"reasoning\":{\"effort\":\"none\"},\"created_at\":1788837161,\"store\":true,\"tools\":[],\"output\":[],\"top_p\":1.0,\"completed_at\":1788837161,\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.8-max\",\"service_tier\":\"default\",\"id\":\"resp_97f8cd28-c7ab-9d51-9c14-4fc531decdc8\",\"max_output_tokens\":1024,\"object\":\"response\",\"status\":\"in_progress\"}}\n\nid:3\nevent:response.output_item.added\n:HTTP_STATUS/200\ndata:{\"sequence_number\":2,\"item\":{\"id\":\"msg_a908ad8e-6220-444b-880c-02aecbaf8fac\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[],\"status\":\"in_progress\"},\"output_index\":0,\"type\":\"response.output_item.added\"}\n\nid:4\nevent:response.content_part.added\n:HTTP_STATUS/200\ndata:{\"sequence_number\":3,\"output_index\":0,\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_a908ad8e-6220-444b-880c-02aecbaf8fac\",\"part\":{\"type\":\"output_text\",\"annotations\":[],\"text\":\"\"}}\n\nid:5\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":4,\"content_index\":0,\"item_id\":\"msg_a908ad8e-6220-444b-880c-02aecbaf8fac\",\"delta\":\"OK\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:6\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":5,\"content_index\":0,\"item_id\":\"msg_a908ad8e-6220-444b-880c-02aecbaf8fac\",\"delta\":\".\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:7\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":6,\"content_index\":0,\"item_id\":\"msg_a908ad8e-6220-444b-880c-02aecbaf8fac\",\"delta\":\"\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:8\nevent:response.output_text.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":7,\"content_index\":0,\"item_id\":\"msg_a908ad8e-6220-444b-880c-02aecbaf8fac\",\"text\":\"OK.\",\"output_index\":0,\"type\":\"response.output_text.done\",\"logprobs\":[]}\n\nid:9\nevent:response.content_part.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":8,\"output_index\":0,\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_a908ad8e-6220-444b-880c-02aecbaf8fac\",\"part\":{\"type\":\"output_text\",\"annotations\":[],\"text\":\"OK.\"}}\n\nid:10\nevent:response.output_item.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":9,\"item\":{\"id\":\"msg_a908ad8e-6220-444b-880c-02aecbaf8fac\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"text\":\"OK.\"}],\"status\":\"completed\"},\"output_index\":0,\"type\":\"response.output_item.done\"}\n\nid:11\nevent:response.completed\n:HTTP_STATUS/200\ndata:{\"sequence_number\":10,\"type\":\"response.completed\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"reasoning\":{\"effort\":\"none\"},\"usage\":{\"total_tokens\":60,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens\":2,\"input_tokens\":58,\"output_tokens_details\":{\"reasoning_tokens\":0},\"x_details\":[{\"total_tokens\":60,\"x_billing_type\":\"response_api\",\"output_tokens\":2,\"input_tokens\":58,\"prompt_tokens_details\":{\"cached_tokens\":0}}]},\"created_at\":1788837161,\"store\":true,\"tools\":[],\"output\":[{\"id\":\"msg_a908ad8e-6220-444b-880c-02aecbaf8fac\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"text\":\"OK.\"}],\"status\":\"completed\"}],\"top_p\":1.0,\"completed_at\":1788837161,\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.8-max\",\"service_tier\":\"default\",\"id\":\"resp_97f8cd28-c7ab-9d51-9c14-4fc531decdc8\",\"max_output_tokens\":1024,\"object\":\"response\",\"status\":\"completed\"}}\n\n"
"body": "id:1\nevent:response.created\n:HTTP_STATUS/200\ndata:{\"sequence_number\":0,\"type\":\"response.created\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"reasoning\":{\"effort\":\"none\"},\"created_at\":1789446229,\"store\":true,\"tools\":[],\"output\":[],\"top_p\":1.0,\"completed_at\":1789446229,\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.8-max\",\"service_tier\":\"default\",\"id\":\"resp_69640198-a315-9e88-9b70-679d545ee9ca\",\"max_output_tokens\":1024,\"object\":\"response\",\"status\":\"queued\"}}\n\nid:2\nevent:response.in_progress\n:HTTP_STATUS/200\ndata:{\"sequence_number\":1,\"type\":\"response.in_progress\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"reasoning\":{\"effort\":\"none\"},\"created_at\":1789446229,\"store\":true,\"tools\":[],\"output\":[],\"top_p\":1.0,\"completed_at\":1789446229,\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.8-max\",\"service_tier\":\"default\",\"id\":\"resp_69640198-a315-9e88-9b70-679d545ee9ca\",\"max_output_tokens\":1024,\"object\":\"response\",\"status\":\"in_progress\"}}\n\nid:3\nevent:response.output_item.added\n:HTTP_STATUS/200\ndata:{\"sequence_number\":2,\"item\":{\"id\":\"msg_68f76c07-ac47-483d-a90d-da5972f7af3d\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[],\"status\":\"in_progress\"},\"output_index\":0,\"type\":\"response.output_item.added\"}\n\nid:4\nevent:response.content_part.added\n:HTTP_STATUS/200\ndata:{\"sequence_number\":3,\"output_index\":0,\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_68f76c07-ac47-483d-a90d-da5972f7af3d\",\"part\":{\"type\":\"output_text\",\"annotations\":[],\"text\":\"\"}}\n\nid:5\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":4,\"content_index\":0,\"item_id\":\"msg_68f76c07-ac47-483d-a90d-da5972f7af3d\",\"delta\":\"OK\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:6\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":5,\"content_index\":0,\"item_id\":\"msg_68f76c07-ac47-483d-a90d-da5972f7af3d\",\"delta\":\".\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:7\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":6,\"content_index\":0,\"item_id\":\"msg_68f76c07-ac47-483d-a90d-da5972f7af3d\",\"delta\":\"\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:8\nevent:response.output_text.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":7,\"content_index\":0,\"item_id\":\"msg_68f76c07-ac47-483d-a90d-da5972f7af3d\",\"text\":\"OK.\",\"output_index\":0,\"type\":\"response.output_text.done\",\"logprobs\":[]}\n\nid:9\nevent:response.content_part.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":8,\"output_index\":0,\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_68f76c07-ac47-483d-a90d-da5972f7af3d\",\"part\":{\"type\":\"output_text\",\"annotations\":[],\"text\":\"OK.\"}}\n\nid:10\nevent:response.output_item.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":9,\"item\":{\"id\":\"msg_68f76c07-ac47-483d-a90d-da5972f7af3d\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"text\":\"OK.\"}],\"status\":\"completed\"},\"output_index\":0,\"type\":\"response.output_item.done\"}\n\nid:11\nevent:response.completed\n:HTTP_STATUS/200\ndata:{\"sequence_number\":10,\"type\":\"response.completed\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"reasoning\":{\"effort\":\"none\"},\"usage\":{\"total_tokens\":60,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens\":2,\"input_tokens\":58,\"output_tokens_details\":{\"reasoning_tokens\":0},\"x_details\":[{\"total_tokens\":60,\"x_billing_type\":\"response_api\",\"output_tokens\":2,\"input_tokens\":58,\"prompt_tokens_details\":{\"cached_tokens\":0}}]},\"created_at\":1789446229,\"store\":true,\"tools\":[],\"output\":[{\"id\":\"msg_68f76c07-ac47-483d-a90d-da5972f7af3d\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"text\":\"OK.\"}],\"status\":\"completed\"}],\"top_p\":1.0,\"completed_at\":1789446229,\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.8-max\",\"service_tier\":\"default\",\"id\":\"resp_69640198-a315-9e88-9b70-679d545ee9ca\",\"max_output_tokens\":1024,\"object\":\"response\",\"status\":\"completed\"}}\n\n"
}
},
{
@@ -39,14 +39,14 @@
"headers": {
"content-type": "application/json"
},
"body": "{\"model\":\"qwen3.8-max\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What word did I ask you to remember? Reply with only the word.\"}]}],\"store\":true,\"reasoning\":{\"effort\":\"none\"},\"max_output_tokens\":1024,\"previous_response_id\":\"resp_97f8cd28-c7ab-9d51-9c14-4fc531decdc8\",\"stream\":true}"
"body": "{\"model\":\"qwen3.8-max\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What word did I ask you to remember? Reply with only the word.\"}]}],\"store\":true,\"reasoning\":{\"effort\":\"none\"},\"max_output_tokens\":1024,\"previous_response_id\":\"resp_69640198-a315-9e88-9b70-679d545ee9ca\",\"stream\":true}"
},
"response": {
"status": 200,
"headers": {
"content-type": "text/event-stream;charset=UTF-8"
},
"body": "id:1\nevent:response.created\n:HTTP_STATUS/200\ndata:{\"sequence_number\":0,\"type\":\"response.created\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"reasoning\":{\"effort\":\"none\"},\"created_at\":1788837162,\"store\":true,\"tools\":[],\"output\":[],\"top_p\":1.0,\"completed_at\":1788837162,\"previous_response_id\":\"resp_97f8cd28-c7ab-9d51-9c14-4fc531decdc8\",\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.8-max\",\"service_tier\":\"default\",\"id\":\"resp_1d81d34c-d3ec-9f98-ab59-5d4641d4217a\",\"max_output_tokens\":1024,\"object\":\"response\",\"status\":\"queued\"}}\n\nid:2\nevent:response.in_progress\n:HTTP_STATUS/200\ndata:{\"sequence_number\":1,\"type\":\"response.in_progress\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"reasoning\":{\"effort\":\"none\"},\"created_at\":1788837162,\"store\":true,\"tools\":[],\"output\":[],\"top_p\":1.0,\"completed_at\":1788837162,\"previous_response_id\":\"resp_97f8cd28-c7ab-9d51-9c14-4fc531decdc8\",\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.8-max\",\"service_tier\":\"default\",\"id\":\"resp_1d81d34c-d3ec-9f98-ab59-5d4641d4217a\",\"max_output_tokens\":1024,\"object\":\"response\",\"status\":\"in_progress\"}}\n\nid:3\nevent:response.output_item.added\n:HTTP_STATUS/200\ndata:{\"sequence_number\":2,\"item\":{\"id\":\"msg_7bc50c33-8fbd-4b10-91fc-4f2533bb2091\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[],\"status\":\"in_progress\"},\"output_index\":0,\"type\":\"response.output_item.added\"}\n\nid:4\nevent:response.content_part.added\n:HTTP_STATUS/200\ndata:{\"sequence_number\":3,\"output_index\":0,\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_7bc50c33-8fbd-4b10-91fc-4f2533bb2091\",\"part\":{\"type\":\"output_text\",\"annotations\":[],\"text\":\"\"}}\n\nid:5\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":4,\"content_index\":0,\"item_id\":\"msg_7bc50c33-8fbd-4b10-91fc-4f2533bb2091\",\"delta\":\"ap\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:6\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":5,\"content_index\":0,\"item_id\":\"msg_7bc50c33-8fbd-4b10-91fc-4f2533bb2091\",\"delta\":\"ricot\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:7\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":6,\"content_index\":0,\"item_id\":\"msg_7bc50c33-8fbd-4b10-91fc-4f2533bb2091\",\"delta\":\"\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:8\nevent:response.output_text.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":7,\"content_index\":0,\"item_id\":\"msg_7bc50c33-8fbd-4b10-91fc-4f2533bb2091\",\"text\":\"apricot\",\"output_index\":0,\"type\":\"response.output_text.done\",\"logprobs\":[]}\n\nid:9\nevent:response.content_part.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":8,\"output_index\":0,\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_7bc50c33-8fbd-4b10-91fc-4f2533bb2091\",\"part\":{\"type\":\"output_text\",\"annotations\":[],\"text\":\"apricot\"}}\n\nid:10\nevent:response.output_item.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":9,\"item\":{\"id\":\"msg_7bc50c33-8fbd-4b10-91fc-4f2533bb2091\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"text\":\"apricot\"}],\"status\":\"completed\"},\"output_index\":0,\"type\":\"response.output_item.done\"}\n\nid:11\nevent:response.completed\n:HTTP_STATUS/200\ndata:{\"sequence_number\":10,\"type\":\"response.completed\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"reasoning\":{\"effort\":\"none\"},\"usage\":{\"total_tokens\":92,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens\":3,\"input_tokens\":89,\"output_tokens_details\":{\"reasoning_tokens\":0},\"x_details\":[{\"total_tokens\":92,\"x_billing_type\":\"response_api\",\"output_tokens\":3,\"input_tokens\":89,\"prompt_tokens_details\":{\"cached_tokens\":0}}]},\"created_at\":1788837162,\"store\":true,\"tools\":[],\"output\":[{\"id\":\"msg_7bc50c33-8fbd-4b10-91fc-4f2533bb2091\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"text\":\"apricot\"}],\"status\":\"completed\"}],\"top_p\":1.0,\"completed_at\":1788837162,\"previous_response_id\":\"resp_97f8cd28-c7ab-9d51-9c14-4fc531decdc8\",\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.8-max\",\"service_tier\":\"default\",\"id\":\"resp_1d81d34c-d3ec-9f98-ab59-5d4641d4217a\",\"max_output_tokens\":1024,\"object\":\"response\",\"status\":\"completed\"}}\n\n"
"body": "id:1\nevent:response.created\n:HTTP_STATUS/200\ndata:{\"sequence_number\":0,\"type\":\"response.created\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"reasoning\":{\"effort\":\"none\"},\"created_at\":1789446230,\"store\":true,\"tools\":[],\"output\":[],\"top_p\":1.0,\"completed_at\":1789446230,\"previous_response_id\":\"resp_69640198-a315-9e88-9b70-679d545ee9ca\",\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.8-max\",\"service_tier\":\"default\",\"id\":\"resp_2c1473fa-ac55-9733-817f-30994dd0a4e9\",\"max_output_tokens\":1024,\"object\":\"response\",\"status\":\"queued\"}}\n\nid:2\nevent:response.in_progress\n:HTTP_STATUS/200\ndata:{\"sequence_number\":1,\"type\":\"response.in_progress\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"reasoning\":{\"effort\":\"none\"},\"created_at\":1789446230,\"store\":true,\"tools\":[],\"output\":[],\"top_p\":1.0,\"completed_at\":1789446230,\"previous_response_id\":\"resp_69640198-a315-9e88-9b70-679d545ee9ca\",\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.8-max\",\"service_tier\":\"default\",\"id\":\"resp_2c1473fa-ac55-9733-817f-30994dd0a4e9\",\"max_output_tokens\":1024,\"object\":\"response\",\"status\":\"in_progress\"}}\n\nid:3\nevent:response.output_item.added\n:HTTP_STATUS/200\ndata:{\"sequence_number\":2,\"item\":{\"id\":\"msg_7162b038-5ed9-4abc-86d2-0620bfa653ad\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[],\"status\":\"in_progress\"},\"output_index\":0,\"type\":\"response.output_item.added\"}\n\nid:4\nevent:response.content_part.added\n:HTTP_STATUS/200\ndata:{\"sequence_number\":3,\"output_index\":0,\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_7162b038-5ed9-4abc-86d2-0620bfa653ad\",\"part\":{\"type\":\"output_text\",\"annotations\":[],\"text\":\"\"}}\n\nid:5\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":4,\"content_index\":0,\"item_id\":\"msg_7162b038-5ed9-4abc-86d2-0620bfa653ad\",\"delta\":\"ap\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:6\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":5,\"content_index\":0,\"item_id\":\"msg_7162b038-5ed9-4abc-86d2-0620bfa653ad\",\"delta\":\"ricot\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:7\nevent:response.output_text.delta\n:HTTP_STATUS/200\ndata:{\"sequence_number\":6,\"content_index\":0,\"item_id\":\"msg_7162b038-5ed9-4abc-86d2-0620bfa653ad\",\"delta\":\"\",\"output_index\":0,\"type\":\"response.output_text.delta\",\"logprobs\":[]}\n\nid:8\nevent:response.output_text.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":7,\"content_index\":0,\"item_id\":\"msg_7162b038-5ed9-4abc-86d2-0620bfa653ad\",\"text\":\"apricot\",\"output_index\":0,\"type\":\"response.output_text.done\",\"logprobs\":[]}\n\nid:9\nevent:response.content_part.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":8,\"output_index\":0,\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_7162b038-5ed9-4abc-86d2-0620bfa653ad\",\"part\":{\"type\":\"output_text\",\"annotations\":[],\"text\":\"apricot\"}}\n\nid:10\nevent:response.output_item.done\n:HTTP_STATUS/200\ndata:{\"sequence_number\":9,\"item\":{\"id\":\"msg_7162b038-5ed9-4abc-86d2-0620bfa653ad\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"text\":\"apricot\"}],\"status\":\"completed\"},\"output_index\":0,\"type\":\"response.output_item.done\"}\n\nid:11\nevent:response.completed\n:HTTP_STATUS/200\ndata:{\"sequence_number\":10,\"type\":\"response.completed\",\"response\":{\"top_logprobs\":0,\"metadata\":{},\"presence_penalty\":0.0,\"reasoning\":{\"effort\":\"none\"},\"usage\":{\"total_tokens\":92,\"input_tokens_details\":{\"cached_tokens\":0},\"output_tokens\":3,\"input_tokens\":89,\"output_tokens_details\":{\"reasoning_tokens\":0},\"x_details\":[{\"total_tokens\":92,\"x_billing_type\":\"response_api\",\"output_tokens\":3,\"input_tokens\":89,\"prompt_tokens_details\":{\"cached_tokens\":0}}]},\"created_at\":1789446230,\"store\":true,\"tools\":[],\"output\":[{\"id\":\"msg_7162b038-5ed9-4abc-86d2-0620bfa653ad\",\"role\":\"assistant\",\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"text\":\"apricot\"}],\"status\":\"completed\"}],\"top_p\":1.0,\"completed_at\":1789446230,\"previous_response_id\":\"resp_69640198-a315-9e88-9b70-679d545ee9ca\",\"frequency_penalty\":0.0,\"parallel_tool_calls\":true,\"background\":false,\"temperature\":1.0,\"tool_choice\":\"auto\",\"model\":\"qwen3.8-max\",\"service_tier\":\"default\",\"id\":\"resp_2c1473fa-ac55-9733-817f-30994dd0a4e9\",\"max_output_tokens\":1024,\"object\":\"response\",\"status\":\"completed\"}}\n\n"
}
}
]
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -11,7 +11,7 @@
"thinking-off"
],
"name": "minimax-responses/m3-streams-text-with-effort-none",
"recordedAt": "2026-09-07T16:53:21.223Z"
"recordedAt": "2026-09-15T04:25:23.202Z"
},
"interactions": [
{
@@ -22,14 +22,14 @@
"headers": {
"content-type": "application/json"
},
"body": "{\"model\":\"MiniMax-M3\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is 173 multiplied by 219? Reply with only the final integer.\"}]}],\"reasoning\":{\"effort\":\"none\"},\"max_output_tokens\":1536,\"stream\":true}"
"body": "{\"model\":\"MiniMax-M3\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is 173 multiplied by 219? Reply with only the final integer.\"}]}],\"reasoning\":{\"effort\":\"none\"},\"max_output_tokens\":1536,\"stream\":true}"
},
"response": {
"status": 200,
"headers": {
"content-type": "text/event-stream; charset=utf-8"
},
"body": "event: response.created\ndata: {\"type\":\"response.created\",\"sequence_number\":0,\"response\":{\"id\":\"40d878e9c7ecc67905d069cdf9bec7c0\",\"object\":\"response\",\"created_at\":1788800001,\"model\":\"MiniMax-M3\",\"status\":\"in_progress\",\"output\":[],\"output_text\":null,\"usage\":null,\"error\":null,\"incomplete_details\":null,\"instructions\":null,\"metadata\":{},\"tools\":null,\"tool_choice\":\"auto\",\"temperature\":1,\"top_p\":0.95,\"text\":{\"format\":{\"type\":\"text\"}},\"reasoning\":{\"effort\":\"none\",\"summary\":null},\"max_output_tokens\":1536,\"parallel_tool_calls\":true,\"previous_response_id\":null,\"conversation\":null,\"store\":false,\"service_tier\":\"standard\",\"safety_identifier\":null,\"truncation\":\"disabled\"}}\n\nevent: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"sequence_number\":1,\"response\":{\"id\":\"40d878e9c7ecc67905d069cdf9bec7c0\",\"object\":\"response\",\"created_at\":1788800001,\"model\":\"MiniMax-M3\",\"status\":\"in_progress\",\"output\":[],\"output_text\":null,\"usage\":null,\"error\":null,\"incomplete_details\":null,\"instructions\":null,\"metadata\":{},\"tools\":null,\"tool_choice\":\"auto\",\"temperature\":1,\"top_p\":0.95,\"text\":{\"format\":{\"type\":\"text\"}},\"reasoning\":{\"effort\":\"none\",\"summary\":null},\"max_output_tokens\":1536,\"parallel_tool_calls\":true,\"previous_response_id\":null,\"conversation\":null,\"store\":false,\"service_tier\":\"standard\",\"safety_identifier\":null,\"truncation\":\"disabled\"}}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"sequence_number\":2,\"item\":{\"id\":\"40d878e9c7ecc67905d069cdf9bec7c0_msg\",\"type\":\"message\",\"status\":\"in_progress\",\"role\":\"assistant\",\"content\":[]},\"output_index\":0}\n\nevent: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"sequence_number\":3,\"part\":{\"type\":\"output_text\",\"text\":\"\",\"annotations\":[]},\"output_index\":0,\"content_index\":0,\"item_id\":\"40d878e9c7ecc67905d069cdf9bec7c0_msg\"}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"sequence_number\":4,\"output_index\":0,\"content_index\":0,\"item_id\":\"40d878e9c7ecc67905d069cdf9bec7c0_msg\",\"delta\":\"378\"}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"sequence_number\":5,\"output_index\":0,\"content_index\":0,\"item_id\":\"40d878e9c7ecc67905d069cdf9bec7c0_msg\",\"delta\":\"87\"}\n\nevent: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"sequence_number\":6,\"output_index\":0,\"content_index\":0,\"item_id\":\"40d878e9c7ecc67905d069cdf9bec7c0_msg\",\"text\":\"37887\"}\n\nevent: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"sequence_number\":7,\"part\":{\"type\":\"output_text\",\"text\":\"37887\",\"annotations\":[]},\"output_index\":0,\"content_index\":0,\"item_id\":\"40d878e9c7ecc67905d069cdf9bec7c0_msg\"}\n\nevent: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"sequence_number\":8,\"item\":{\"status\":\"completed\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"37887\",\"annotations\":[]}],\"id\":\"40d878e9c7ecc67905d069cdf9bec7c0_msg\",\"type\":\"message\"},\"output_index\":0}\n\nevent: response.completed\ndata: {\"type\":\"response.completed\",\"sequence_number\":9,\"response\":{\"id\":\"40d878e9c7ecc67905d069cdf9bec7c0\",\"object\":\"response\",\"created_at\":1788800001,\"model\":\"MiniMax-M3\",\"status\":\"completed\",\"output\":[{\"id\":\"40d878e9c7ecc67905d069cdf9bec7c0_msg\",\"type\":\"message\",\"status\":\"completed\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"37887\",\"annotations\":null}]}],\"output_text\":\"37887\",\"usage\":{\"input_tokens\":179,\"output_tokens\":3,\"total_tokens\":182,\"input_tokens_details\":{\"cached_tokens\":128}},\"error\":null,\"incomplete_details\":null,\"instructions\":null,\"metadata\":{},\"tools\":null,\"tool_choice\":\"auto\",\"temperature\":1,\"top_p\":0.95,\"text\":{\"format\":{\"type\":\"text\"}},\"reasoning\":{\"effort\":\"none\",\"summary\":null},\"max_output_tokens\":1536,\"parallel_tool_calls\":true,\"previous_response_id\":null,\"conversation\":null,\"store\":false,\"service_tier\":\"standard\",\"safety_identifier\":null,\"truncation\":\"disabled\"}}\n\n"
"body": "event: response.created\ndata: {\"type\":\"response.created\",\"sequence_number\":0,\"response\":{\"id\":\"dd8176b3bdefd70688853863475c9cd3\",\"object\":\"response\",\"created_at\":1789446323,\"model\":\"MiniMax-M3\",\"status\":\"in_progress\",\"output\":[],\"output_text\":null,\"usage\":null,\"error\":null,\"incomplete_details\":null,\"instructions\":null,\"metadata\":{},\"tools\":null,\"tool_choice\":\"auto\",\"temperature\":1,\"top_p\":0.95,\"text\":{\"format\":{\"type\":\"text\"}},\"reasoning\":{\"effort\":\"none\",\"summary\":null},\"max_output_tokens\":1536,\"parallel_tool_calls\":true,\"previous_response_id\":null,\"conversation\":null,\"store\":false,\"service_tier\":\"standard\",\"safety_identifier\":null,\"truncation\":\"disabled\"}}\n\nevent: response.in_progress\ndata: {\"type\":\"response.in_progress\",\"sequence_number\":1,\"response\":{\"id\":\"dd8176b3bdefd70688853863475c9cd3\",\"object\":\"response\",\"created_at\":1789446323,\"model\":\"MiniMax-M3\",\"status\":\"in_progress\",\"output\":[],\"output_text\":null,\"usage\":null,\"error\":null,\"incomplete_details\":null,\"instructions\":null,\"metadata\":{},\"tools\":null,\"tool_choice\":\"auto\",\"temperature\":1,\"top_p\":0.95,\"text\":{\"format\":{\"type\":\"text\"}},\"reasoning\":{\"effort\":\"none\",\"summary\":null},\"max_output_tokens\":1536,\"parallel_tool_calls\":true,\"previous_response_id\":null,\"conversation\":null,\"store\":false,\"service_tier\":\"standard\",\"safety_identifier\":null,\"truncation\":\"disabled\"}}\n\nevent: response.output_item.added\ndata: {\"type\":\"response.output_item.added\",\"sequence_number\":2,\"item\":{\"id\":\"dd8176b3bdefd70688853863475c9cd3_msg\",\"type\":\"message\",\"status\":\"in_progress\",\"role\":\"assistant\",\"content\":[]},\"output_index\":0}\n\nevent: response.content_part.added\ndata: {\"type\":\"response.content_part.added\",\"sequence_number\":3,\"part\":{\"annotations\":[],\"type\":\"output_text\",\"text\":\"\"},\"output_index\":0,\"content_index\":0,\"item_id\":\"dd8176b3bdefd70688853863475c9cd3_msg\"}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"sequence_number\":4,\"output_index\":0,\"content_index\":0,\"item_id\":\"dd8176b3bdefd70688853863475c9cd3_msg\",\"delta\":\"378\"}\n\nevent: response.output_text.delta\ndata: {\"type\":\"response.output_text.delta\",\"sequence_number\":5,\"output_index\":0,\"content_index\":0,\"item_id\":\"dd8176b3bdefd70688853863475c9cd3_msg\",\"delta\":\"87\"}\n\nevent: response.output_text.done\ndata: {\"type\":\"response.output_text.done\",\"sequence_number\":6,\"output_index\":0,\"content_index\":0,\"item_id\":\"dd8176b3bdefd70688853863475c9cd3_msg\",\"text\":\"37887\"}\n\nevent: response.content_part.done\ndata: {\"type\":\"response.content_part.done\",\"sequence_number\":7,\"part\":{\"text\":\"37887\",\"annotations\":[],\"type\":\"output_text\"},\"output_index\":0,\"content_index\":0,\"item_id\":\"dd8176b3bdefd70688853863475c9cd3_msg\"}\n\nevent: response.output_item.done\ndata: {\"type\":\"response.output_item.done\",\"sequence_number\":8,\"item\":{\"id\":\"dd8176b3bdefd70688853863475c9cd3_msg\",\"type\":\"message\",\"status\":\"completed\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"37887\",\"annotations\":[]}]},\"output_index\":0}\n\nevent: response.completed\ndata: {\"type\":\"response.completed\",\"sequence_number\":9,\"response\":{\"id\":\"dd8176b3bdefd70688853863475c9cd3\",\"object\":\"response\",\"created_at\":1789446323,\"model\":\"MiniMax-M3\",\"status\":\"completed\",\"output\":[{\"id\":\"dd8176b3bdefd70688853863475c9cd3_msg\",\"type\":\"message\",\"status\":\"completed\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"37887\",\"annotations\":null}]}],\"output_text\":\"37887\",\"usage\":{\"input_tokens\":179,\"output_tokens\":3,\"total_tokens\":182,\"input_tokens_details\":{\"cached_tokens\":128}},\"error\":null,\"incomplete_details\":null,\"instructions\":null,\"metadata\":{},\"tools\":null,\"tool_choice\":\"auto\",\"temperature\":1,\"top_p\":0.95,\"text\":{\"format\":{\"type\":\"text\"}},\"reasoning\":{\"effort\":\"none\",\"summary\":null},\"max_output_tokens\":1536,\"parallel_tool_calls\":true,\"previous_response_id\":null,\"conversation\":null,\"store\":false,\"service_tier\":\"standard\",\"safety_identifier\":null,\"truncation\":\"disabled\"}}\n\n"
}
}
]
File diff suppressed because one or more lines are too long
@@ -2,9 +2,16 @@
"version": 1,
"metadata": {
"model": "kimi-k3",
"tags": ["prefix:moonshot-responses", "provider:moonshot", "protocol:responses", "tool", "tool-choice", "error"],
"tags": [
"prefix:moonshot-responses",
"provider:moonshot",
"protocol:responses",
"tool",
"tool-choice",
"error"
],
"name": "moonshot-responses/k3-rejects-unsupported-tool-choice-none",
"recordedAt": "2026-09-07T20:38:12.966Z"
"recordedAt": "2026-09-15T04:26:05.011Z"
},
"interactions": [
{
@@ -15,7 +22,7 @@
"headers": {
"content-type": "application/json"
},
"body": "{\"model\":\"kimi-k3\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Use get_weather to look up the current weather in Paris.\"}]}],\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get the current weather in a city\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\",\"enum\":[\"Paris\"]}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"tool_choice\":\"none\",\"reasoning\":{\"effort\":\"low\"},\"max_output_tokens\":4096,\"stream\":true}"
"body": "{\"model\":\"kimi-k3\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Use get_weather to look up the current weather in Paris.\"}]}],\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get the current weather in a city\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\",\"enum\":[\"Paris\"]}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"tool_choice\":\"none\",\"reasoning\":{\"effort\":\"low\"},\"max_output_tokens\":4096,\"stream\":true}"
},
"response": {
"status": 400,
@@ -2,9 +2,16 @@
"version": 1,
"metadata": {
"model": "kimi-k3",
"tags": ["prefix:moonshot-responses", "provider:moonshot", "protocol:responses", "tool", "tool-choice", "error"],
"tags": [
"prefix:moonshot-responses",
"provider:moonshot",
"protocol:responses",
"tool",
"tool-choice",
"error"
],
"name": "moonshot-responses/k3-rejects-unsupported-tool-choice-required",
"recordedAt": "2026-09-07T20:38:12.717Z"
"recordedAt": "2026-09-15T04:26:04.715Z"
},
"interactions": [
{
@@ -15,7 +22,7 @@
"headers": {
"content-type": "application/json"
},
"body": "{\"model\":\"kimi-k3\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Use get_weather to look up the current weather in Paris.\"}]}],\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get the current weather in a city\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\",\"enum\":[\"Paris\"]}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"tool_choice\":\"required\",\"reasoning\":{\"effort\":\"low\"},\"max_output_tokens\":4096,\"stream\":true}"
"body": "{\"model\":\"kimi-k3\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Use get_weather to look up the current weather in Paris.\"}]}],\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get the current weather in a city\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\",\"enum\":[\"Paris\"]}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"tool_choice\":\"required\",\"reasoning\":{\"effort\":\"low\"},\"max_output_tokens\":4096,\"stream\":true}"
},
"response": {
"status": 400,
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,8 +1,6 @@
{
"version": 1,
"metadata": {
"provider": "openai",
"protocol": "openai-responses",
"transport": "websocket",
"model": "gpt-5.5",
"tags": [
@@ -14,7 +12,7 @@
"continuation"
],
"name": "openai-responses-websocket/continues-a-tool-call-over-one-socket",
"recordedAt": "2026-08-20T00:00:00.000Z"
"recordedAt": "2026-09-15T04:26:14.118Z"
},
"interactions": [
{
@@ -32,67 +30,562 @@
{
"direction": "client",
"kind": "text",
"body": "{\"type\":\"response.create\",\"model\":\"gpt-5.5\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]}],\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"store\":false,\"max_output_tokens\":50,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"instructions\":\"Call get_weather once, then reply exactly: Paris is sunny.\"}"
"body": "{\"type\":\"response.create\",\"model\":\"gpt-5.5\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]}],\"instructions\":\"Call get_weather once, then reply exactly: Paris is sunny.\",\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"max_output_tokens\":50}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.created\",\"response\":{\"id\":\"resp_ws_tool_1\"}}"
"body": "{\"type\":\"response.created\",\"response\":{\"id\":\"resp_0d9a44b6df400533016aa8c8e36de887d1be260913d131b2ca\",\"object\":\"response\",\"created_at\":1789446371,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Call get_weather once, then reply exactly: Paris is sunny.\",\"max_output_tokens\":50,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Get current weather for a city.\",\"name\":\"get_weather\",\"output_schema\":null,\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"type\":\"function_call\",\"id\":\"fc_ws_weather\",\"call_id\":\"call_ws_weather\",\"name\":\"get_weather\",\"arguments\":\"\"}}"
"body": "{\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0d9a44b6df400533016aa8c8e36de887d1be260913d131b2ca\",\"object\":\"response\",\"created_at\":1789446371,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Call get_weather once, then reply exactly: Paris is sunny.\",\"max_output_tokens\":50,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Get current weather for a city.\",\"name\":\"get_weather\",\"output_schema\":null,\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.function_call_arguments.delta\",\"item_id\":\"fc_ws_weather\",\"delta\":\"{\\\"city\\\":\\\"Paris\\\"}\"}"
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqqMjkTDRDSH8NF87_7rVqaYHPh3s50_dKjFuErKXNrbllU6-WO1sUja3OI9tItISMP9vJRuGv8TUe66uEuA7qdzaqiRMMG56QOmtV8_4ZJahMhjV52nZM1ESM88nTdzb3vgzdYQoWJrOObr4jfCdc_UAiz9eDLjXJU822aIZQQ5lK4Awc0c9b8jCar9Sk9H9PizctBYHemxHYFYQl2bsNV6vaAnVn_E2jhRRcMSy5HVbpqsN2177lCJl_A21cXqMQchf8yrnXMB2MrZg8jbmEWWzwsdmaAq40D_JT7w_tbMGvUqY_rVxVFFeZrD_mXJrtVgG9oqNUdNs6EvkTfwLsnw-FzXiXlCcRTZOIc1WH7vimC-PFNO8cO8Fh7kedeDILI41LG1zjDo8DMoNsuDzd1fYchC-o6BSPSi2yFFMIPgUWCOM-76vO-j5l44eWOPUETj850_1a8tbpA-GLeXVIwm8BBvbP9wz74I0zXSDCjPMdfueH5FFriArRVB1n2VAKdwmN5uQuRjjIdqFXTQrHCQVZSVgFO4o8MluptmbPnvkvExnXaNqwT-W2znRGr3R7Tx4kgylIKgrgfVbZAiKU4NMyx1nV1EXjVpMbW_08wwVV7ZdziDKVsscbCNorWofFw-6ha7MQpX16eP6Hreb88vnE8LRhLB6XetNe12kHjjqmchR9gQk4M4JdF9n682XTUm0EREfU7zmzTDwROOoJJPurasF7x8dtTs8-S5hRBZxY3m2yZlNoO6xHdvc_ysJ1WaAtBcRDJ_-MaetWwIKWHta6sw5ra93W5DplJdK7WJAyAknqXUt42kNKYJxQ5anuesm1i9cgN854tc_j0-cb6wVKXrNto5CVV4xYrGtrbb0xQKHx5bcjeRVRtuJZH_KyQMe1Ct_7U-VCiVKBlf8T0vBCB2O3MQZ3xkLRE8JwV7Z-4PuRG39-qe3Hwpikntq6JAdQSy9qDZ4_mtJ_NPThI_livwLBd1_oAG9_iizrO3hccKDxhsFXtDa8Nlk-X4SnvR7HMFo5rrSXyan5f1VWq02rrUF4lp19cEuGvgpJX2J94ytbwcs1l0w2PXE--AAjFuB1UtM2rCaPI8KQ6Sa3LFG399HlyqnuIZIbr4SDJvshCpy4uEO_avpF8_kusO-Mnavk9tEGoTOE0pKWKn3U-rQKKA==\",\"summary\":[]},\"output_index\":0,\"sequence_number\":2}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"type\":\"function_call\",\"id\":\"fc_ws_weather\",\"call_id\":\"call_ws_weather\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\"}}"
"body": "{\"type\":\"response.reasoning_summary_part.added\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"output_index\":0,\"part\":{\"type\":\"summary_text\",\"text\":\"\"},\"sequence_number\":3,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.completed\",\"response\":{\"id\":\"resp_ws_tool_1\"}}"
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"**Implementing weather response**\\n\\nI\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"TbCJpsdDvQ3o\",\"output_index\":0,\"sequence_number\":4,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" need\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"3NIheDr1kOF\",\"output_index\":0,\"sequence_number\":5,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" to\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"MbC15foL7SIw7\",\"output_index\":0,\"sequence_number\":6,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" follow\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"We9s1kCww\",\"output_index\":0,\"sequence_number\":7,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"Kgi3ip1iR2ZT\",\"output_index\":0,\"sequence_number\":8,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" developer\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"KFxLc4\",\"output_index\":0,\"sequence_number\":9,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"'s\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"ERgA3Gaiks0yYV\",\"output_index\":0,\"sequence_number\":10,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" instructions\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"Iw8\",\"output_index\":0,\"sequence_number\":11,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" and\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"CbL5c4Exf10E\",\"output_index\":0,\"sequence_number\":12,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" call\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"panR6nMaWqv\",\"output_index\":0,\"sequence_number\":13,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"sxlbh7P7akjF\",\"output_index\":0,\"sequence_number\":14,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" weather\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"ku1RV4qB\",\"output_index\":0,\"sequence_number\":15,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" tool\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"Vlu0g7WGuY3\",\"output_index\":0,\"sequence_number\":16,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" just\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"gh23NDqizMl\",\"output_index\":0,\"sequence_number\":17,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" once\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"TQdqoWkbZrk\",\"output_index\":0,\"sequence_number\":18,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" for\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"xyhpMfVjyqq5\",\"output_index\":0,\"sequence_number\":19,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"vqbXSgY6GLKp\",\"output_index\":0,\"sequence_number\":20,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" data\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"k8i4o9tjubR\",\"output_index\":0,\"sequence_number\":21,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\".\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"Cp81EzNFyVcTaDG\",\"output_index\":0,\"sequence_number\":22,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" After\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"gZvjSHmOcX\",\"output_index\":0,\"sequence_number\":23,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" that\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"WAdp4PoJOHO\",\"output_index\":0,\"sequence_number\":24,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\",\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"RixHPsXTUsaOO70\",\"output_index\":0,\"sequence_number\":25,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" I\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"C22hnkcv3owo0d\",\"output_index\":0,\"sequence_number\":26,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"’ll\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"PlVRoVD38Fzc1\",\"output_index\":0,\"sequence_number\":27,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" be\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"hPrJxA6NlRIGZ\",\"output_index\":0,\"sequence_number\":28,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" ready\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"B19EzScDoZ\",\"output_index\":0,\"sequence_number\":29,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" to\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"9UPFCS4mptILJ\",\"output_index\":0,\"sequence_number\":30,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" respond\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"wM6fcu6j\",\"output_index\":0,\"sequence_number\":31,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" in\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"Q6aijntRNsTdM\",\"output_index\":0,\"sequence_number\":32,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" a\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"j3odkTfYv8dDPp\",\"output_index\":0,\"sequence_number\":33,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" straightforward\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"\",\"output_index\":0,\"sequence_number\":34,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" manner\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"BGJ8SDV9I\",\"output_index\":0,\"sequence_number\":35,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" by\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"lM69n9Hu3r1YG\",\"output_index\":0,\"sequence_number\":36,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" saying\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"zYkNnOIOm\",\"output_index\":0,\"sequence_number\":37,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\",\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"z6nCAycYSFfJFZA\",\"output_index\":0,\"sequence_number\":38,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" “\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"yYE1PTF5KvQRn5\",\"output_index\":0,\"sequence_number\":39,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"Paris\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"zlWVqe3y8Q6\",\"output_index\":0,\"sequence_number\":40,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" is\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"4DAQpZBUFnFZO\",\"output_index\":0,\"sequence_number\":41,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" sunny\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"urn1MrxoKY\",\"output_index\":0,\"sequence_number\":42,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\".”\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"LhcYABogHBOAwH\",\"output_index\":0,\"sequence_number\":43,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" It\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"35qoo13EcH3X7\",\"output_index\":0,\"sequence_number\":44,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"’s\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"8hjeaSFDVMzBvP\",\"output_index\":0,\"sequence_number\":45,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" essential\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"Rm9Bwp\",\"output_index\":0,\"sequence_number\":46,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" to\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"7HFMBnHcZbacD\",\"output_index\":0,\"sequence_number\":47,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" make\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"nI7WmQUCakM\",\"output_index\":0,\"sequence_number\":48,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"6mbcq3VEkBwZ\",\"output_index\":0,\"sequence_number\":49,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" call\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"KPrqt6D9EsE\",\"output_index\":0,\"sequence_number\":50,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\",\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"GRYEOxIsz2HS8w0\",\"output_index\":0,\"sequence_number\":51,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" then\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"Xkl9XYMfWN7\",\"output_index\":0,\"sequence_number\":52,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" provide\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"KLDzW6ZT\",\"output_index\":0,\"sequence_number\":53,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" that\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"x6PcDZl3shp\",\"output_index\":0,\"sequence_number\":54,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" exact\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"YHWDXsg0BV\",\"output_index\":0,\"sequence_number\":55,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" information\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"RAzc\",\"output_index\":0,\"sequence_number\":56,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" without\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"FLlSX8hW\",\"output_index\":0,\"sequence_number\":57,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" any\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"vCUqWUWWDTW6\",\"output_index\":0,\"sequence_number\":58,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" additional\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"QOcjY\",\"output_index\":0,\"sequence_number\":59,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" fluff\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"4N5N79MGrj\",\"output_index\":0,\"sequence_number\":60,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"—\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"RC7OofXAZPgzwg4\",\"output_index\":0,\"sequence_number\":61,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"keeping\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"r3W2LbFFc\",\"output_index\":0,\"sequence_number\":62,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" it\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"FfX7i9BVTzP0c\",\"output_index\":0,\"sequence_number\":63,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" clear\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"CrbdwCSLpZ\",\"output_index\":0,\"sequence_number\":64,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" and\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"owo3yXXSQ6jZ\",\"output_index\":0,\"sequence_number\":65,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" direct\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"gt5hFKptL\",\"output_index\":0,\"sequence_number\":66,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"!\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"7Yo2k8CiX6rZK3k\",\"output_index\":0,\"sequence_number\":67,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" I\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"RAcZYrckPw8dh3\",\"output_index\":0,\"sequence_number\":68,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\"’ll\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"CR9GWOnHlEkV9\",\"output_index\":0,\"sequence_number\":69,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" make\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"GufKyMsROBB\",\"output_index\":0,\"sequence_number\":70,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" sure\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"UhvVD6VIeLd\",\"output_index\":0,\"sequence_number\":71,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"ziUpHNR0ZHr4\",\"output_index\":0,\"sequence_number\":72,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" tool\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"ZjPg0mkR8Nq\",\"output_index\":0,\"sequence_number\":73,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" call\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"x6DFe0sU1Mv\",\"output_index\":0,\"sequence_number\":74,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" is\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"R6f2YO4oPLB81\",\"output_index\":0,\"sequence_number\":75,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" done\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"cf2CMjdUYdG\",\"output_index\":0,\"sequence_number\":76,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" properly\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"cnYHDGP\",\"output_index\":0,\"sequence_number\":77,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" and\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"W6cdOAi9VvWn\",\"output_index\":0,\"sequence_number\":78,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" use\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"PGTdQ0QwpnyX\",\"output_index\":0,\"sequence_number\":79,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"2QX1s43jqUIk\",\"output_index\":0,\"sequence_number\":80,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" commentary\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"qKkgA\",\"output_index\":0,\"sequence_number\":81,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" for\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"pFbEEeKNZj2h\",\"output_index\":0,\"sequence_number\":82,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" the\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"OIt5IE0Qo6aX\",\"output_index\":0,\"sequence_number\":83,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\" call\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"fEv5R2QxzLt\",\"output_index\":0,\"sequence_number\":84,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.delta\",\"delta\":\".\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"obfuscation\":\"uCr3VWty9DouhDW\",\"output_index\":0,\"sequence_number\":85,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_text.done\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"output_index\":0,\"sequence_number\":86,\"summary_index\":0,\"text\":\"**Implementing weather response**\\n\\nI need to follow the developer's instructions and call the weather tool just once for the data. After that, I’ll be ready to respond in a straightforward manner by saying, “Paris is sunny.” It’s essential to make the call, then provide that exact information without any additional fluff—keeping it clear and direct! I’ll make sure the tool call is done properly and use the commentary for the call.\"}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.reasoning_summary_part.done\",\"item_id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"output_index\":0,\"part\":{\"type\":\"summary_text\",\"text\":\"**Implementing weather response**\\n\\nI need to follow the developer's instructions and call the weather tool just once for the data. After that, I’ll be ready to respond in a straightforward manner by saying, “Paris is sunny.” It’s essential to make the call, then provide that exact information without any additional fluff—keeping it clear and direct! I’ll make sure the tool call is done properly and use the commentary for the call.\"},\"sequence_number\":87,\"summary_index\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqqMjljcIP44L8J5Ibxsh3psjGXYUB6G62Ys0AaSsU4PNZyxoyxQ9beUuZRE5krvpE7xSrWerozO09S8clznM8FXYV_M26d4BoKvr9DGT7CbLYg2iRAZj3_7H8G45SIJVvM-QAnM8Zrrz_QC7UIjczvX8--oQZXxuaNGuELH8Og4OpmD3mJ6GrxZxwRa3zoc-z1NFFQkDQfL30xYFusftkVIwvGEjrtiG5LGR_HhfkcbVdzAuQy7dmXqLhjX_y-0i57KFAIG18p7Q-Ddjw53q1VDwdxcu04LnXUHL8mUArkq967E1efCKYJJ1MgqyCEquTsz1Zsorg2fwaYOALL965_-HjfNdhAluZqK84ihrnATGIfGEX7qRlZNmcraZ_Y7JgVdyfoQelXREGzT0QKjf4pM3Juvi9RLHfve5FdtigKXTChigqdQsynCSxVzUlbizbsbOA86_W8ZnUQEiLR0aywKgGbpKu4riFxFD2fzkO5Mf9QNDf1p90b_ql9WMv3jwo7-EyToDEV3GgEzjqDQj2VZmATheTpHWc0uwGZxRlJz-GB-MYr_NJZJ-D1SekyFM8oUUyv3tEnKk7dG0ij5nffAMH0ej-h5TLyWQ7PLxmEFHdWyEOt86Qf2Km-u0jUxvQhuZ7Fqgt1LP4o5sIhz8a-KfgCUlOyIMYdim12ndglyV8F-QsCbrJCOIs-UMTJgLwQ1_LElk9WmJpfizk0W6dTEAHHNPFxdKkTCYOb-uEPnVL5nfntcg1a9kSmXoi3PUN4rz-JxTLfXK-eBrg5CimvvyMoNlDYsgqrSpG7WotH3xU0ycQwVf6QtpzTNai2tcrGZlrdiFObEwNMu26DLH_845AJ68D_-iRNeIGbVbHJyR65LKR7Q8AXuwQrN48yrC-TlbSKqR6v-ztycyTUuDcr6PpawIpIDCKahNfyELC-iC6RA_-Vsa04y1NBOXOaEK47PFpGNP3IovSLAsI8mEynOGWooqbuiusIBMHeWIZRdniiqdBTbPlXLj7gLxpl6rinOgnEAKGPUUr52KYqA7tVuXZUk2JQOOZqV7vAKzpVs5mDfj6s14s1sHF4zJE5AnBqlppcDA46MOkzBvDdfdaczWY8GA-DCBRAjfFfIBP5l9z3iwbPt7R5s3vqIpxJ2P7FoemX-0PjAcCDxGaCWRS5M_D6ONGrJ-mbmk7lp9rmGTvMleTf1RoK7b87Rh1TLRmudLMnmDmxDapNN8g-c7YtmFgl7LL9D2yPpqUcrZpTQAkwbX9i4mIUIIkLgDCtjoVvs2LNSxVutJ9yHxgel9g7mSZdpu03muIGbRj-K44IuEIq4gSM_H8rz2OR9YizsZG_19b\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Implementing weather response**\\n\\nI need to follow the developer's instructions and call the weather tool just once for the data. After that, I’ll be ready to respond in a straightforward manner by saying, “Paris is sunny.” It’s essential to make the call, then provide that exact information without any additional fluff—keeping it clear and direct! I’ll make sure the tool call is done properly and use the commentary for the call.\"}]},\"output_index\":0,\"sequence_number\":88}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"id\":\"fc_0d9a44b6df400533016aa8c8e5585c87d189ec09ea7ce4c445\",\"type\":\"function_call\",\"status\":\"in_progress\",\"arguments\":\"\",\"call_id\":\"call_hejtTYDa1IfLyNIzb3fq9gJs\",\"name\":\"get_weather\"},\"output_index\":1,\"sequence_number\":89}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.function_call_arguments.delta\",\"delta\":\"{\\\"\",\"item_id\":\"fc_0d9a44b6df400533016aa8c8e5585c87d189ec09ea7ce4c445\",\"obfuscation\":\"eKg7OGvtGwDIwu\",\"output_index\":1,\"sequence_number\":90}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.function_call_arguments.delta\",\"delta\":\"city\",\"item_id\":\"fc_0d9a44b6df400533016aa8c8e5585c87d189ec09ea7ce4c445\",\"obfuscation\":\"DJy8TaXOhtLi\",\"output_index\":1,\"sequence_number\":91}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\":\\\"\",\"item_id\":\"fc_0d9a44b6df400533016aa8c8e5585c87d189ec09ea7ce4c445\",\"obfuscation\":\"lq8D2NWvjwgHm\",\"output_index\":1,\"sequence_number\":92}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.function_call_arguments.delta\",\"delta\":\"Paris\",\"item_id\":\"fc_0d9a44b6df400533016aa8c8e5585c87d189ec09ea7ce4c445\",\"obfuscation\":\"4fgqE1jlq41\",\"output_index\":1,\"sequence_number\":93}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.function_call_arguments.delta\",\"delta\":\"\\\"}\",\"item_id\":\"fc_0d9a44b6df400533016aa8c8e5585c87d189ec09ea7ce4c445\",\"obfuscation\":\"bass2ZeGpJKxwf\",\"output_index\":1,\"sequence_number\":94}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.function_call_arguments.done\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\",\"item_id\":\"fc_0d9a44b6df400533016aa8c8e5585c87d189ec09ea7ce4c445\",\"output_index\":1,\"sequence_number\":95}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"id\":\"fc_0d9a44b6df400533016aa8c8e5585c87d189ec09ea7ce4c445\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\",\"call_id\":\"call_hejtTYDa1IfLyNIzb3fq9gJs\",\"name\":\"get_weather\"},\"output_index\":1,\"sequence_number\":96}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0d9a44b6df400533016aa8c8e36de887d1be260913d131b2ca\",\"object\":\"response\",\"created_at\":1789446371,\"status\":\"completed\",\"background\":false,\"completed_at\":1789446373,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Call get_weather once, then reply exactly: Paris is sunny.\",\"max_output_tokens\":50,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[{\"id\":\"rs_0d9a44b6df400533016aa8c8e446bc87d1964263dc3d1fff1e\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqqMjl2oviigMd2ielExmi3PWYcBUFDcQ53qvDsLPaN2vKin-hLCGpa1fMvdgOrtutnq5n4qp3vuN3PdndXOjozyqJMkyftlgaziR6v7DsgV_0Mlu5zQAPANgTC7Z4TUyf7BOoQDbac0W4TOeFMFv_5n0l50r4xNufMY9aBMwqCikz0NUdicv5YOCjoZ-IqP8F0vGlLD7cTg8wtvlLfwoTBA5zC8PntTaAcDLv3_jPvaQ2zwZC0nXg6J-ZfaaFGAJ2vcCLTau3ekfrEWItpKl8iv1AULUUKMbsKjWOvGa19--y5Tqym36-K_isbGYVDPe8mXdF4iFV-zhOF4FclILAic28QQHIygG-9stVy2XBSF7UEa-svIrckG3xS3Gq8XA3mKA5Y5GLLKJiviZviMVCQAoatDHLO5f8RmkipKnJSPgL8c5DZp2tNBadst2ndClzvxaZGjZD6EN98ZF8Ha0HMW-6oKhxpLOdqVEOApquMBZHsRy9_ubPP-ar7tzYm9RVDmDcc1exNg1i8sFbSLUoxF8xw-OHY6fuvqq0XqZR_FvysYp1qdIAwFRgbHAgLP8KK3j15eyx8aRXP6sW98p6z7kw1SC-osDQQpz6yp-YYgtReKKAgYHFHCLax8ryCZCd3nMZi0rzG4dZhiQ3-2ousEb1ELB3BWG43ejzKyp5iQjIbOCbPbOB208ryQAvkV7mn7KaDvBZ_LRCXnhn8hZ13pAq4k78aCWIBs3R2G4i56QMD7CDeUs9XWTMMJltsPz1gl-Tv0gaO8r5HNVXaJlXob4NgdU7fiOtzIVR0kdSknBPJPKbcQesBRJJkcJ62qTWiycXWLGlg5bIaCPbFCePe-eYuIzg5KUQUbp8o09MxF-g6SXe_Z0BPbA3vfldKkcPQfJeH4EVw8HbhsCoPFTwwMTnVuUnwItBUEvIU-ALNVP1ZamN7htnjDLX3CtzEXC0VteGFoCXZukH_F44EC-p8JFs2RmzzYveTHfEohBBQZwM-w7Uoj4VDNuI7dQ8VztxD20xfkZ_sLbA7WhZEwer_XcgaHPmLDCp8NgtZqQ2LaMcAUs3Avx65ew_dcMufgeSiUTMOWF3oABuoLQaOVBLdlW8e1bIAUqgUuNVjqvxS50LYksBAodl0QgU0PjmnAELP73IU2lekCG6nazXwdNzBLLLvRfcjSxgDpqR4KHAsyKleazTFh9OsFBms1ixz3EH1bzT0s4g3E3ZuGsovHAyNvNUCcufgjX_gLbpT1HRzDAc-YjRtwoKR5xNIJkVqIXW7Oluhj-2wcH7s5c2noQVIx_h83RQex6yXAfTdmQKjbuEhKHI841kQA9jpzPwxbMenP2c\",\"summary\":[{\"type\":\"summary_text\",\"text\":\"**Implementing weather response**\\n\\nI need to follow the developer's instructions and call the weather tool just once for the data. After that, I’ll be ready to respond in a straightforward manner by saying, “Paris is sunny.” It’s essential to make the call, then provide that exact information without any additional fluff—keeping it clear and direct! I’ll make sure the tool call is done properly and use the commentary for the call.\"}]},{\"id\":\"fc_0d9a44b6df400533016aa8c8e5585c87d189ec09ea7ce4c445\",\"type\":\"function_call\",\"status\":\"completed\",\"arguments\":\"{\\\"city\\\":\\\"Paris\\\"}\",\"call_id\":\"call_hejtTYDa1IfLyNIzb3fq9gJs\",\"name\":\"get_weather\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Get current weather for a city.\",\"name\":\"get_weather\",\"output_schema\":null,\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":67,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":47,\"output_tokens_details\":{\"reasoning_tokens\":27},\"total_tokens\":114},\"user\":null,\"metadata\":{}},\"sequence_number\":97}"
},
{
"direction": "client",
"kind": "text",
"body": "{\"type\":\"response.create\",\"model\":\"gpt-5.5\",\"input\":[{\"type\":\"function_call_output\",\"call_id\":\"call_ws_weather\",\"output\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}],\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"store\":false,\"max_output_tokens\":50,\"previous_response_id\":\"resp_ws_tool_1\",\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"instructions\":\"Call get_weather once, then reply exactly: Paris is sunny.\"}"
"body": "{\"type\":\"response.create\",\"model\":\"gpt-5.5\",\"input\":[{\"type\":\"function_call_output\",\"call_id\":\"call_hejtTYDa1IfLyNIzb3fq9gJs\",\"output\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}],\"instructions\":\"Call get_weather once, then reply exactly: Paris is sunny.\",\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get current weather for a city.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"max_output_tokens\":50,\"previous_response_id\":\"resp_0d9a44b6df400533016aa8c8e36de887d1be260913d131b2ca\"}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.created\",\"response\":{\"id\":\"resp_ws_tool_2\"}}"
"body": "{\"type\":\"response.created\",\"response\":{\"id\":\"resp_0d9a44b6df400533016aa8c8e57f0c87d1be4c46b4c1d01c71\",\"object\":\"response\",\"created_at\":1789446373,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Call get_weather once, then reply exactly: Paris is sunny.\",\"max_output_tokens\":50,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":\"resp_0d9a44b6df400533016aa8c8e36de887d1be260913d131b2ca\",\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Get current weather for a city.\",\"name\":\"get_weather\",\"output_schema\":null,\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"type\":\"message\",\"id\":\"msg_ws_tool_2\",\"role\":\"assistant\",\"content\":[]}}"
"body": "{\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0d9a44b6df400533016aa8c8e57f0c87d1be4c46b4c1d01c71\",\"object\":\"response\",\"created_at\":1789446373,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Call get_weather once, then reply exactly: Paris is sunny.\",\"max_output_tokens\":50,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":\"resp_0d9a44b6df400533016aa8c8e36de887d1be260913d131b2ca\",\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Get current weather for a city.\",\"name\":\"get_weather\",\"output_schema\":null,\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.delta\",\"item_id\":\"msg_ws_tool_2\",\"delta\":\"Paris is sunny.\"}"
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_0d9a44b6df400533016aa8c8e61a4487d1b1f1c08b9d79b591\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":2}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.done\",\"item_id\":\"msg_ws_tool_2\",\"text\":\"Paris is sunny.\"}"
"body": "{\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_0d9a44b6df400533016aa8c8e61a4487d1b1f1c08b9d79b591\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":3}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"type\":\"message\",\"id\":\"msg_ws_tool_2\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"Paris is sunny.\"}]}}"
"body": "{\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Paris\",\"item_id\":\"msg_0d9a44b6df400533016aa8c8e61a4487d1b1f1c08b9d79b591\",\"logprobs\":[],\"obfuscation\":\"Dyae6fOTpWJ\",\"output_index\":0,\"sequence_number\":4}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.completed\",\"response\":{\"id\":\"resp_ws_tool_2\"}}"
"body": "{\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" is\",\"item_id\":\"msg_0d9a44b6df400533016aa8c8e61a4487d1b1f1c08b9d79b591\",\"logprobs\":[],\"obfuscation\":\"jQkWZ9QLds7oL\",\"output_index\":0,\"sequence_number\":5}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\" sunny\",\"item_id\":\"msg_0d9a44b6df400533016aa8c8e61a4487d1b1f1c08b9d79b591\",\"logprobs\":[],\"obfuscation\":\"qnUKuAqamJ\",\"output_index\":0,\"sequence_number\":6}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_0d9a44b6df400533016aa8c8e61a4487d1b1f1c08b9d79b591\",\"logprobs\":[],\"obfuscation\":\"75jYC4tq0LAyGLi\",\"output_index\":0,\"sequence_number\":7}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_0d9a44b6df400533016aa8c8e61a4487d1b1f1c08b9d79b591\",\"logprobs\":[],\"output_index\":0,\"sequence_number\":8,\"text\":\"Paris is sunny.\"}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_0d9a44b6df400533016aa8c8e61a4487d1b1f1c08b9d79b591\",\"output_index\":0,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Paris is sunny.\"},\"sequence_number\":9}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_0d9a44b6df400533016aa8c8e61a4487d1b1f1c08b9d79b591\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Paris is sunny.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":0,\"sequence_number\":10}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0d9a44b6df400533016aa8c8e57f0c87d1be4c46b4c1d01c71\",\"object\":\"response\",\"created_at\":1789446373,\"status\":\"completed\",\"background\":false,\"completed_at\":1789446374,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Call get_weather once, then reply exactly: Paris is sunny.\",\"max_output_tokens\":50,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[{\"id\":\"msg_0d9a44b6df400533016aa8c8e61a4487d1b1f1c08b9d79b591\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Paris is sunny.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":\"resp_0d9a44b6df400533016aa8c8e36de887d1be260913d131b2ca\",\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[{\"type\":\"function\",\"description\":\"Get current weather for a city.\",\"name\":\"get_weather\",\"output_schema\":null,\"parameters\":{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":135,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":8,\"output_tokens_details\":{\"reasoning_tokens\":0},\"total_tokens\":143},\"user\":null,\"metadata\":{}},\"sequence_number\":11}"
}
]
}
@@ -1,8 +1,6 @@
{
"version": 1,
"metadata": {
"provider": "openai",
"protocol": "openai-responses",
"transport": "websocket",
"model": "gpt-5.5",
"tags": [
@@ -14,7 +12,7 @@
"full-context"
],
"name": "openai-responses-websocket/reconstructs-full-context-after-reconnect",
"recordedAt": "2026-08-20T00:00:00.000Z"
"recordedAt": "2026-09-15T04:27:52.291Z"
},
"interactions": [
{
@@ -32,37 +30,67 @@
{
"direction": "client",
"kind": "text",
"body": "{\"type\":\"response.create\",\"model\":\"gpt-5.5\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Reply exactly: Alpha.\"}]}],\"store\":false,\"max_output_tokens\":30,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"instructions\":\"Follow the user's exact reply instruction.\"}"
"body": "{\"type\":\"response.create\",\"model\":\"gpt-5.5\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Reply exactly: Alpha.\"}]}],\"instructions\":\"Follow the user's exact reply instruction.\",\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"max_output_tokens\":30}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.created\",\"response\":{\"id\":\"resp_ws_reconnect_1\"}}"
"body": "{\"type\":\"response.created\",\"response\":{\"id\":\"resp_0a6277dd90b94da1016aa8c94635e487d1b05f52fdfb9b7de3\",\"object\":\"response\",\"created_at\":1789446470,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Follow the user's exact reply instruction.\",\"max_output_tokens\":30,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"type\":\"message\",\"id\":\"msg_ws_reconnect_1\",\"role\":\"assistant\",\"content\":[]}}"
"body": "{\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0a6277dd90b94da1016aa8c94635e487d1b05f52fdfb9b7de3\",\"object\":\"response\",\"created_at\":1789446470,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Follow the user's exact reply instruction.\",\"max_output_tokens\":30,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.delta\",\"item_id\":\"msg_ws_reconnect_1\",\"delta\":\"Alpha.\"}"
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"id\":\"rs_0a6277dd90b94da1016aa8c946e33487d1b725d8e9dc874d82\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqqMlGU6VDsubzNyei9ZguJbtE8djsfaMGt1HYio6WGnP-8LNkfhUm9xhUBGxB8oqGcXBiqzUQa-aVgxTrxguZC_iD3ZyML4xXDWadyHkkZNerJyLBz8vTrFoZTnSBIVl460rTHLNoaUGTKlCHPMh_JsUtqN2tm4QG-6uYfNA7B5Mo-2JX-0Y3OoTOJsBwCgyTAcN_r_R9DCsXiuo_DemYFtjHlkurYc4wEaKAZ5EL9LHR3DhiSqXqJbO06zn_fSUKeMLxwK1TIHir88CV-b44rQcR5FCPX_PYfmWLXUbKtq6r_gsP1QFLRMOv90IQuVMpwSOa13tRbEDXN1ALNsbrFJDhzV1X2wKW7ovUiQoSLva0uKWqhjLo3VycjTksIBejFA3UeBEzSgrP_Kgiqk3FhTBFQAuEoeRMd4slQkL_kWZiOhcAnTRgW5VS-o9HaRR_-SgVFnXat8nxBgrv-eWocnKwEAU8NK-ZgS0IjJxNkuB3BPjFhNXljd0Ywaty4AITNfTE34yS_1zf3s9yIIQY8fd8lG6UeSU_WhjHBwmWpmMwZJHpzNGQquINGdGRZePCEptlilZquQkwgtI1UQbpG70uA-rvBtANpzBCV07oQl-MAgC6_PKXNrKt6njmyICJqvtiGhi9nl9B8rfktxoJqLtk8GUQpmRg-D8Xu5nQp_l6qg5jcyQm8rE3WHiHVUgQkPpjpmyIdypqH4PMOxJ2Qw-tXl9wMhQ9VdsFWHM81IBm5QDyGTl4yqiAasa-zz0kgj_ilAGuAyI4Y0xSAUELK6F_sAAlAGRdq0w3q_XPrMrdTuZfoBfsACoNxT4qawaU0VXhVb_6PuJAPjNIH8GFNMKATuQLOFvB3cuS6yraaAQa4G2YkGSVTiwZDVcoGWQRdLaLlUMpSQFuzO3Q_VlB3ZnOqcGXhdctGKiiln3ant8LT9LEqjb2s_uYP3vgbJi-QYkjsWud_35rNhyTgXg1rlVRrJ4Sd0UCtWD5fnrrhCgtr_oqPmefzCuhOrULpudQSkzYl8EAIkSjcV-NdfwQdar5GCmluHnGQ8qUcWRsEGmZF1ZX2b_Ni5HQhPvVWn-7283HKUy0Mqxl25ZPNRXq8XErU06N6bZvm5lQbdOjz06DzsPYRm1MkOsnW8wb9QzGlk_ZO_j_ROuh51nyMRmB1-r-tA==\",\"summary\":[]},\"output_index\":0,\"sequence_number\":2}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.done\",\"item_id\":\"msg_ws_reconnect_1\",\"text\":\"Alpha.\"}"
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"id\":\"rs_0a6277dd90b94da1016aa8c946e33487d1b725d8e9dc874d82\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqqMlHtG6yltzgW_UjUcYxvl2hoMkk7cSEH5SJMe9CR5gKIaCwCh4peUo8XZrd-EU-tPCXthv7JzHxYXGVG1fIgTJ7BjBoOh-jgd_oGlyCHj2jVPj7nVoB763tZHdyw_ovL3V7GpJ6VeLGIsRGqNTnBz47ZuKikTKrbTDupn6fT2wOM_69zwdg4-UVnoF2J9E_jIS0E5XRtRwOidqANl61HCwo7LV-Ut1aqCbXb-59vkrVGWPxD_8n4smf7Qjywc0FCH9zwuEDX4cPdqj3MmjvYPrn0jand9LKkAy9rblaKFJSQfVuritbHrdQrnF7hLxu7QCpehOzNXOkpNEqTmXnAZjfMc53hq-ahH6_KoyYlZpompwyGPngFeI2fKRqP8rlpDilD1BHBsh-bl7kXzI8HQ_jameXwPZ1La6gjtFkThXp53BD6BOx11SB9Nypqolu2at5rR32UYcGrYeGtTu-HmYGp0oHVFkHumTNuHKmGXV14dI9swgryfygLhX8EAJrWrjm3e8rvAkHKpAZ0IkmlCcp15UCFDKNeS560fQVRaKXWnQ7m0Ih3C1xG0ifJ89j27c8GHo1kAhEJk-lSB1-FZr9Ls_w7N772kmZ2a7LLswu3kNW78kPas_CtcOnBOHwE1DhcVh5YpxwNftOHZnK1v8NPNF8EWqio4ArZy1thMCzH5zXBNcFzgd8tePMFukBblSP8QGQoVYRqTne2sFoOZXjslXnDvEe-Ycj8X38zWgRiwAk7guOloFC7Se36KCDP357773Vah86gWCt55mSEyhVW_GF1oTuHvJ18GZsXcyN21scF1PSr8YaCM_jR7ZkU1GYXbQK2Y4oTAV9XDptQA5YzEREnn7muC_6v5ZTAglZF1lhn9Q0NwmylZEAXJdSGHaqXt1Hv-vQlprA_9m22vrreBOTLPnVK946J8absKrwfe-jK_1n_9YQR43uwH8XwFBFND0c4lICCQGbxwM8pX4ACWR0c19aORCYm-M5FrJsxmG29_aDVNhcvkoQ3mlP7ITQeqzkrjfytSwLb2BYpXYZKjEHNfV9j3JoxJobUkK5hrxXBhTvZzbVBnE0LSXQNwR-JAcOliP_jXBEeQ-28B-aGW8TI1vEP2i260QKgzOzPC-pOoFp0-vCvxojNO0kE16ECVTLfDNLNGSMZs9vekdLJP2akBp6PsUXsQUTbWO_wr1E2oNU7ctfMRxoh-yP0ZW2_xz_NjE72O5LF_6zuDVV7Q==\",\"summary\":[]},\"output_index\":0,\"sequence_number\":3}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"type\":\"message\",\"id\":\"msg_ws_reconnect_1\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"Alpha.\"}]}}"
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_0a6277dd90b94da1016aa8c947253887d184c150fcbcbcd8da\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":1,\"sequence_number\":4}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.completed\",\"response\":{\"id\":\"resp_ws_reconnect_1\"}}"
"body": "{\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_0a6277dd90b94da1016aa8c947253887d184c150fcbcbcd8da\",\"output_index\":1,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":5}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Alpha\",\"item_id\":\"msg_0a6277dd90b94da1016aa8c947253887d184c150fcbcbcd8da\",\"logprobs\":[],\"obfuscation\":\"wGOuc4A33dI\",\"output_index\":1,\"sequence_number\":6}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_0a6277dd90b94da1016aa8c947253887d184c150fcbcbcd8da\",\"logprobs\":[],\"obfuscation\":\"KCxWfYMrSVrn8yA\",\"output_index\":1,\"sequence_number\":7}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_0a6277dd90b94da1016aa8c947253887d184c150fcbcbcd8da\",\"logprobs\":[],\"output_index\":1,\"sequence_number\":8,\"text\":\"Alpha.\"}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_0a6277dd90b94da1016aa8c947253887d184c150fcbcbcd8da\",\"output_index\":1,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Alpha.\"},\"sequence_number\":9}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_0a6277dd90b94da1016aa8c947253887d184c150fcbcbcd8da\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Alpha.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":1,\"sequence_number\":10}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0a6277dd90b94da1016aa8c94635e487d1b05f52fdfb9b7de3\",\"object\":\"response\",\"created_at\":1789446470,\"status\":\"completed\",\"background\":false,\"completed_at\":1789446471,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Follow the user's exact reply instruction.\",\"max_output_tokens\":30,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[{\"id\":\"rs_0a6277dd90b94da1016aa8c946e33487d1b725d8e9dc874d82\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqqMlHAUEp7fOISGE4K9cq9a5CnGGpEQfe8UAd2LG_ZPCXUTjxIXWjHlK4WTB2a3HuGTI0Xe04whY-De7v9o_I63UqHHNMypHcucLABooY1oXaj5w1Vaj76sDaaIOm6kKlJaSwQEuDEDsh3TsNxC1wLZJ06FC5OrSGHcP_XKQ4EMOAy2INOaA8ONlqJ3ozUdLCcbh4ihmov-UMrVjEuzXHqg_xZ5TCDDZVmAQecxHOKnVnx9JqMHtAI5dz10KMpE910pGioPY1l_dKoYtlr-uc04Xje-kEXWiA52Gehf6bwWx9jokYIH-ymEVeitMMsVxx4MpK59F9byeUQgFMvx6-QaPYl6rgteZjcAHi3Tqgt4W3gzJC376V6_av_EgzfzvmicEQcmGU3-Ia1jf7F41J6NjxDMwpCpBBLK5UVUVXFZK7n4cRe9x7FdLbXRT1OK275Vu_q8g445POWvDB7q6lA6nY68wur1wcQREbBJ_jwViswKlg4b95jl9nigR4i6JHn3kpqPydmRZ2NUYDwvOdUnxARpWy1E7y2eql32NXaq5SaVezhwqnaAvgZusmXuVaA9MyoCbWNGawjUnEHP6nGk4106e_0kv0ByD9eLrCG1UtZ9OnfodTXSANNpQSw-AkcutWklrngg4Tp7gMBHiP0TPg2mo1NRtEAC62vvSBxA_IkJvLGt0ImGG-aLsSqgQFj5r3h8_sZCh6Uc4dOXXTwWIZ5rblzBlheejGYpc0KDBgvsMxUa29DI6APe5RJ7oy1lMtHttTedEYolJ0yWv9c2gfNynGwez4eZHMWON5mOvJus5uZQKUTBBG0qMMjnwHiVph-8Du7kE0vTTqGe8a6z9eErSDumwjokgiMux4nzRZTTTptj_7G00kEhcg3-BuGk64ASnZjnW1VRjbgaor3kaXbprXD6bU5tGrke2TD2_NTafYsaQ2e6mU7I8d0okK5lGsWfucH6dihiBuaNcwvwjuav8VmmO6s_GSubNkXnlSueFL8j8FfInko_oXOJ5EQxwFzg_StfINKBUgicJjIMTP3Oxe_CVMIKnDrde4EeX6RRRyk2hMOgTRXti_72DIrLG29iUPnA59DDLmPv07OOOkEoek9_A8JibqYye5CwnFJ7P0nMmWE4gjq2BLNlWLHVsjOtZ7U4fmUH_DgUnWWsnyp-hebStJgK_SSH5VOfpULDIDrCOBVXAx7w8avQUTYLdTnaBMdtWFbwHD8LfictodLw==\",\"summary\":[]},{\"id\":\"msg_0a6277dd90b94da1016aa8c947253887d184c150fcbcbcd8da\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Alpha.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":22,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":18,\"output_tokens_details\":{\"reasoning_tokens\":10},\"total_tokens\":40},\"user\":null,\"metadata\":{}},\"sequence_number\":11}"
}
]
},
@@ -81,37 +109,67 @@
{
"direction": "client",
"kind": "text",
"body": "{\"type\":\"response.create\",\"model\":\"gpt-5.5\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Reply exactly: Alpha.\"}]},{\"type\":\"message\",\"id\":\"msg_ws_reconnect_1\",\"role\":\"assistant\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"text\":\"Alpha.\"}]},{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Reply exactly: Beta.\"}]}],\"store\":false,\"max_output_tokens\":30,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"instructions\":\"Follow the user's exact reply instruction.\"}"
"body": "{\"type\":\"response.create\",\"model\":\"gpt-5.5\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Reply exactly: Alpha.\"}]},{\"type\":\"reasoning\",\"id\":\"rs_0a6277dd90b94da1016aa8c946e33487d1b725d8e9dc874d82\",\"summary\":[],\"encrypted_content\":\"gAAAAABqqMlHtG6yltzgW_UjUcYxvl2hoMkk7cSEH5SJMe9CR5gKIaCwCh4peUo8XZrd-EU-tPCXthv7JzHxYXGVG1fIgTJ7BjBoOh-jgd_oGlyCHj2jVPj7nVoB763tZHdyw_ovL3V7GpJ6VeLGIsRGqNTnBz47ZuKikTKrbTDupn6fT2wOM_69zwdg4-UVnoF2J9E_jIS0E5XRtRwOidqANl61HCwo7LV-Ut1aqCbXb-59vkrVGWPxD_8n4smf7Qjywc0FCH9zwuEDX4cPdqj3MmjvYPrn0jand9LKkAy9rblaKFJSQfVuritbHrdQrnF7hLxu7QCpehOzNXOkpNEqTmXnAZjfMc53hq-ahH6_KoyYlZpompwyGPngFeI2fKRqP8rlpDilD1BHBsh-bl7kXzI8HQ_jameXwPZ1La6gjtFkThXp53BD6BOx11SB9Nypqolu2at5rR32UYcGrYeGtTu-HmYGp0oHVFkHumTNuHKmGXV14dI9swgryfygLhX8EAJrWrjm3e8rvAkHKpAZ0IkmlCcp15UCFDKNeS560fQVRaKXWnQ7m0Ih3C1xG0ifJ89j27c8GHo1kAhEJk-lSB1-FZr9Ls_w7N772kmZ2a7LLswu3kNW78kPas_CtcOnBOHwE1DhcVh5YpxwNftOHZnK1v8NPNF8EWqio4ArZy1thMCzH5zXBNcFzgd8tePMFukBblSP8QGQoVYRqTne2sFoOZXjslXnDvEe-Ycj8X38zWgRiwAk7guOloFC7Se36KCDP357773Vah86gWCt55mSEyhVW_GF1oTuHvJ18GZsXcyN21scF1PSr8YaCM_jR7ZkU1GYXbQK2Y4oTAV9XDptQA5YzEREnn7muC_6v5ZTAglZF1lhn9Q0NwmylZEAXJdSGHaqXt1Hv-vQlprA_9m22vrreBOTLPnVK946J8absKrwfe-jK_1n_9YQR43uwH8XwFBFND0c4lICCQGbxwM8pX4ACWR0c19aORCYm-M5FrJsxmG29_aDVNhcvkoQ3mlP7ITQeqzkrjfytSwLb2BYpXYZKjEHNfV9j3JoxJobUkK5hrxXBhTvZzbVBnE0LSXQNwR-JAcOliP_jXBEeQ-28B-aGW8TI1vEP2i260QKgzOzPC-pOoFp0-vCvxojNO0kE16ECVTLfDNLNGSMZs9vekdLJP2akBp6PsUXsQUTbWO_wr1E2oNU7ctfMRxoh-yP0ZW2_xz_NjE72O5LF_6zuDVV7Q==\"},{\"type\":\"message\",\"id\":\"msg_0a6277dd90b94da1016aa8c947253887d184c150fcbcbcd8da\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"Alpha.\"}],\"phase\":\"final_answer\",\"status\":\"completed\"},{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Reply exactly: Beta.\"}]}],\"instructions\":\"Follow the user's exact reply instruction.\",\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"max_output_tokens\":30}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.created\",\"response\":{\"id\":\"resp_ws_reconnect_2\"}}"
"body": "{\"type\":\"response.created\",\"response\":{\"id\":\"resp_0a6277dd90b94da1016aa8c94776f487d19683416e4d24a143\",\"object\":\"response\",\"created_at\":1789446471,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Follow the user's exact reply instruction.\",\"max_output_tokens\":30,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"type\":\"message\",\"id\":\"msg_ws_reconnect_2\",\"role\":\"assistant\",\"content\":[]}}"
"body": "{\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_0a6277dd90b94da1016aa8c94776f487d19683416e4d24a143\",\"object\":\"response\",\"created_at\":1789446471,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Follow the user's exact reply instruction.\",\"max_output_tokens\":30,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.delta\",\"item_id\":\"msg_ws_reconnect_2\",\"delta\":\"Beta.\"}"
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"id\":\"rs_0a6277dd90b94da1016aa8c9480f6487d1a070a12dafc69e6a\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqqMlI9VZgFUiN_kctOUHXqwezRPZvkNVoD0xlUTiMU0SRYwrKB81mG-G3pcMVbgzfSb9WZ4Zywz-aEFa3nEsGGqi9W6UfFwKGM-APFrQkCxKkP43x230UnhDfb8Kf3yw-KB0o2OJFSgUxd2tJP1xo6JYbwubAv7IhrjZPESbWuAR0wfqGwmorHOvUmHIQr0XCTdtDlVcfopybb-ZTIo1UhCnAud48ze73p6K_yNU12k6auOpIu8prj2bn4O5kNS8tNIO5YsSVtaUqAMyUG6XmWCB0XiYerjvIYBSQb526_ex79YZrNwsQNE76iGO_sT-Vy2F36nqBhgAlHZBRxL2CWYEDgCehrNDktz3CUFKrahuLYBUk6oJb-85sZfAZO-MY6UqlkXZqTHcwBHGJdj8bkl8XTGUmOgAQC3StISb3zXYGtj8Vtdl5eYNh3Vhm2sSGrsgYOAlELTo-tqi4QtmrX1AHYbGPCmuj1sNieV7RcKSVaalHCj1WmCDbRGVPKgvRnQQ8WK5VnN_CPUL3pKJO3b617Yx1wneNzUsVFaUVOemkXd8G81qEZr_YmGBW-dSFqkNiSJH06ULFNvrmkFPhlEHtaiTC8lJFC45aGlM7BGm-cGw1ONt199FtL56t6u8p32wx2C6BvhvxiAyBeJpMQQw_I1NBBPO9REHtTbI0jtGClP9g5BBV0rzWlMsmjhyVwSC7d7xyqLIDuMuZ9nq11SA2BZDZtTQRryZU2DAUuJ6Cu_CAbPxmmscCEScVUJXUq8Y4rupf6GRrch6yocWn409ybBWzsUDXf9omAllM-FEWeLAH3Yg2BVVfBiHUblDgWpz2wXO9HIxY6amt7N8GpNiUYBl4c8EvalyoD7j7eLGqsdwrneS5mMMaozsnXw4bTcCYSMZ8yhtOWHeG7OLAGeb4gVwUFbH7LUz6A8cLKftljtshRFBm9E41NQ6J-KEG2Ia_AjOVgA0zCFZsaJFZ_LP-KVoj4At9k1yvSZyWSOJX_hfazXo3eHsi5p3j4wHz9U_ogQYPRkiMqQvUCtwwX_R7GX2D29NPSI4MlGKLoVhETUBcTOvTf4_6RxWhnBjl-1flGo6BtOMZ_s_mamho7jj8w3F_excGoM1NyvrM7fTy1-kCDMm48q1QyANxHCuQ9TwOgXJnmK3J-rWVNjuTCAKNdQ==\",\"summary\":[]},\"output_index\":0,\"sequence_number\":2}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.done\",\"item_id\":\"msg_ws_reconnect_2\",\"text\":\"Beta.\"}"
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"id\":\"rs_0a6277dd90b94da1016aa8c9480f6487d1a070a12dafc69e6a\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqqMlIaef7g4rrAJk05lFmziUYNLhpLqHcXC0CflH7X2Khf98dbAp53eKCZdS1vUdbbqBmIbW5av324WBrWupcwKKNZUxqUBcHBuI6VxqI6Wi95Z8jHkWUBUXPlgHB3KshumV-AhNKhagzdoe9NVgbTV4ki2Y6EwW-xM2yCKAtl7U05G9W8xq37MmmRL2PM68eIMYPjq7rLU24bEOC6yCs1Z708MTAAwKiCZY93q1pkOOl7dMkm82HmnmN_0tgKIT9UgX9tH2LUgKEmYj_xVWAArMvK1aujod6HtoA2E3P0Dcq_pkR49WdNENaFY4NWikxDeTfqw7xNCSmBZ5oo00sAnCndYhwPvvXy6dvnz66_-Tl6-uOxWmcFVedLiic0yML1O8MT7x6lz63vR1fjFqimo0_3ik-fqkXtQrxVSJpY_FTJH3rsAHiCSu0K9G6tytat3m-VrgxXyHguL-xn6JkXoBUFv-njlWc9R9DVIQFPPQLGwFxmQPwBYGlQRroU6q81C81ALkwvvbHws8a3lzRgJeSQFJNnzaHEFbdMyIW9Rem7wNISJyaZgcqRT531BCB-0UfVZTOUtl1eugXV0w_uvaLUcrGoA-beAaN6XbcTEuhQ5oXNiXdEQpt94glIwEd32oa3Ly3Sdb7-VcsOtKWfLHE4CnVDHv5Q4bx8GYTIkov4ZX6Y3lBB1G0xnAS45ucZZSi_crYa4c4dL2QY_LMqJgfZBdopttKrV43toK3aB2BjiehyP_2EjuK26S6UqiB-BDCX59LNu8cd3Ye-GK_bceFTPEbsoKIHn4Ne_DQgnVMKQ0M66jcoMuVXMhkoCjsAkTkVNM_rXbSFDxcnI6pGtliyGpeTNVs2LmSqpaTAJ1WywhPCP5jPL_1Xk2uXg3iS_EWJ0pDUOmpv1SKkWAkL-I_3y9IzcG4KB5bfJAf4SASB3GaAHfWPmFT70nVD5TtuEuxrhqXP1h9U0AAc4Y2aX9kSEIEJ4TWILyZfBN-7YKbH1R-LftmeccJsNfPAHjVHvMb2RLKjedFU3jhxHGblPDWAibkcQEv4ZOND3mhomtjqM8HaB2bhiVYl5KT9RqjcJGimgOr5-xbkyvHiojkZkl35doEHuuaF6b3_wibYpJANpBxFN0cDBZ0bAmBdx452c20EDsdHPvYWapI-gdYx2aHHMIgk-Xx9iZx1t1bnbY5yeKxJeZoFH4pV0vB01DZPmw-CpCRnOGp96Zdu26rjp4F9w==\",\"summary\":[]},\"output_index\":0,\"sequence_number\":3}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"type\":\"message\",\"id\":\"msg_ws_reconnect_2\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"Beta.\"}]}}"
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_0a6277dd90b94da1016aa8c948516487d1a9818eaf4845bd4f\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":1,\"sequence_number\":4}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.completed\",\"response\":{\"id\":\"resp_ws_reconnect_2\"}}"
"body": "{\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_0a6277dd90b94da1016aa8c948516487d1a9818eaf4845bd4f\",\"output_index\":1,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":5}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Beta\",\"item_id\":\"msg_0a6277dd90b94da1016aa8c948516487d1a9818eaf4845bd4f\",\"logprobs\":[],\"obfuscation\":\"WqDiBoHEYYfz\",\"output_index\":1,\"sequence_number\":6}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_0a6277dd90b94da1016aa8c948516487d1a9818eaf4845bd4f\",\"logprobs\":[],\"obfuscation\":\"PIv3yLskbYILa6g\",\"output_index\":1,\"sequence_number\":7}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_0a6277dd90b94da1016aa8c948516487d1a9818eaf4845bd4f\",\"logprobs\":[],\"output_index\":1,\"sequence_number\":8,\"text\":\"Beta.\"}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_0a6277dd90b94da1016aa8c948516487d1a9818eaf4845bd4f\",\"output_index\":1,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Beta.\"},\"sequence_number\":9}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_0a6277dd90b94da1016aa8c948516487d1a9818eaf4845bd4f\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Beta.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":1,\"sequence_number\":10}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.completed\",\"response\":{\"id\":\"resp_0a6277dd90b94da1016aa8c94776f487d19683416e4d24a143\",\"object\":\"response\",\"created_at\":1789446471,\"status\":\"completed\",\"background\":false,\"completed_at\":1789446472,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Follow the user's exact reply instruction.\",\"max_output_tokens\":30,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[{\"id\":\"rs_0a6277dd90b94da1016aa8c9480f6487d1a070a12dafc69e6a\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqqMlI1N6KzakCxDtgF9wHTAVvBjMChBW3G-Ys0eorlfORBx5SY6N5M_jQQ0xsc0C9iOJOsp1PFw68lHqNXrGBmmZ-Vh6LqYK20Wp7kX6oy2hizTvdgzZtszGxl56mES1XdVU7H1CdtX5ycdgwvE9vkx0to5Y-K1S_y2VPC1TluybnftIQkuKRs-97xQNeNH6hXq_YaMukTbzr-OKn28tYPx7k2u1oY5LeNCpYGTOtEyRFc56uzy66Z3bVpRVk0huvB43_NiA3p43coZVyKMQ0k3yzli1GqNaup1riouQRuqGXVgvN_Eix8oE6QaaOj4h3PaS6Z25Map-SAxq-3FMOiCGWwewjMvWYp3VmrvHOLfXzzUMIcYBRBdlNlvfMhT0dWhTS9_YR4QFixdCVemIUg4nv6292K1FHeXDT3HWGoJPsq4I3hLpqSd0PIXImJ3NVHchhV6KhV98Entde92sZv_y8f2JUvXRhPKsuC5uDIHHPoGl81AiDWNLyyu6SzBHFolHrLWH-lsqH-wHPl03qfh7dE_IcAVkyQl5UK1oHwsa7xEZsK2VoZvkUneUyYyGVT6H2uXKQEU4veY5HhWAoRq7lqgBqN7dD2xQelACMvYupUKB_bEJFSRiD3Rrkfhb-VItgbUjj44-T_DnYB5T8kjnWEWx767BPM1dmNrJK1NI5hD1vBVcrVknblccJDGlsBPSing24UGtkNJqJyclKUNBN9umZq7inBs-XDnqzMPfPX9X1nSP1cLGqbDlYNvDWgRritY3rySUZcu4rFRZ7J2RyiAuc7RgJTUXCyzouLVpGqfyWhDlFRJxS20zQfKUBHbywZ8pkcVav7TCdvIPXy7MMdxqJY8m3uhw2sLYvWs--U20fGVTY8_X3LN8ShyNbHQNj5OYMrG4uyrALiDtKbSTGgaC0uX0I6Ovu3OFrheLyWG9GNlAGn2ob5eZXPGq1uB-fJjnTftwQ8B3feDYq7SyBT5-LnBDd93QDdBvtsyHQHvtqv8c-S0ntAtcqP9lDNd1OkLjiFd7qcEDcq1Nxyn_9d2cPjYgGdiASW8X9FIE8Kf4nNTC9omgzAdPyS7RPj6Ke271QrWhjTjUuhWGvsiN5JSfGTIqe_4e1A5k990n5Q97Mjg-63N9HNJmUm5bJ6vCbYzet-bg2-jD-4_eUdkA2h1pFO56rlWItR7uxrnEWBwVwY438KezON-wa51JBjnqGQRSsBXvLt4wOb0xyPXfXVQ==\",\"summary\":[]},{\"id\":\"msg_0a6277dd90b94da1016aa8c948516487d1a9818eaf4845bd4f\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Beta.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":39,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":18,\"output_tokens_details\":{\"reasoning_tokens\":10},\"total_tokens\":57},\"user\":null,\"metadata\":{}},\"sequence_number\":11}"
}
]
}
@@ -1,8 +1,6 @@
{
"version": 1,
"metadata": {
"provider": "openai",
"protocol": "openai-responses",
"transport": "websocket",
"model": "gpt-5.5",
"tags": [
@@ -14,7 +12,7 @@
"recovery"
],
"name": "openai-responses-websocket/recovers-from-explicit-continuation-rejection",
"recordedAt": "2026-08-20T00:00:00.000Z"
"recordedAt": "2026-09-15T04:31:59.100Z"
},
"interactions": [
{
@@ -32,37 +30,67 @@
{
"direction": "client",
"kind": "text",
"body": "{\"type\":\"response.create\",\"model\":\"gpt-5.5\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Reply exactly: Ready.\"}]}],\"store\":false,\"max_output_tokens\":30,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"instructions\":\"Follow the user's exact reply instruction.\"}"
"body": "{\"type\":\"response.create\",\"model\":\"gpt-5.5\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Reply exactly: Ready.\"}]}],\"instructions\":\"Follow the user's exact reply instruction.\",\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"max_output_tokens\":30}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.created\",\"response\":{\"id\":\"resp_ws_rejection_1\"}}"
"body": "{\"type\":\"response.created\",\"response\":{\"id\":\"resp_01cc0cda24c36acf016aa8ca3c1d3c87d1853283f43675e411\",\"object\":\"response\",\"created_at\":1789446716,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Follow the user's exact reply instruction.\",\"max_output_tokens\":30,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"type\":\"message\",\"id\":\"msg_ws_rejection_1\",\"role\":\"assistant\",\"content\":[]}}"
"body": "{\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_01cc0cda24c36acf016aa8ca3c1d3c87d1853283f43675e411\",\"object\":\"response\",\"created_at\":1789446716,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Follow the user's exact reply instruction.\",\"max_output_tokens\":30,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.delta\",\"item_id\":\"msg_ws_rejection_1\",\"delta\":\"Ready.\"}"
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"id\":\"rs_01cc0cda24c36acf016aa8ca3ced8c87d1a14c5c6a2ced8544\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqqMo85QOkFAOiRbNGARO6tiDQnoZhPmdpq6tLJKMp5JE7PAThNVvyzKnxkYsSvaaNF0vh9uwCbaU2WjcPreWkg9govp2cRVGA83YD2p1J9p42ue_5_OepyoHti2_PprBOAWaGQuAImj_40hZrPYTciQYVDHegSmz4qYG34d4AzCsPYLK1FCJgPg25rd-YaJE1F70qP2Ky1Nf2haNj5lj-a1BhBX6dQegTX2WUUeufguzdt0TICYc4dxkvoaqVXGVn61GvrKCzUsDmq48YrgToaeolJJwl_tmDJlxHZ7HPCZsoYhHCHVcF662wC0G8t8nPDCJ7yRlbgPDXDk58ap-OMFioxGR_O3Qbrq-ZqUGgFboN2M8HsXxtVbO5Ik9zWQCsK13Hw5OOa8LrTsPyVHPOrhSSO5Jdqts2q9VuS9aT3dB1GTmBRGtkV9oL4iEvZ158n3xsTuPrYiWWP6yY2hWVrQUAI7KQViUp9T1p8NdIou8XJu4_zuFV2ogJMY3Pfa6Y8clr04SWMsfpxZLqJ8Z5vagcy_JWFElOY2uYmS2AetP5uRoq4HJrcPEk_hnoGY6MDR7v-9Nek41bkJ2s9H-oWWy9M1mb_bSn92E280ljoBiPrME9iopTXdkbo8As2oLgfWsMHxc3x05t_bRFtIp5N8mzVpBTBMb_JlBEIhIhu5E4mVl1a_BxQa6-NgCV8SJVDrnISsNcJ2zHVFOQO8fWLsBhnPCsmGgCxpLJcT1YlbfoDRuYJ-6sR5NRoHtswgA3E5WwptgFdeH7WWVLLqZrHI9__Pv2lF4Ne8fMeZjiiz5QquV2dIIOuwnAU66XDm1YDjmcEMNMLcpo94ZZQdb17skuQQOgrximW9pHrsTJtBSmYTgW3YBx1ZGX3cdgGhr5YY4IDKkMPMs-hZLgPt8Z6BL1HryYxaqha5QyE6Q2EwTH-mK7mCOxGXEp98CXpd-LJ2difKdEmJ1A1mn2PGB-44dKy6EiKI2U5_QoBeEc_CtqkkqNtvueaiyvP0xxoga9YLKylfpOiZwvG1UmxV5nunQcvUsYTLfBFLko7WqlLUZgVFLywOGnbxhjkZU17nSKgHgHIceWCXNl5G-QqKcmObD1hhZuwDH1V5At2fc40SSxu2a2r1IZWSuzKEBzgH1KEcr4lTqGDEs3Uez0ZjNL4O0VMA==\",\"summary\":[]},\"output_index\":0,\"sequence_number\":2}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.done\",\"item_id\":\"msg_ws_rejection_1\",\"text\":\"Ready.\"}"
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"id\":\"rs_01cc0cda24c36acf016aa8ca3ced8c87d1a14c5c6a2ced8544\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqqMo9M9B15LsV1CLsXpNJpI08GiwCAK97hTheQ2s1lAkKgARMs1HIIXVeAr-tbUq88yN51fiQE8HvGwdF9ZcNI6bZle8D2PS7m8O7UE7C1Z_HX_fz2f0pRHyRfWpVAT2gYQWIDeZu6UdgxbKPBW0lXWzlk_relHG5x6nXkYzZoEeVasqavWoyMMSX7cexe-IYJh6e_3DgRpOchueS8Z-70P0w5R83Ea7UXZQNhMA0yDEYu_td2PmE2Pd2PUTOB3mxF2pb1z7-2t6S0UryhHx0az7Gh2eT60GGUqz9CIZzNE_FX--tszeuO0eI92Cen5tirOUHBTyyDqE0eG26DRl_p_U-xDZwaQONbtYbvkvrj-G7FA2oZXxjJPHuQZsNgBslXS-H0KT1lx3Y8XJ9QMVjFLFaucFG64wCmXPfCH8dYtX_YqfYQR4lwNfiSbyJEX2oDvTVVD_aCJ9NRo7c0aCTtmKBvr6fvvAy3MAFxAp_Sm2nMx4P5GYO4qAmJDByywKw-VK1vHlv3NRmVsAgbArIFgm-axoCs2PLpvZjDqeQGPavaq8zKWTyZYqBsEzKZUtGOZfYjD4mud0Z08I4i2H4K-L00ccVauode3548ZipOIuslbhJxonQXsF6TFdW2Hj8E5JjoEr5IbmwHyI0PBcDWW5AmkjHLwr9v08mFppRoD-2wzPAd5igROAuUbvJhiQd2A-uOaohwMjdpFxrjyUqgGTlI5g7tmI1ceeQWms0bKm8Pd0wIqVM3Nq6YvV7XfEyeogfRMVUQezr_lES42ZMVAoKBSFzMysDwCFkhVNVclcTUpcUUbVp21FChG7Ag-xuq8Cl6OGLA8nWX1C0aCf2HNa-n3dkYr1DtUziurh1MD-UIs5jdGiq3ptrc0VaVZwNdD4jVfAoHB_Ws7GiISXuclfpqsG3DTJEfzlbukI1vxXrt3FArsHiQvQjW5UM7gGel32M6p8AlXRxnez9PgIuU1WrtBUJetk7m39AZwp_aqbqC-AJ-MF70xP1VJZwFN-GeNL3VZsRHePFG4h7Pj---CCZRGlmzuE1-b-sIE7Bn_gbue_qHFMZJhAY55MO25vfZbSoLWHZCGmLMjJVHOYCPoy6l7zyxNmoIcC58QILNWal31KLCDmCsASmZC-xQRjyFwt-kvLbvk38Dc02IKcP3ujhf6WRRr1A0hh1K-gXmv_XI_MUFDcOVIjjhB-rXgjWSCoKfaKI9GCIsb9mnNaBq65BWg==\",\"summary\":[]},\"output_index\":0,\"sequence_number\":3}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"type\":\"message\",\"id\":\"msg_ws_rejection_1\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"Ready.\"}]}}"
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_01cc0cda24c36acf016aa8ca3d4a0087d1950121aed4802434\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":1,\"sequence_number\":4}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.completed\",\"response\":{\"id\":\"resp_ws_rejection_1\"}}"
"body": "{\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_01cc0cda24c36acf016aa8ca3d4a0087d1950121aed4802434\",\"output_index\":1,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":5}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Ready\",\"item_id\":\"msg_01cc0cda24c36acf016aa8ca3d4a0087d1950121aed4802434\",\"logprobs\":[],\"obfuscation\":\"yVIztfbsPuE\",\"output_index\":1,\"sequence_number\":6}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_01cc0cda24c36acf016aa8ca3d4a0087d1950121aed4802434\",\"logprobs\":[],\"obfuscation\":\"JCmv7pYK1A0iw4B\",\"output_index\":1,\"sequence_number\":7}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_01cc0cda24c36acf016aa8ca3d4a0087d1950121aed4802434\",\"logprobs\":[],\"output_index\":1,\"sequence_number\":8,\"text\":\"Ready.\"}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_01cc0cda24c36acf016aa8ca3d4a0087d1950121aed4802434\",\"output_index\":1,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Ready.\"},\"sequence_number\":9}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_01cc0cda24c36acf016aa8ca3d4a0087d1950121aed4802434\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Ready.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":1,\"sequence_number\":10}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.completed\",\"response\":{\"id\":\"resp_01cc0cda24c36acf016aa8ca3c1d3c87d1853283f43675e411\",\"object\":\"response\",\"created_at\":1789446716,\"status\":\"completed\",\"background\":false,\"completed_at\":1789446717,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Follow the user's exact reply instruction.\",\"max_output_tokens\":30,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[{\"id\":\"rs_01cc0cda24c36acf016aa8ca3ced8c87d1a14c5c6a2ced8544\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqqMo9oK4vZmZaaep7pLFevWG5a8w530vO67UGYaUMhJxBeIdygArtXWAA7aPdnm5m7rnAIsynP6yKszDOlGUU4Yy2ozVYoTaB9xKu-q1TV4xwiw5B5-mD_xYgjOJsCG1UQMw-eKq886C7ZkEHk67DhT-LGHfNtaWbWmyZQVAul00QOB7hljH7heOSmlhg75On58XJasazC6j440UnI75tT6hyvput0zEcnY3zFCFgQqQLY1pQThlf2lYsZflHn7Mxofu1w0lePWJhXlkUHTAoouavXhTP-PcfYghF9jtE7MyNozAeM5UzltLtVRJcC-0vwltStdfeME_JTw_PfOnhSXTLQCQUmyPX3azIklIQREQbsf5nrFDSdnlYrAFXGsyzXGCfM66HoE1fOTnSFkcwcsnC23a-lfSE8b7VTLG6A2jpHMxKuf-0R37Hzz3hWC9apQcNUnvNGLWF5nSLEGxFX3dIYcrzVdJDhFaEIxSSxf2HbrSTMdZY6mkdLKBwmYCpztLD4xMufdWPfCzEMsdAGWzQ8Xkm3baG_r6ts4ojsE5jLCLyfi1fSO69fuoLrm2dx4A8Wh_euL9_eiVmBgXeL2PLjB72ipT3l50gezTlTndv2VjNO3EBSzoktlQpSD6x3XruDvN7_gP26-QryzgpmfTOFjj9__Tv_UptzVjUZVBeCvfeTaFhHvaL_DHpcges__3_9LGh86zb4YXGDDrQUgmMKq_5mu4fhIRWuYt0SAxQIrmZRtvMET8i2CgjZ9bz5ivm2WMt8snzhrrOuA1b9DzmOVTNik0SwPGLtFMKgSFCfGlydQ_leYuEVZgtSC2uOavta53OHQ_GeBkzNjqrkqgspQI6virJxYQwSasCCmrU4b5GmoMj2571Qxdx4Kybc3txSX2E-g5LkBIUUgFGAgBVAipFnCTELAT31UL-nYQpFmmN5qmHchzCCyvuLxHmQIolkDNM0wwbkltO856tA0HmAy7dcAWgewMdUN_A4--Yr3DTPJ8MNqfLQYCFOq0n7QukaUx7V14FR-3O7RkCHFzUdSkuAqUKNQ4-7U6ME6CskkUREPI-YTRpml4YOD_iumjwemkoPq7d2tNw3lptCaE0OEKZ3fbv5LDklsl3Uvpe7empEh3DtEQN4aZuMT7Fcjq3tBTvd0KOFYrPex62smCVbTPE6KPZ52TvQKxOto-sV8Z9CZDf6TT7Uxdo-hH5-uvBO1ax_lVSYvsOlUWOELnFYQ==\",\"summary\":[]},{\"id\":\"msg_01cc0cda24c36acf016aa8ca3d4a0087d1950121aed4802434\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Ready.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":22,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":20,\"output_tokens_details\":{\"reasoning_tokens\":12},\"total_tokens\":42},\"user\":null,\"metadata\":{}},\"sequence_number\":11}"
}
]
},
@@ -81,47 +109,77 @@
{
"direction": "client",
"kind": "text",
"body": "{\"type\":\"response.create\",\"model\":\"gpt-5.5\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Reply exactly: Recovered.\"}]}],\"store\":false,\"max_output_tokens\":30,\"previous_response_id\":\"resp_ws_rejection_1\",\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"instructions\":\"Follow the user's exact reply instruction.\"}"
"body": "{\"type\":\"response.create\",\"model\":\"gpt-5.5\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Reply exactly: Recovered.\"}]}],\"instructions\":\"Follow the user's exact reply instruction.\",\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"max_output_tokens\":30,\"previous_response_id\":\"resp_01cc0cda24c36acf016aa8ca3c1d3c87d1853283f43675e411\"}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"error\",\"error\":{\"code\":\"previous_response_not_found\",\"message\":\"Previous response not found\"}}"
"body": "{\n \"type\": \"error\",\n \"error\": {\n \"type\": \"invalid_request_error\",\n \"code\": \"previous_response_not_found\",\n \"message\": \"Previous response with id 'resp_01cc0cda24c36acf016aa8ca3c1d3c87d1853283f43675e411' not found.\",\n \"param\": \"previous_response_id\"\n },\n \"status\": 400\n}"
},
{
"direction": "client",
"kind": "text",
"body": "{\"type\":\"response.create\",\"model\":\"gpt-5.5\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Reply exactly: Ready.\"}]},{\"type\":\"message\",\"id\":\"msg_ws_rejection_1\",\"role\":\"assistant\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"text\":\"Ready.\"}]},{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Reply exactly: Recovered.\"}]}],\"store\":false,\"max_output_tokens\":30,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"instructions\":\"Follow the user's exact reply instruction.\"}"
"body": "{\"type\":\"response.create\",\"model\":\"gpt-5.5\",\"input\":[{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Reply exactly: Ready.\"}]},{\"type\":\"reasoning\",\"id\":\"rs_01cc0cda24c36acf016aa8ca3ced8c87d1a14c5c6a2ced8544\",\"summary\":[],\"encrypted_content\":\"gAAAAABqqMo9M9B15LsV1CLsXpNJpI08GiwCAK97hTheQ2s1lAkKgARMs1HIIXVeAr-tbUq88yN51fiQE8HvGwdF9ZcNI6bZle8D2PS7m8O7UE7C1Z_HX_fz2f0pRHyRfWpVAT2gYQWIDeZu6UdgxbKPBW0lXWzlk_relHG5x6nXkYzZoEeVasqavWoyMMSX7cexe-IYJh6e_3DgRpOchueS8Z-70P0w5R83Ea7UXZQNhMA0yDEYu_td2PmE2Pd2PUTOB3mxF2pb1z7-2t6S0UryhHx0az7Gh2eT60GGUqz9CIZzNE_FX--tszeuO0eI92Cen5tirOUHBTyyDqE0eG26DRl_p_U-xDZwaQONbtYbvkvrj-G7FA2oZXxjJPHuQZsNgBslXS-H0KT1lx3Y8XJ9QMVjFLFaucFG64wCmXPfCH8dYtX_YqfYQR4lwNfiSbyJEX2oDvTVVD_aCJ9NRo7c0aCTtmKBvr6fvvAy3MAFxAp_Sm2nMx4P5GYO4qAmJDByywKw-VK1vHlv3NRmVsAgbArIFgm-axoCs2PLpvZjDqeQGPavaq8zKWTyZYqBsEzKZUtGOZfYjD4mud0Z08I4i2H4K-L00ccVauode3548ZipOIuslbhJxonQXsF6TFdW2Hj8E5JjoEr5IbmwHyI0PBcDWW5AmkjHLwr9v08mFppRoD-2wzPAd5igROAuUbvJhiQd2A-uOaohwMjdpFxrjyUqgGTlI5g7tmI1ceeQWms0bKm8Pd0wIqVM3Nq6YvV7XfEyeogfRMVUQezr_lES42ZMVAoKBSFzMysDwCFkhVNVclcTUpcUUbVp21FChG7Ag-xuq8Cl6OGLA8nWX1C0aCf2HNa-n3dkYr1DtUziurh1MD-UIs5jdGiq3ptrc0VaVZwNdD4jVfAoHB_Ws7GiISXuclfpqsG3DTJEfzlbukI1vxXrt3FArsHiQvQjW5UM7gGel32M6p8AlXRxnez9PgIuU1WrtBUJetk7m39AZwp_aqbqC-AJ-MF70xP1VJZwFN-GeNL3VZsRHePFG4h7Pj---CCZRGlmzuE1-b-sIE7Bn_gbue_qHFMZJhAY55MO25vfZbSoLWHZCGmLMjJVHOYCPoy6l7zyxNmoIcC58QILNWal31KLCDmCsASmZC-xQRjyFwt-kvLbvk38Dc02IKcP3ujhf6WRRr1A0hh1K-gXmv_XI_MUFDcOVIjjhB-rXgjWSCoKfaKI9GCIsb9mnNaBq65BWg==\"},{\"type\":\"message\",\"id\":\"msg_01cc0cda24c36acf016aa8ca3d4a0087d1950121aed4802434\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"Ready.\"}],\"phase\":\"final_answer\",\"status\":\"completed\"},{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Reply exactly: Recovered.\"}]}],\"instructions\":\"Follow the user's exact reply instruction.\",\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"max_output_tokens\":30}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.created\",\"response\":{\"id\":\"resp_ws_rejection_2\"}}"
"body": "{\"type\":\"response.created\",\"response\":{\"id\":\"resp_01cc0cda24c36acf016aa8ca3de54c87d1b3f522e8bd2f6982\",\"object\":\"response\",\"created_at\":1789446717,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Follow the user's exact reply instruction.\",\"max_output_tokens\":30,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":0}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"type\":\"message\",\"id\":\"msg_ws_rejection_2\",\"role\":\"assistant\",\"content\":[]}}"
"body": "{\"type\":\"response.in_progress\",\"response\":{\"id\":\"resp_01cc0cda24c36acf016aa8ca3de54c87d1b3f522e8bd2f6982\",\"object\":\"response\",\"created_at\":1789446717,\"status\":\"in_progress\",\"background\":false,\"completed_at\":null,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Follow the user's exact reply instruction.\",\"max_output_tokens\":30,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"auto\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":null,\"user\":null,\"metadata\":{}},\"sequence_number\":1}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.delta\",\"item_id\":\"msg_ws_rejection_2\",\"delta\":\"Recovered.\"}"
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"id\":\"rs_01cc0cda24c36acf016aa8ca3ebf4087d18e71282a5ea4bc04\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqqMo-dtBCl_gnEkmEpBqej9pZjra5vIb7stkXTCmIZF_R073a3SEI66YOhJlTwQOoLnzXCdKku5QKF3HWUTITgGcEYVoH9mR5b0iDs_KMJ5XSBr-TslHOj9msVlDvDeBbbXJ5Yuq0hfmZS7wwwyyR-D429_wvAR_sKJnnf_RbPSY9Wmd9EZciG7VOsBsBJg4hXiB8W_0LmbfxJgLpxubA69UTC8keEzfl6YNLdPH3MYpdclyuYWdnM9o1EG-kkcXjElN1ATS4lt2qTXFSmi2akPyDTQNRRiYAJxwmmf_bTotggvtW1EkGeuCUcet_8vzvG371m83kEQpCyhgXnofjdOyloVpcxwij6Avc63lD4oiHy4ic0em_BgmJfOTvEaBMBSuB8KM2lWscWRYfD_zgnifXYRVaLsZ9cRspJ2hO7-3pBu2_j_gLs8SAxW1YhHyY2rMbMNJbnSiEanCbATplivIc6wzB-41zR2h3U-wGwkv4pywLg0A_aMAnqypW7iYugtVoaFXYq7o24sW6JvkVLEpEXABpfbbjT_SdBfL08Ta-pwmptkKKmP4lzV38JgbXynB4N-pbSAD6Q5At79qn-0KETydNuyPNn-ymQQKOIidlRjXfb8Z8PGoaqmRXCkyxcXTD81EE5UVBWsJeQN6T5RzQYYUggo0KjshBr2FAub7b9MqBjUSXnsTxwcmPZFeP3WZfTbuT-r0qxH6Ni8BcTy5K4c0qmiEdzF2OvCFIwZOh__tprFFiiUmIxwKrcI-hMEU9ilq-nFzC5QleS31l8WkzpAKC2iWRpKz_EK5FJ-XoAFw3rtmAN-6mPt2_aZlZNdFAXn4JUr3lccT3jzyCN6-6WLorKHNpYgi715WfpQoDjYbGKwpCjvIj6R7Msw-QELH73FRL4dUj2R0kciA04KFIa_eFiS1mwSuVorX8WGM8bYkCrctkbeP4ExrUMoix4g-cMvryOvRG6cngk2PplXF67v8xL_0FtFYaxzK8OpEwDlUuEclqiJS-uF-kVcR_FE7sQgz6gUqfm2fCKq99bZ1K2ylyPd8jTFu3dcZ4gr1BOm9IHMTt3QBSV1l6q-A_6v6FBZoJrbty_x-hamWuWgpemzG0d9iF6MppFiSNd6PlrMH112Yp0Aqv8pJ1wyNOhWtWwcH6O-LD3K1vMNeRt3q13w==\",\"summary\":[]},\"output_index\":0,\"sequence_number\":2}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.done\",\"item_id\":\"msg_ws_rejection_2\",\"text\":\"Recovered.\"}"
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"id\":\"rs_01cc0cda24c36acf016aa8ca3ebf4087d18e71282a5ea4bc04\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqqMo_jv-Y0S-TLI5hac6vAjiuTBsft7PJge5P9vpm8zR-PFxA6DDAm92Ff8_hWSyrqSjSv5amBU4yqqANYzJx3reX0fSFHBMmW3BDTifJ9rVSw_jnhK8_3o4m0wPDzbhk-jlVE6wU72_7AZzb0xt7Uo3cvh-PYtvomA9oJx_P3G06DDvJ6D0ea-5C6fYkHkMCW2BIED5oNrX88Ety__OPiNP__cX0AuODa80Ls-_lGKJI15AaLaJGMVvgZZvB86wjMHT_UBK7xwAvyKziCWgp3oh4jR6SUnTtkF5EwZs5sQbmXN3dG0DN7P7c-DL7qLM4y3bIqjhKjNJJ1fBpE56pAkAkWFGjuxM9W8zLDuv-xMyun2B4ttdKVdl7eubr8Jz_cNieGTXtpfYsntuxDEIl-bYSxlhOLkpiWACXQlbFxCCKb3e4Tijke-Z0I8OiarvRgKZyzCn_wXF7nNqvXNj_AkYV8FQuF9d2iqSata399K_w8sM1Zt3pbbJB6_9Yxspp89TVlvRITXWH_0xSOWDG1D3f4AiOQ2y9xSUmweHHTUgxRLfhAJlrgGzE7lDRLKEe_SOSXfx3ONEprhQpeHW_kWAVxXaQjuMaIwzsl6REmtZAZjjvw1q5TIAkB_hMDhmnpsB9_XFVR97il9NwTLnpkLmFerxo6i5Z0-ElUuy8QbzM0xuuBY5B4UqKpZI42C3znxGn_vzct4DNYffVEdA5KMIg_om4Dbsuz2QPfo-8WSOwCcme9AqPpiPPi8ieGR7VsVoq26qeuB-nEr9kxZnBZvWakSZsAIue22ZP3nJqxUgi0zvnftPxN21HvQHzhUNw69WnmGLSE_sfehdlKDCfSIC0irNpW8NH8w_gIp9zCHIaG6mfqY6aERoEw6R4oDQxEimJP2E6zoJzLWfObBasmWrJBScB_K_Q7sopD3fSaHUAhI4o-U3hL-8v_1BpfSMuZWRhDb781WjNY4BSpB9WfqAM-jEWzdbfp60G7Q1ZdDVgqny5a1KFyalyxb44imnoPlNh-fl_c2iqEZRAy4-TSo0RxfjwKSIvF2GCnR8jG7tKH5dYFgCeKWbzWFgX22Gg_4ZdLh5-kmMqEKIzQkqswOYmiCBvCzK7QZPl8KZkSpvmXkkQvKKlR7nakIdOpkqzb087KXWOxzkCc5uudq0nxPvGkMoDS1g1mEE4qFX5w_ZJYaW6ULENzcK8NR6BaMmwvU0L\",\"summary\":[]},\"output_index\":0,\"sequence_number\":3}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"type\":\"message\",\"id\":\"msg_ws_rejection_2\",\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"Recovered.\"}]}}"
"body": "{\"type\":\"response.output_item.added\",\"item\":{\"id\":\"msg_01cc0cda24c36acf016aa8ca3f17f887d1aa91596ed4fd7a68\",\"type\":\"message\",\"status\":\"in_progress\",\"content\":[],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":1,\"sequence_number\":4}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.completed\",\"response\":{\"id\":\"resp_ws_rejection_2\"}}"
"body": "{\"type\":\"response.content_part.added\",\"content_index\":0,\"item_id\":\"msg_01cc0cda24c36acf016aa8ca3f17f887d1aa91596ed4fd7a68\",\"output_index\":1,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"\"},\"sequence_number\":5}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\"Recovered\",\"item_id\":\"msg_01cc0cda24c36acf016aa8ca3f17f887d1aa91596ed4fd7a68\",\"logprobs\":[],\"obfuscation\":\"Gd6uxmr\",\"output_index\":1,\"sequence_number\":6}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.delta\",\"content_index\":0,\"delta\":\".\",\"item_id\":\"msg_01cc0cda24c36acf016aa8ca3f17f887d1aa91596ed4fd7a68\",\"logprobs\":[],\"obfuscation\":\"4yZd9PKDNMCzGFV\",\"output_index\":1,\"sequence_number\":7}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_text.done\",\"content_index\":0,\"item_id\":\"msg_01cc0cda24c36acf016aa8ca3f17f887d1aa91596ed4fd7a68\",\"logprobs\":[],\"output_index\":1,\"sequence_number\":8,\"text\":\"Recovered.\"}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.content_part.done\",\"content_index\":0,\"item_id\":\"msg_01cc0cda24c36acf016aa8ca3f17f887d1aa91596ed4fd7a68\",\"output_index\":1,\"part\":{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Recovered.\"},\"sequence_number\":9}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.output_item.done\",\"item\":{\"id\":\"msg_01cc0cda24c36acf016aa8ca3f17f887d1aa91596ed4fd7a68\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Recovered.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"},\"output_index\":1,\"sequence_number\":10}"
},
{
"direction": "server",
"kind": "text",
"body": "{\"type\":\"response.completed\",\"response\":{\"id\":\"resp_01cc0cda24c36acf016aa8ca3de54c87d1b3f522e8bd2f6982\",\"object\":\"response\",\"created_at\":1789446717,\"status\":\"completed\",\"background\":false,\"completed_at\":1789446719,\"error\":null,\"frequency_penalty\":0.0,\"incomplete_details\":null,\"instructions\":\"Follow the user's exact reply instruction.\",\"max_output_tokens\":30,\"max_tool_calls\":null,\"model\":\"gpt-5.5-2026-04-23\",\"moderation\":null,\"output\":[{\"id\":\"rs_01cc0cda24c36acf016aa8ca3ebf4087d18e71282a5ea4bc04\",\"type\":\"reasoning\",\"content\":[],\"encrypted_content\":\"gAAAAABqqMo_tTrOofpc1jivBwgDV6YyVCiZNC4DHnW2mXBF8Yaw6kA3-psWdVLAwpZiAYNDSlXLdFZlYfiBGuT7StE00bzH5sXfOz9ioSiCr-GMdJ74UEw_V6OeuRwTEcPHh2ILuKI4o4U0eP1xg7fye14XhnOAEwbd1kkkP2nl6llT-nixDOj7eGVgl4MdVbVnp__XVkio7trNK1EoZO4zc9SN7MRU-EbBxjFJPXZNey3jsv2rNjsfzbI7pEMFAS9OwY9FpX1r-8p1l2MBOeiSPl4eFLBou0ACrfiray-WHQ-d6Mkxzjgx268oLrfqIv_DGLVZOISC09kWUoqXSqqRBKV_IM-0lHqUx5pUUi9l4reUQzH9EynPvIMOHD_FapHVI8--CJp-Bkm8qbBKVclonOXqaZtGQ8N0Dnp-1WwSrt_5dE8-k7aU1gQABgfs1g_EaE09tnjfld64jgTwZrfFP4WxhzyINnTXpPtX8Lwf3Cjlu2_xbu0VEy2i4_s7rZ2IIu7zDN-yakOkiqbe38xT5KX2YRPCeB88pdSM17hjIjbyKDFx1TZbZyTSSGvs9XjyaEDr899CeMjNyMaBfJwhrMdFW5P7H6BlIASKVJhJw27RYW4EuQtslXVFzpzmtRnqebPLtijXT_krg7icWL7a5YAS2q5V-XXmWLSuKJW6wNpp1vdHpxl8ZOWfeNvkKlqO0cd8grYBd_l9Evx4EoF0YSIXXUCWpMIMhJgLEPt8eOW4RBG3aPk83C8FINyReXl3Rq9aVYcEowwKAxIGHyuXRjnx-Di2JqmtaH_G5kpuPkACEhSKjWp7Sv175lWdBpUvGAKDA6GDSHLz5vtJsPDFIyYecAxLyDZ0VYVosaIfW6otisr6sIWuxL4AXoLAD9fiXh6y_s2Gbb-DSQesjF7oJvftcHMxnL2j5gZ6_Z1d03C4UjbqYS9vy2vSOOQ4VR8CX4QhNIXbYeEUhxWdmtWM27zcJ_yCm_H4hmo4QAAv83C64qP9eVZ3Z7g84T-IYz8C-8oM9n8ceitghRZ-G7X6uVUUiOUj1Tmx1uekDrFNEbHg9XeWGMua2z7Y3UUl9iq0r_oJwBxxBY3QEk03Pwulk6cx6LRDoP_30MHV-oLRcHKeWKBjIld4qio5z_xoDZKD2b_KB5i43gfPk8B-vzI9tBmtNLsPKOIsbSyj8Oo9CJv_1sXgU8IoM1uxtPDpg2W8pGL4rkEC\",\"summary\":[]},{\"id\":\"msg_01cc0cda24c36acf016aa8ca3f17f887d1aa91596ed4fd7a68\",\"type\":\"message\",\"status\":\"completed\",\"content\":[{\"type\":\"output_text\",\"annotations\":[],\"logprobs\":[],\"text\":\"Recovered.\"}],\"phase\":\"final_answer\",\"role\":\"assistant\"}],\"parallel_tool_calls\":true,\"presence_penalty\":0.0,\"previous_response_id\":null,\"prompt_cache_key\":null,\"prompt_cache_retention\":\"24h\",\"reasoning\":{\"context\":\"current_turn\",\"effort\":\"medium\",\"mode\":\"standard\",\"summary\":\"detailed\"},\"safety_identifier\":null,\"service_tier\":\"default\",\"store\":false,\"temperature\":1.0,\"text\":{\"format\":{\"type\":\"text\"},\"verbosity\":\"low\"},\"tool_choice\":\"auto\",\"tool_usage\":{\"image_gen\":{\"input_tokens\":0,\"input_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"output_tokens\":0,\"output_tokens_details\":{\"image_tokens\":0,\"text_tokens\":0},\"total_tokens\":0},\"web_search\":{\"num_requests\":0}},\"tools\":[],\"top_logprobs\":0,\"top_p\":0.98,\"truncation\":\"disabled\",\"usage\":{\"input_tokens\":40,\"input_tokens_details\":{\"cache_write_tokens\":0,\"cached_tokens\":0},\"output_tokens\":17,\"output_tokens_details\":{\"reasoning_tokens\":9},\"total_tokens\":57},\"user\":null,\"metadata\":{}},\"sequence_number\":11}"
}
]
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -169,7 +169,7 @@ describe("Amazon Bedrock Mantle provider", () => {
summary: [{ type: "summary_text", text: "Considering." }],
encrypted_content: "mantle-state",
},
{ role: "user", content: [{ type: "input_text", text: "Continue." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Continue." }] },
])
}),
)
@@ -22,7 +22,7 @@ it.effect("conversation lowering excludes generation settings and tool definitio
model: "fixture",
instructions: "Keep the context",
input: [
{ role: "user", content: [{ type: "input_text", text: "hello" }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "hello" }] },
{ type: "message", role: "assistant", status: "completed", content: [{ type: "output_text", text: "hi" }] },
],
})
@@ -32,7 +32,7 @@ testEffect(
if (new URL(request.url).pathname.endsWith("/compact")) {
expect(JSON.parse(text)).toEqual({
model: "overlaid",
input: [{ role: "user", content: [{ type: "input_text", text: "hello" }] }],
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "hello" }] }],
instructions: "request instructions",
previous_response_id: "resp_previous",
})
@@ -253,7 +253,7 @@ for (const overlay of [undefined, { service_tier: "priority", prompt_cache_key:
Effect.sync(() => {
expect(JSON.parse(text)).toEqual({
model: "fixture",
input: [{ role: "user", content: [{ type: "input_text", text: "hello" }] }],
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "hello" }] }],
service_tier: overlay?.service_tier ?? "flex",
prompt_cache_key: overlay?.prompt_cache_key ?? "affinity",
prompt_cache_retention: "24h",
@@ -394,7 +394,7 @@ for (const model of [
if (new URL(request.url).pathname.endsWith("/responses/compact")) {
expect(body).toEqual({
model: model.id,
input: [{ role: "user", content: [{ type: "input_text", text: "original" }] }],
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "original" }] }],
instructions: "system",
})
return respond(
@@ -407,7 +407,7 @@ for (const model of [
)
}
expect(new URL(request.url).pathname.endsWith("/responses")).toBe(true)
expect(body.input).toEqual([...output, { role: "user", content: [{ type: "input_text", text: "continue" }] }])
expect(body.input).toEqual([...output, { type: "message", role: "user", content: [{ type: "input_text", text: "continue" }] }])
return respond(sseEvents({ type: "response.completed", response: { id: "resp_1", output: [] } }), {
headers: { "content-type": "text/event-stream" },
})
@@ -66,7 +66,7 @@ for (const model of [
content: [{ type: "output_text", text: `Final ${index}` }],
},
]),
{ role: "user", status: "incomplete", content: [{ type: "input_text", text: "Continue" }] },
{ type: "message", role: "user", status: "incomplete", content: [{ type: "input_text", text: "Continue" }] },
])
}),
)
@@ -112,7 +112,7 @@ for (const model of [
status: "completed",
content: [{ type: "output_text", text: "The next step is" }],
},
{ role: "user", content: [{ type: "input_text", text: "Continue" }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Continue" }] },
])
}),
)
+21 -4
View File
@@ -248,7 +248,23 @@ describe("OpenAI Chat route", () => {
}),
)
it.effect("maps the request prompt cache key", () =>
it.effect("maps the request prompt cache key when the compatibility flag is set", () =>
Effect.gen(function* () {
const prepared = yield* compileRequest(
LLM.request({
model: OpenAIChat.route
.with({ endpoint: { baseURL: "https://api.compatible.test/v1" }, auth: Auth.bearer("test") })
.model({ id: "compatible-model", compatibility: { supportsPromptCacheKey: true } }),
prompt: "Hello",
promptCacheKey: "session_123",
}),
)
expect(prepared.body.prompt_cache_key).toBe("session_123")
}),
)
it.effect("omits the prompt cache key without the compatibility flag", () =>
Effect.gen(function* () {
const prepared = yield* compileRequest(
LLM.request({
@@ -261,7 +277,7 @@ describe("OpenAI Chat route", () => {
}),
)
expect(prepared.body.prompt_cache_key).toBe("session_123")
expect(prepared.body).not.toHaveProperty("prompt_cache_key")
}),
)
@@ -280,7 +296,7 @@ describe("OpenAI Chat route", () => {
}),
)
it.effect("maps the xAI Chat prompt cache key to conversation affinity", () =>
it.effect("maps the xAI Chat prompt cache key to conversation affinity header only", () =>
LLMClient.generate(
LLM.request({
model: XAI.configure({ apiKey: "test", baseURL: "https://api.x.ai/v1" }).chat("grok-4.5"),
@@ -294,7 +310,8 @@ describe("OpenAI Chat route", () => {
const web = yield* HttpClientRequest.toWeb(input.request).pipe(Effect.orDie)
expect(web.headers.get("x-grok-conv-id")).toBe("session_123")
const body = decodeJson(yield* Effect.promise(() => web.text()))
expect(ProviderShared.isRecord(body) ? body.prompt_cache_key : undefined).toBe("session_123")
// Chat uses the header; prompt_cache_key is Responses-only.
expect(ProviderShared.isRecord(body) ? body.prompt_cache_key : undefined).toBeUndefined()
return input.respond(sseEvents(deltaChunk({}, "stop")), {
headers: { "content-type": "text/event-stream" },
})
@@ -47,7 +47,7 @@ describe("Open Responses-compatible route", () => {
})
expect(prepared.body).toEqual({
model: "example-model",
input: [{ role: "user", content: [{ type: "input_text", text: "Say hello." }] }],
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "Say hello." }] }],
instructions: "You are concise.",
stream: true,
store: false,
@@ -89,8 +89,8 @@ describe("Open Responses-compatible route", () => {
expect(prepared.body.instructions).toBe("Initial instructions.")
expect(prepared.body.input).toEqual([
{ role: "user", content: [{ type: "input_text", text: "Before." }] },
{ role: "developer", content: "Operator update." },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Before." }] },
{ type: "message", role: "developer", content: "Operator update." },
{ type: "message", role: "assistant", status: "completed", content: [{ type: "output_text", text: "After." }] },
])
}),
@@ -111,8 +111,8 @@ describe("Open Responses-compatible route", () => {
)
expect(prepared.body.input).toEqual([
{ role: "user", content: [{ type: "input_text", text: "Before." }] },
{ role: "user", content: [{ type: "input_text", text: "After." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Before." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "After." }] },
])
}),
)
@@ -143,6 +143,7 @@ describe("Open Responses-compatible route", () => {
expect(prepared.body.input).toEqual([
{
type: "message",
role: "user",
content: [{ type: "input_file", filename: "input.pdf", file_data: pdf }],
},
@@ -360,9 +361,9 @@ describe("Open Responses-compatible route", () => {
expect(prepared.body.input).toEqual([
items[0],
{ role: "user", content: [{ type: "input_text", text: JSON.stringify(items[1]) }] },
{ role: "user", content: [{ type: "input_text", text: JSON.stringify(items[2]) }] },
{ role: "user", content: [{ type: "input_text", text: JSON.stringify(items[3]) }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: JSON.stringify(items[1]) }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: JSON.stringify(items[2]) }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: JSON.stringify(items[3]) }] },
])
}),
)
@@ -34,6 +34,13 @@ const observationFrame = (observation: ChannelObservation) => {
const terminal = (observation: ChannelObservation) => observation.type !== "frame"
// Whether the model emits an encrypted reasoning item ahead of its reply varies
// between recordings, so full-context assertions compare the conversation without it.
const conversation = (body: unknown) => {
const input = (body as { input: ReadonlyArray<{ type?: string }> }).input
return input.filter((item) => item.type !== "reasoning")
}
// This channel fixture supports sequential test traffic.
const makeChannel = Effect.gen(function* () {
const constructor = yield* Socket.WebSocketConstructor
@@ -167,14 +174,12 @@ describe("OpenAI Responses WebSocket recorded", () => {
expect(second.text).toBe("Beta.")
expect(channel.opens()).toBe(2)
expect(channel.sent[1]).not.toHaveProperty("previous_response_id")
expect(channel.sent[1]).toMatchObject({
instructions: "Follow the user's exact reply instruction.",
input: [
{ role: "user", content: [{ type: "input_text", text: "Reply exactly: Alpha." }] },
{ role: "assistant", status: "completed", content: [{ type: "output_text", text: "Alpha." }] },
{ role: "user", content: [{ type: "input_text", text: "Reply exactly: Beta." }] },
],
})
expect(channel.sent[1]).toMatchObject({ instructions: "Follow the user's exact reply instruction." })
expect(conversation(channel.sent[1])).toMatchObject([
{ type: "message", role: "user", content: [{ type: "input_text", text: "Reply exactly: Alpha." }] },
{ role: "assistant", status: "completed", content: [{ type: "output_text", text: "Alpha." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Reply exactly: Beta." }] },
])
}),
)
@@ -204,14 +209,12 @@ describe("OpenAI Responses WebSocket recorded", () => {
expect(channel.opens()).toBe(2)
expect(channel.sent[1]).toHaveProperty("previous_response_id", expect.any(String))
expect(channel.sent[2]).not.toHaveProperty("previous_response_id")
expect(channel.sent[2]).toMatchObject({
instructions: "Follow the user's exact reply instruction.",
input: [
{ role: "user", content: [{ type: "input_text", text: "Reply exactly: Ready." }] },
{ role: "assistant", status: "completed", content: [{ type: "output_text", text: "Ready." }] },
{ role: "user", content: [{ type: "input_text", text: "Reply exactly: Recovered." }] },
],
})
expect(channel.sent[2]).toMatchObject({ instructions: "Follow the user's exact reply instruction." })
expect(conversation(channel.sent[2])).toMatchObject([
{ type: "message", role: "user", content: [{ type: "input_text", text: "Reply exactly: Ready." }] },
{ role: "assistant", status: "completed", content: [{ type: "output_text", text: "Ready." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Reply exactly: Recovered." }] },
])
}),
)
})
@@ -1,5 +1,5 @@
import { describe, expect } from "bun:test"
import { ConfigProvider, Effect, Layer, Ref, Schema, Stream } from "effect"
import { ConfigProvider, Effect, Layer, Logger, Ref, Schema, Stream } from "effect"
import { Headers, HttpClientRequest } from "effect/unstable/http"
import {
LLM,
@@ -141,7 +141,7 @@ describe("OpenAI Responses route", () => {
expect(prepared.body).toEqual({
model: "gpt-4.1-mini",
input: [{ role: "user", content: [{ type: "input_text", text: "Say hello." }] }],
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "Say hello." }] }],
instructions: "You are concise.",
store: false,
include: ["reasoning.encrypted_content"],
@@ -409,8 +409,8 @@ describe("OpenAI Responses route", () => {
)
expect(prepared.body.input).toEqual([
{ role: "user", content: [{ type: "input_text", text: "Before." }] },
{ role: "developer", content: "Operator update." },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Before." }] },
{ type: "message", role: "developer", content: "Operator update." },
{ type: "message", role: "assistant", status: "completed", content: [{ type: "output_text", text: "After." }] },
])
}),
@@ -504,7 +504,7 @@ describe("OpenAI Responses route", () => {
expect(JSON.parse(sent[0])).toEqual({
type: "response.create",
model: "gpt-4.1-mini",
input: [{ role: "user", content: [{ type: "input_text", text: "Say hello." }] }],
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "Say hello." }] }],
store: false,
include: ["reasoning.encrypted_content"],
})
@@ -592,7 +592,7 @@ describe("OpenAI Responses route", () => {
type: "response.create",
model: "gpt-5.2",
store: false,
input: [{ role: "user", content: [{ type: "input_text", text: "Weather?" }] }],
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "Weather?" }] }],
}
const first = continuationDriver(firstRequest)
const firstCreate = yield* first.create(undefined)
@@ -642,7 +642,7 @@ describe("OpenAI Responses route", () => {
type: "response.create",
model: "gpt-5.2",
store: false,
input: [{ role: "user", content: [{ type: "input_text", text: "Weather?" }] }],
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "Weather?" }] }],
}
const first = continuationDriver(firstRequest)
const firstCreate = yield* first.create(undefined)
@@ -689,7 +689,7 @@ describe("OpenAI Responses route", () => {
it.effect("continues a promoted steer after assistant output with response-only text metadata", () =>
Effect.forEach([undefined, []], (output) =>
Effect.gen(function* () {
const firstInput = [{ role: "user", content: [{ type: "input_text", text: "First" }] }]
const firstInput = [{ type: "message", role: "user", content: [{ type: "input_text", text: "First" }] }]
const first = continuationDriver({ type: "response.create", model: "gpt-5.2", store: false, input: firstInput })
const create = yield* first.create(undefined)
yield* first.observe(
@@ -711,7 +711,7 @@ describe("OpenAI Responses route", () => {
ProviderShared.encodeJson({ type: "response.completed", response: { id: "resp_1", output } }),
),
)
const steer = { role: "user", content: [{ type: "input_text", text: "Actually, be brief" }] }
const steer = { type: "message", role: "user", content: [{ type: "input_text", text: "Actually, be brief" }] }
const next = continuationDriver({
type: "response.create",
model: "gpt-5.2",
@@ -732,7 +732,7 @@ describe("OpenAI Responses route", () => {
it.effect("continues streamed reasoning when completion re-encrypts the same item", () =>
Effect.gen(function* () {
const firstInput = [{ role: "user", content: [{ type: "input_text", text: "Think" }] }]
const firstInput = [{ type: "message", role: "user", content: [{ type: "input_text", text: "Think" }] }]
const request = { type: "response.create", model: "gpt-5.2", store: false, input: firstInput }
const reasoning = {
type: "reasoning",
@@ -760,7 +760,11 @@ describe("OpenAI Responses route", () => {
)
const next = continuationDriver({
...request,
input: [...firstInput, reasoning, { role: "user", content: [{ type: "input_text", text: "Continue" }] }],
input: [
...firstInput,
reasoning,
{ type: "message", role: "user", content: [{ type: "input_text", text: "Continue" }] },
],
})
const continued = yield* next.create(saved)
@@ -768,7 +772,7 @@ describe("OpenAI Responses route", () => {
expect(continued.mode).toBe("incremental")
expect(ProviderShared.decodeJson(continued.message)).toMatchObject({
previous_response_id: "resp_1",
input: [{ role: "user", content: [{ type: "input_text", text: "Continue" }] }],
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "Continue" }] }],
})
const edited = yield* continuationDriver({
...request,
@@ -785,7 +789,7 @@ describe("OpenAI Responses route", () => {
model: "gpt-5.2",
store: false,
metadata: { source: "one" },
input: [{ role: "user", content: [{ type: "input_text", text: "First" }] }],
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "First" }] }],
}
const first = continuationDriver(request)
const create = yield* first.create(undefined)
@@ -795,7 +799,10 @@ describe("OpenAI Responses route", () => {
ProviderShared.encodeJson({ type: "response.completed", response: { id: "resp_1" } }),
),
)
const appended = [...request.input, { role: "user", content: [{ type: "input_text", text: "Second" }] }]
const appended = [
...request.input,
{ type: "message", role: "user", content: [{ type: "input_text", text: "Second" }] },
]
const changes = [
{ ...request, model: "gpt-5.3", input: appended },
{ ...request, instructions: "Changed", input: appended },
@@ -804,7 +811,10 @@ describe("OpenAI Responses route", () => {
{ ...request, metadata: { source: "two" }, input: appended },
{
...request,
input: [{ role: "user", content: [{ type: "input_text", text: "Rewritten history" }] }, appended[1]],
input: [
{ type: "message", role: "user", content: [{ type: "input_text", text: "Rewritten history" }] },
appended[1],
],
},
]
@@ -888,7 +898,7 @@ describe("OpenAI Responses route", () => {
type: "response.create",
model: "gpt-5.2",
store: false,
input: [{ role: "user", content: [{ type: "input_text", text: "First" }] }],
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "First" }] }],
}
const first = continuationDriver(firstRequest, classifyingChannelDriver)
const saved = checkpoint(
@@ -900,7 +910,10 @@ describe("OpenAI Responses route", () => {
const second = continuationDriver(
{
...firstRequest,
input: [...firstRequest.input, { role: "user", content: [{ type: "input_text", text: "Second" }] }],
input: [
...firstRequest.input,
{ type: "message", role: "user", content: [{ type: "input_text", text: "Second" }] },
],
},
classifyingChannelDriver,
)
@@ -947,11 +960,14 @@ describe("OpenAI Responses route", () => {
model: "grok-4.6",
store: true,
instructions: "You are terse.",
input: [{ role: "user", content: [{ type: "input_text", text: "First" }] }],
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "First" }] }],
}
const secondRequest = {
...firstRequest,
input: [...firstRequest.input, { role: "user", content: [{ type: "input_text", text: "Second" }] }],
input: [
...firstRequest.input,
{ type: "message", role: "user", content: [{ type: "input_text", text: "Second" }] },
],
}
const saved = checkpoint(
yield* continuationDriver(firstRequest).observe(
@@ -971,7 +987,7 @@ describe("OpenAI Responses route", () => {
model: "grok-4.6",
store: true,
previous_response_id: "resp_1",
input: [{ role: "user", content: [{ type: "input_text", text: "Second" }] }],
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "Second" }] }],
})
// Declining the continuation sends the step in full and never sends a previous_response_id.
@@ -1082,7 +1098,9 @@ describe("OpenAI Responses route", () => {
)
const expected = {
input: [{ role: "user", content: [{ type: "input_text", text: "Say \uFFFDhello \u{1F600}." }] }],
input: [
{ type: "message", role: "user", content: [{ type: "input_text", text: "Say \uFFFDhello \u{1F600}." }] },
],
metadata: { source: "overlay\uFFFD" },
}
expect(JSON.parse(yield* Ref.get(message))).toMatchObject(expected)
@@ -1124,7 +1142,9 @@ describe("OpenAI Responses route", () => {
}),
)
it.effect("builds Azure WebSocket requests with v1 URLs and bearer auth", () =>
// Azure's WebSocket upgrade accepts the same credential header as HTTP: `api-key` for keys,
// `Authorization: Bearer` for Entra tokens. Rewriting a key into a bearer is rejected.
it.effect("builds Azure WebSocket requests with v1 URLs and the route's auth header", () =>
Effect.gen(function* () {
const deps = Layer.succeed(
RequestExecutor.Service,
@@ -1133,13 +1153,13 @@ describe("OpenAI Responses route", () => {
const cases = [
{
model: Azure.configure({ resourceName: "opencode-test", apiKey: "azure-key" }).responses("deployment"),
authorization: "Bearer azure-key",
headers: { "api-key": "azure-key", authorization: undefined },
},
{
model: Azure.configure({ resourceName: "opencode-test", auth: Auth.bearer("entra-token") }).responses(
"deployment",
),
authorization: "Bearer entra-token",
headers: { "api-key": undefined, authorization: "Bearer entra-token" },
},
]
@@ -1150,8 +1170,8 @@ describe("OpenAI Responses route", () => {
Effect.gen(function* () {
expect(exchange.connect.url).toBe("wss://opencode-test.openai.azure.com/openai/v1/responses")
expect(exchange.connect.rotateAfterMs).toBe(55 * 60 * 1000)
expect(exchange.connect.headers.authorization).toBe(item.authorization)
expect(exchange.connect.headers["api-key"]).toBeUndefined()
expect(exchange.connect.headers.authorization).toBe(item.headers.authorization)
expect(exchange.connect.headers["api-key"]).toBe(item.headers["api-key"])
expect(exchange.connect.headers["openai-beta"]).toBeUndefined()
expect(JSON.parse((yield* exchange.driver.create(undefined)).message)).toMatchObject({
type: "response.create",
@@ -1199,6 +1219,11 @@ describe("OpenAI Responses route", () => {
},
]
const warnings: string[] = []
const logger = Logger.make((entry) => {
if (entry.logLevel !== "Warn") return
warnings.push(String(Array.isArray(entry.message) ? entry.message[0] : entry.message))
})
yield* Effect.forEach(cases, (item) =>
LLMClient.generate(LLM.request({ model: item.model, prompt: "Say hello." }), {
webSocket: { execute: () => Effect.die("unexpected WebSocket request") },
@@ -1214,6 +1239,9 @@ describe("OpenAI Responses route", () => {
),
),
),
).pipe(Effect.provide(Logger.layer([logger])))
expect(warnings).toEqual(
cases.map(() => "Azure OpenAI Responses does not offer WebSocket for this endpoint; using HTTP"),
)
}),
)
@@ -1513,7 +1541,7 @@ describe("OpenAI Responses route", () => {
expect(prepared.body).toEqual({
model: "gpt-4.1-mini",
input: [
{ role: "user", content: [{ type: "input_text", text: "What is the weather?" }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "What is the weather?" }] },
{ type: "function_call", call_id: "call_1", name: "lookup", arguments: '{"query":"weather"}' },
{ type: "function_call_output", call_id: "call_1", output: '{"forecast":"sunny"}' },
],
@@ -1829,6 +1857,7 @@ describe("OpenAI Responses route", () => {
instructions: "You are concise. Continue from the provided history.",
input: [
{
type: "message",
role: "user",
content: [
{ type: "input_text", text: "What is shown here?" },
@@ -1841,11 +1870,16 @@ describe("OpenAI Responses route", () => {
summary: [{ type: "summary_text", text: "I inspected the previous turn." }],
},
{ role: "assistant", content: [{ type: "output_text", text: "It shows a small test image." }] },
{ role: "user", content: [{ type: "input_text", text: "Check the weather in Paris before continuing." }] },
{
type: "message",
role: "user",
content: [{ type: "input_text", text: "Check the weather in Paris before continuing." }],
},
{ type: "function_call", call_id: "call_weather_1", name: "get_weather", arguments: '{"city":"Paris"}' },
{ type: "function_call_output", call_id: "call_weather_1", output: '{"temperature":22}' },
{ role: "assistant", content: [{ type: "output_text", text: "Paris is 22 degrees." }] },
{
type: "message",
role: "user",
content: [{ type: "input_text", text: "Continue from this conversation in one short sentence." }],
},
@@ -3354,7 +3388,7 @@ describe("OpenAI Responses route", () => {
const body = yield* Effect.promise(() => web.json())
expect(body).toMatchObject({
input: [
{ role: "user", content: [{ type: "input_text", text: "What changed?" }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "What changed?" }] },
{
type: "reasoning",
id: "rs_1",
@@ -3362,7 +3396,7 @@ describe("OpenAI Responses route", () => {
summary: [{ type: "summary_text", text: "Checked the previous diff." }],
},
{ role: "assistant", content: [{ type: "output_text", text: "The parser changed." }] },
{ role: "user", content: [{ type: "input_text", text: "Summarize it." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Summarize it." }] },
],
})
return input.respond(
@@ -3487,7 +3521,7 @@ describe("OpenAI Responses route", () => {
expect(prepared.body.input).toEqual([
item,
{ role: "user", content: [{ type: "input_text", text: "Continue." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Continue." }] },
])
}),
)
@@ -3524,9 +3558,9 @@ describe("OpenAI Responses route", () => {
)
expect(prepared.body.input).toEqual([
{ role: "user", content: [{ type: "input_text", text: "Search." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Search." }] },
{ type: "web_search_call", id: "ws_1", status: "completed" },
{ role: "user", content: [{ type: "input_text", text: "Continue." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Continue." }] },
])
}),
)
@@ -3556,8 +3590,8 @@ describe("OpenAI Responses route", () => {
expect(prepared.body.input).toEqual([
items[0],
{ role: "user", content: [{ type: "input_text", text: JSON.stringify(items[1]) }] },
{ role: "user", content: [{ type: "input_text", text: JSON.stringify(items[2]) }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: JSON.stringify(items[1]) }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: JSON.stringify(items[2]) }] },
])
}),
)
@@ -3594,10 +3628,11 @@ describe("OpenAI Responses route", () => {
expect(prepared.body.input).toEqual([
{
type: "message",
role: "user",
content: [{ type: "input_text", text: '{"type":"web_search_call","id":"ws_1","status":"completed"}' }],
},
{ role: "user", content: [{ type: "input_text", text: "Continue." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Continue." }] },
])
}),
)
@@ -3624,6 +3659,7 @@ describe("OpenAI Responses route", () => {
expect(prepared.body.input).toEqual([
{
type: "message",
role: "user",
content: [{ type: "input_text", text: '{"type":"web_search_call","id":"ws_other","status":"completed"}' }],
},
@@ -3760,6 +3796,7 @@ describe("OpenAI Responses route", () => {
expect(prepared.body.input).toEqual([
{ type: "web_search_call", id: "ws_1", status: "completed" },
{
type: "message",
role: "user",
content: [{ type: "input_text", text: '{"type":"web_search_call","id":"bad ref","status":"completed"}' }],
},
@@ -3802,9 +3839,9 @@ describe("OpenAI Responses route", () => {
expect(prepared.body.store).toBe(false)
expect(prepared.body.input).toEqual([
{ role: "user", content: [{ type: "input_text", text: "Generate a black triangle." }] },
{ role: "user", content: [{ type: "input_image", image_url: "data:image/png;base64,AQID" }] },
{ role: "user", content: [{ type: "input_text", text: "Make it blue." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Generate a black triangle." }] },
{ type: "message", role: "user", content: [{ type: "input_image", image_url: "data:image/png;base64,AQID" }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Make it blue." }] },
])
}),
)
@@ -3841,7 +3878,7 @@ describe("OpenAI Responses route", () => {
)
expect(prepared.body.input).toEqual([
{ role: "user", content: [{ type: "input_image", image_url: "data:image/png;base64,AQID" }] },
{ type: "message", role: "user", content: [{ type: "input_image", image_url: "data:image/png;base64,AQID" }] },
])
}),
)
@@ -3913,7 +3950,7 @@ describe("OpenAI Responses route", () => {
expect(prepared.body).toMatchObject({
input: [
{ role: "user", content: [{ type: "input_text", text: "What changed?" }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "What changed?" }] },
{
type: "reasoning",
id: "rs_1",
@@ -3921,7 +3958,7 @@ describe("OpenAI Responses route", () => {
encrypted_content: null,
},
{ role: "assistant", content: [{ type: "output_text", text: "The parser changed." }] },
{ role: "user", content: [{ type: "input_text", text: "Summarize it." }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Summarize it." }] },
],
store: false,
})
@@ -4594,8 +4631,20 @@ describe("OpenAI Responses route", () => {
compileRequest(LLM.request({ model, messages: [response.message], providerOptions: { store } })),
)
expect(prepared.map((request) => request.body.input)).toEqual([
[{ role: "user", content: [{ type: "input_image", image_url: "data:image/png;base64,AQID" }] }],
[{ role: "user", content: [{ type: "input_image", image_url: "data:image/png;base64,AQID" }] }],
[
{
type: "message",
role: "user",
content: [{ type: "input_image", image_url: "data:image/png;base64,AQID" }],
},
],
[
{
type: "message",
role: "user",
content: [{ type: "input_image", image_url: "data:image/png;base64,AQID" }],
},
],
])
}),
)
@@ -4629,6 +4678,7 @@ describe("OpenAI Responses route", () => {
)
expect(prepared.body.input).toEqual([
{
type: "message",
role: "user",
content: [{ type: "input_text", text: '{"code":"search_failed","message":"Search unavailable"}' }],
},
@@ -4712,6 +4762,7 @@ describe("OpenAI Responses route", () => {
expect(prepared.body.input).toEqual([
{
type: "message",
role: "user",
content: [
{ type: "input_image", image_url: "data:image/png;base64,AAECAw==" },
@@ -4744,6 +4795,7 @@ describe("OpenAI Responses route", () => {
expect(prepared.body.input).toEqual([
{
type: "message",
role: "user",
content: [
{
@@ -4769,6 +4821,7 @@ describe("OpenAI Responses route", () => {
expect(prepared.body.input).toEqual([
{
type: "message",
role: "user",
content: [
{
@@ -4803,6 +4856,7 @@ describe("OpenAI Responses route", () => {
expect(prepared.body.input).toEqual([
{
type: "message",
role: "user",
content: [
{ type: "input_image", image_url: "https://example.com/image.png" },
@@ -193,7 +193,7 @@ describe("xAI Responses route", () => {
expect(prepared.body.input).toEqual([
items[0],
items[1],
{ role: "user", content: [{ type: "input_text", text: JSON.stringify(items[2]) }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: JSON.stringify(items[2]) }] },
])
}),
)
@@ -252,7 +252,7 @@ describe("xAI Responses route", () => {
store: true,
include: ["reasoning.encrypted_content"],
previous_response_id: "resp_1",
input: [{ role: "user", content: [{ type: "input_text", text: "Second" }] }],
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "Second" }] }],
})
// The connection cache only serves stored responses, so the default store: false never chains.
@@ -262,8 +262,8 @@ describe("xAI Responses route", () => {
instructions: "You are terse.",
store: false,
input: [
{ role: "user", content: [{ type: "input_text", text: "First" }] },
{ role: "user", content: [{ type: "input_text", text: "Second" }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "First" }] },
{ type: "message", role: "user", content: [{ type: "input_text", text: "Second" }] },
],
})
expect(JSON.parse(unstored.message).previous_response_id).toBeUndefined()
@@ -23,6 +23,7 @@ for (const custom of [false, true]) {
await page.goto(stressSessionHref(fixture.sourceID))
const trigger = page.getByRole("button", { name: "Session details", exact: true })
const summary = page.getByRole("dialog", { name: "Session details", exact: true })
await expect(page.locator('[data-component="composer-editor"]')).toBeEditable()
await expect(trigger).toBeEnabled()
await trigger.hover()
const tooltip = page.getByRole("tooltip")
@@ -278,7 +278,11 @@ async function installMotionProbe(page: Page) {
probe.resetAnchorOnMotion = false
}
probe.terminalAnchorGaps.push(anchorGap)
if (panelGap && terminalRegion.getBoundingClientRect().height > 1)
if (
panelGap &&
reviewRegion.getBoundingClientRect().height > 1 &&
terminalRegion.getBoundingClientRect().height > 1
)
probe.panelGaps.push(panelGap.getBoundingClientRect().height)
if (!review) return
probe.paintGaps.push({
+1 -2
View File
@@ -33,7 +33,7 @@ const processes: Array<ReturnType<typeof Bun.spawn>> = []
const errors: Array<Promise<string>> = []
let failure: unknown
try {
await fs.mkdir(path.join(root, ".opencode"))
await fs.mkdir(path.join(root, ".opencode", "plugins"), { recursive: true })
spawnService()
spawnService()
const registration = await waitForRegistration()
@@ -54,7 +54,6 @@ try {
if (tokenOpenApi.status !== 200) throw new Error("Compiled application rejected query authentication")
if ((await pluginIDs(info.url, headers)).includes("smoke")) throw new Error("Smoke plugin existed before creation")
const plugin = path.join(root, ".opencode", "plugins", "smoke.ts")
await fs.mkdir(path.dirname(plugin), { recursive: true })
await fs.writeFile(plugin, pluginSource())
await waitForPlugin(info.url, headers)
+10 -10
View File
@@ -206,12 +206,9 @@ export type ModelMaxTokensField = "max_completion_tokens" | "max_tokens"
export type ProviderCompaction = { mode: "local" } | { mode: "provider"; threshold?: number }
export type ModelCapabilities = {
tools: boolean
input: Array<string>
output: Array<string>
responsesWebsockets?: boolean
}
export type ProviderTransport = "http" | "websocket"
export type ModelCapabilities = { tools: boolean; input: Array<string>; output: Array<string> }
export type ModelVariant = {
id: string
@@ -1345,6 +1342,7 @@ export type ModelCompatibility = {
maxTokensField?: ModelMaxTokensField
requireFinishReason?: boolean
requireAssistantAfterTool?: boolean
supportsPromptCacheKey?: boolean
}
export type ProviderInfo = {
@@ -1355,7 +1353,7 @@ export type ProviderInfo = {
activation: "auto" | "enabled" | "disabled"
package: string
compaction?: ProviderCompaction
websocket?: boolean
transport?: ProviderTransport
settings?: { [x: string]: any }
headers?: { [x: string]: string }
body?: { [x: string]: any }
@@ -1818,7 +1816,7 @@ export type ModelInfo = {
compatibility?: ModelCompatibility
package?: string
compaction?: ProviderCompaction
websocket?: boolean
transport?: ProviderTransport
settings?: { [x: string]: any }
headers?: { [x: string]: string }
body?: { [x: string]: any }
@@ -2014,6 +2012,7 @@ export type ConfigEntry =
scope?: string
callback_port?: number
redirect_uri?: string
auth_server_metadata_url?: string
}
| false
disabled?: boolean
@@ -2049,7 +2048,7 @@ export type ConfigEntry =
providers?: {
[x: string]: {
compaction?: ProviderCompaction
websocket?: boolean
transport?: ProviderTransport
canonical?: string
name?: string
env?: Array<string>
@@ -2060,7 +2059,7 @@ export type ConfigEntry =
models?: {
[x: string]: {
compaction?: ProviderCompaction
websocket?: boolean
transport?: ProviderTransport
modelID?: string
family?: string
name?: string
@@ -4669,6 +4668,7 @@ export type McpAddInput = {
readonly scope?: string
readonly callback_port?: number
readonly redirect_uri?: string
readonly auth_server_metadata_url?: string
}
| false
readonly disabled?: boolean
+20
View File
@@ -97,6 +97,26 @@ input; `onToolCallEnd` observes settled outcomes and duration. Both hooks return
`Values.RegExp`, `Values.Map`, `Values.Set`, and `Values.Promise`. The interpreter recognizes these by class; a
program's `new URL(...)` is a `Values.URL` wrapping the host `URL`. `Values.isValue` narrows to the data-like kinds.
### `Extension.make` and `Web.make`
An extension is a set of named host functions installed as globals. Arguments arrive as copies, results return as
copies, and a function inside a result is callable the same way. Extension calls are not tool calls and are not shown
in the tool catalog, so hosts describe them in their own instructions.
`Web.make` is the built-in outbound HTTP extension. Nothing is reachable unless a host lists it:
```ts
const runtime = CodeMode.make({
extensions: [Web.make({ allow: ["https://api.example.com"], methods: ["GET", "POST"], maxBodyBytes: 1_048_576 })],
})
// program: const res = await fetch("https://api.example.com/users"); return await res.json()
```
`fetch(url, init?)` accepts `method`, `headers`, and a `string`, `Uint8Array`, or `URLSearchParams` body, and
resolves to `{ url, status, statusText, ok, redirected, headers: { get, has, entries }, text(), json(), bytes() }`.
Every redirect hop is checked against `allow`, bodies larger than `maxBodyBytes` are refused, and `timeoutMs` bounds
the whole request. `Web.signature` is the model-facing signature for host instructions.
### OpenAPI tools
`OpenAPI.fromSpec` converts an OpenAPI 3.x document into one tool per supported operation. Dotted `operationId` values
+68 -37
View File
@@ -19,15 +19,17 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262
TypeScript is transpiled first; the emitted JavaScript must still use the supported subset.
- [x] Top-level `await` and `return` through the program's implicit async-function scope.
- [x] Explicit `return`, final top-level expression as a REPL-style result, and `null` when no value is produced.
- [x] Program results use JSON-like boundaries, with `undefined` and non-finite numbers normalized to `null`. Tool
arguments follow JSON serialization semantics before their schema applies (see the tools section). Own
`__proto__` keys are dropped wherever a host object crosses to the host, so merging tool inputs or results
cannot replace a prototype; `JSON.stringify` still emits the key, like JS, since a string cannot pollute.
- [x] Values `JSON.stringify` would flatten to `{}` cross the host boundary in a useful form instead: a Set as an
array, a RegExp as `"/source/flags"`, a URLSearchParams as its query string. A Map still crosses as `{}`.
Functions, generators, promises, and extension handles are rejected with a hint. In-program `JSON.stringify`
keeps JS behavior for all of these.
- [x] Live Date, RegExp, Map, Set, URL, and URLSearchParams values inside CodeMode.
- [x] The host boundary is `JSON.stringify` plus a short table. The program result and tool arguments cross as
what `JSON.stringify` would serialize: `toJSON` is honored, functions and `undefined` properties vanish,
`undefined` array elements and non-finite numbers become `null`, a cyclic value throws the same `TypeError`,
and Map, RegExp, and generators serialize as `{}`. A bare `undefined` result is `null`.
Tool results come back the way `JSON.parse(JSON.stringify(result))` would. The table, where a value cannot
be JSON but what the program meant is clear: a promise is awaited (a rejection fails the program), a Set
crosses as an array, a URLSearchParams as its query string, an Error as `{ name, message, ...own }`, a
Uint8Array is rejected with a hint to encode as text, and own `__proto__` keys are dropped so merging tool
inputs or results cannot replace a prototype. In-program `JSON.stringify` keeps JS behavior except for the
Error form and a promise, which is a `TypeError` with an await hint rather than a silent `{}`.
- [x] Live Date, RegExp, Map, Set, URL, URLSearchParams, and Uint8Array values inside CodeMode.
- [x] Tool calls through the host-provided `tools` tree only.
- [x] The global `search(...)` built-in: synchronous tool discovery that counts as an admitted tool call and is
shadowable by program declarations like other globals.
@@ -190,7 +192,7 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262
- [x] Prefix and postfix `++` and `--`.
- [x] Plain, arithmetic, bitwise, and logical assignment operators.
- [x] Property deletion on plain data objects and arrays, including computed and optional forms; deleting an array index
creates a hole without changing its length. Deleting a non-configurable property (`length`, `lastIndex`) or
creates a hole without changing its length. Deleting a non-configurable property (`length`) or
assigning a read-only one (`Math.PI`, `fn.name`) throws a `TypeError`, as in strict mode.
- [ ] Operators, `switch` discriminants, template interpolation, and coercion helpers such as `String` and `isNaN`
applied to functions and namespaces; JavaScript coerces them, the interpreter rejects non-data operands.
@@ -231,8 +233,8 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262
callables that settle the promise exactly once (they may escape the executor and settle later); an executor
throw rejects unless the promise already settled, resolving with a promise or callable thenable adopts it, and
resolving with the promise itself rejects with a `TypeError`. Resolver callables work anywhere callbacks are
accepted, including `.then`/`.catch` handlers and collection callbacks, but remain opaque references that cannot
cross the data boundary.
accepted, including `.then`/`.catch` handlers and collection callbacks, and vanish at the data boundary like
any function.
- [x] Recursive assimilation of objects with an own callable `then` field across `Promise.resolve`, combinators,
constructors, reactions, `finally`, `await`, and async returns. Thenable methods run deferred, receive
first-call-wins resolve/reject functions, and ignore throws after settlement. Inherited/accessor `then` fields
@@ -240,11 +242,9 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262
- [x] Dotted tool names are canonicalized into namespace paths; a path can be both callable and a namespace, and the
last tool supplied for a canonical path wins.
- [x] Tool path segments may be named `constructor`, `prototype`, or `__proto__` because paths use inert Map keys.
- [x] Outbound tool arguments follow JSON serialization semantics, like `JSON.stringify`: object properties with
`undefined` values are dropped, `undefined` array elements and non-finite numbers become `null`, and sparse
arrays densify. Tools never receive `undefined` inside their input object, though a bare `tools.t(undefined)`
argument still reaches schema decoding as `undefined`. Program results keep the stricter
normalization where every `undefined` becomes `null`.
- [x] Outbound tool arguments are what `JSON.stringify` would serialize (see the boundary rule above). Tools never
receive `undefined` inside their input object, though a bare `tools.t(undefined)` argument still reaches schema
decoding as `undefined`.
- [ ] Tokenize and case-fold non-ASCII tool paths, descriptions, and queries for tool search.
## Objects and properties
@@ -394,7 +394,9 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262
`unicodeSets`, and `dotAll`.
- [x] Captures, named groups, match `.index` and `.input`, and stateful global matching.
- [x] Integration with supported String methods, including function replacers.
- [x] Writable `lastIndex`.
- [x] Writable `lastIndex`, shared by `exec`, `test`, and the String methods. It is a prototype accessor that stores
a number, so `re.lastIndex = "12"` reads back `12`, `delete` is a no-op, and `hasOwnProperty("lastIndex")` is
`false`.
- [x] Match `indices` metadata for the `d` flag, including named groups on `exec`, `match`, and `matchAll` results.
- [x] `RegExp.escape`.
@@ -424,39 +426,68 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262
`entries`, `toString`, and `size`.
- [x] URL values serialize to their href; URLSearchParams serialize to `{}`.
## Uint8Array
The only binary type. Bytes stay inside the program or cross to extensions as copies; the tool boundary rejects them
with a hint to encode as text first (`TextDecoder`, `toBase64`, `toHex`).
- [x] `new Uint8Array(length | array | iterable | Uint8Array)`, `Uint8Array.from`, `Uint8Array.of`, `fromBase64`,
and `fromHex`. Lengths are capped like arrays.
- [x] Index reads and writes with JS byte semantics: values wrap modulo 256, out-of-range writes are ignored, indexes
cannot be deleted. `length` is a prototype accessor, so `Object.keys` lists only indexes.
- [x] `at`, `slice`, `subarray` (a view on the same bytes), `set`, `fill`, `reverse`, `indexOf`, `lastIndexOf`,
`includes`, `join`, `toString`, `toBase64`, `toHex`, and materialized `keys`, `values`, and `entries` arrays.
- [x] Spread, destructuring, `for...of`, `yield*`, `Array.from`, and `new Set(bytes)`. `Array.isArray` is false.
- [x] String coercion joins with commas; `JSON.stringify` gives `{"0":1,...}`; `console.log` prints
`Uint8Array(n) [...]`.
- [ ] Callback methods (`forEach`, `map`, `filter`, `find`, `reduce`, ...); use `Array.from(bytes, fn)` meanwhile.
- [ ] `ArrayBuffer`, `DataView`, and other typed arrays.
## Web platform helpers
- [x] `atob` and `btoa` with forgiving-base64 decoding and WebIDL string conversion; invalid input throws a
`TypeError`, since there is no `DOMException`.
- [x] `crypto.randomUUID()`.
- [ ] `crypto.getRandomValues` and `crypto.subtle`, `TextEncoder`/`TextDecoder`, and `Blob`: these need a binary
value type, which the JSON-like data model does not have yet.
- [x] `crypto.randomUUID()` and `crypto.getRandomValues(uint8Array)`.
- [x] `TextEncoder` and `TextDecoder` for UTF-8 only: any other label is a `RangeError`. `TextDecoder` accepts the
`fatal` and `ignoreBOM` options; `decode` takes a Uint8Array or nothing.
- [ ] `crypto.subtle`, `Blob`, and `TextDecoder` streaming or non-UTF-8 encodings.
## Extensions
Host classes and functions a host opts in through `Extension.make({ name, globals })` and `CodeMode.make({ extensions })`.
Host functions a host opts in through `Extension.make({ name, globals })` and `CodeMode.make({ extensions })`.
Nothing is exposed unless a host provides it; extension calls are not tool calls.
- [x] Each global is a class or a function, exposed as-is: constructors with `new`, prototype methods, accessors,
and statics (including through an exposed subclass, so `new this()` works), plus inheritance
up to the nearest exposed ancestor. A global that shadows a built-in or another extension throws at `make`.
- [x] Instances of exposed classes stay on the host; the program holds a handle whose only members are the class's.
The same host instance is always the same handle within a run, so identity and `instanceof` hold. Handles
cannot cross the data boundary: returning, stringifying, throwing, or passing one to a tool fails.
- [x] Each global is a function, callable but not constructible, run with `this` undefined. A global that shadows
a built-in or another extension throws at `make`.
- [x] Every value crossing in either direction is converted, never shared: plain objects and arrays are copied,
`Date`, `RegExp`, `URL`, `URLSearchParams`, `Map`, and `Set` become fresh copies with their contents converted,
`Date`, `RegExp`, `URL`, `URLSearchParams`, `Map`, `Set`, and `Uint8Array` become fresh copies with their
contents converted (a host `ArrayBuffer` comes in as a `Uint8Array`; other typed arrays cannot come out),
errors cross as errors with their name and message, and a `__proto__` key is dropped. Functions, generators,
un-awaited promises, and symbols cannot be passed in; an instance of an unexposed class, a symbol, or a BigInt
cannot come out.
un-awaited promises, and symbols cannot be passed in; a class instance, a symbol, or a BigInt cannot come out.
- [x] A host function inside a result becomes a program function whose calls cross the same way, so a result can
carry methods (`res.json()`) whose host closures keep the host state. Diagnostics name it by its path
(`fetch.json`). Like any program function it vanishes at the data boundary.
- [x] A host `Promise` becomes a program promise. Whatever host code returns, resolves, throws, or rejects with
crosses the same way, so `catch (e)` receives a copy of the thrown value (an `Error` of the matching type, or
plain data). A getter must be synchronous.
- [x] A prototype member runs only with a handle of its own class as `this`; a detached call, a plain object, or a
handle of another class throws `TypeError: Illegal invocation`. Program edits to an exposed prototype affect
that run only. Data properties on a class or prototype are not exposed, since a program write would change the
host class itself; expose one through an accessor.
plain data).
- [ ] Program functions as arguments to extension code (callbacks such as `forEach`).
- [ ] Binary values (`Uint8Array`, `ArrayBuffer`) at the extension boundary; needs the binary value type above.
- [ ] Host classes. Stateful host objects are expressed as closures; a declared method table would be the next
step if `new X()` in a program is ever needed.
### Web
`Web.make({ allow, methods, maxBodyBytes, timeoutMs })` is an extension exposing `fetch`. It is unavailable unless a
host provides it.
- [x] `fetch(url, init?)` with `method`, `headers` (record or pairs), and a `string`, `Uint8Array`, or
`URLSearchParams` body. The response is a plain object: `url`, `status`, `statusText`, `ok`, `redirected`,
`headers.get/has/entries`, and `text()`, `json()`, `bytes()` over the fully read body.
- [x] Policy is enforced before any request: origins outside `allow` (or `"*"`), methods outside `methods` (default
`GET`, `HEAD`), non-http(s) URLs, and unsupported `init` keys (`signal`, `credentials`, ...) throw a
`TypeError` naming the problem. Redirects are followed by hand with each hop checked against `allow`, at most
five hops; 303 and 301/302-after-POST switch to GET like browsers. Bodies over `maxBodyBytes` throw a
`RangeError`; `timeoutMs` bounds the whole request.
- [ ] `Headers`, `Response`, and `Request` as runtime types; streaming bodies; `AbortSignal`; `FormData`; `Blob`.
## Errors and diagnostics
+2 -2
View File
@@ -37,7 +37,7 @@ export type ResolvedExecutionLimits = {
export type Options<Provided extends Record<string, unknown> = {}> = ToolRuntime.ToolCallHooks<Services<Provided>> & {
/** Explicit tools exposed to the program as `tools`. */
tools?: Provided & Tools<Services<Provided>>
/** Host classes and functions exposed as globals; see `Extension.make`. */
/** Host functions exposed as globals; see `Extension.make`. */
extensions?: ReadonlyArray<Extension>
/** Resource limits enforced on each execution. */
limits?: ExecutionLimits
@@ -148,6 +148,6 @@ export const make = <const Provided extends Record<string, unknown> = {}>(
}
return {
catalog: prepared.catalog,
execute: (code) => executeProgram(code, prepared, limits, options, (host) => extensionGlobals(host, extensions)),
execute: (code) => executeProgram(code, prepared, limits, options, (ctx) => extensionGlobals(ctx, extensions)),
}
}
+101 -215
View File
@@ -1,229 +1,115 @@
export * as Data from "./data.js"
import type { DiagnosticKind } from "./codemode.js"
import type { Prototypes } from "./interpreter/intrinsics.js"
import { Effect, type Schema } from "effect"
import type { Interpreter } from "./interpreter/interpreter.js"
import { MAX_VALUE_DEPTH } from "./interpreter/limits.js"
import { invalidData, rangeError, typeError } from "./interpreter/model.js"
import {
Arr,
Bytes,
Callable,
define,
entries,
ErrorObj,
get,
isWrapper,
parseArrayIndex,
ProgramArray,
ProgramDate,
ProgramError,
ProgramGenerator,
ProgramHandle,
ProgramMap,
ProgramObject,
ProgramPromise,
ProgramRegExp,
ProgramSet,
ProgramURL,
ProgramURLSearchParams,
keys,
Obj,
PromiseObj,
record,
SetObj,
URLSearchParamsObj,
} from "./interpreter/objects.js"
import { typeofValue } from "./interpreter/references.js"
export const MAX_VALUE_DEPTH = 32
export type Json = Schema.Json
export class ToolRuntimeError extends Error {
constructor(
readonly kind: Extract<
DiagnosticKind,
"UnknownTool" | "InvalidToolInput" | "InvalidToolOutput" | "InvalidDataValue" | "ToolCallLimitExceeded"
>,
message: string,
readonly suggestions: ReadonlyArray<string> = [],
) {
super(message)
this.name = "ToolRuntimeError"
}
type Replacer<R> = (args: Array<unknown>) => Effect.Effect<unknown, unknown, R>
/**
* What `JSON.stringify` would serialize for a program value, as host JSON: `toJSON` is honored, functions and
* `undefined` vanish, non-finite numbers become null, and everything else is copied. Two departures from JS
* so a mistake is not a silent `{}`: an Error serializes as `{ name, message, ...own }`, and a promise throws.
*/
export const toJson = <R>(ctx: Interpreter<R>, value: unknown, replacer?: Replacer<R>) =>
walk(ctx, value, replacer, false)
/**
* The host boundary: `toJson`, plus what a program most likely meant when a value cannot be JSON. A promise is
* awaited, a Set crosses as an array, a URLSearchParams as its query string, a Uint8Array asks to be encoded as
* text first, and a `__proto__` key is dropped so host code can never receive one.
*/
export const toBoundary = <R>(ctx: Interpreter<R>, value: unknown) => walk(ctx, value, undefined, true)
const walk = <R>(
ctx: Interpreter<R>,
value: unknown,
replacer: Replacer<R> | undefined,
boundary: boolean,
): Effect.Effect<Json | undefined, unknown, R> => {
const stack = new Set<object>()
const visit = (holder: Obj, key: string, depth: number): Effect.Effect<Json | undefined, unknown, R> =>
Effect.gen(function* () {
if (depth > MAX_VALUE_DEPTH) throw rangeError(`Value exceeds the maximum depth of ${MAX_VALUE_DEPTH}.`)
const raw = get(holder, key)
if (raw instanceof PromiseObj && !boundary) {
throw invalidData(
"JSON.stringify received an un-awaited Promise; await it first - e.g. `const result = await tools.ns.tool(...)`.",
)
}
const settled = raw instanceof PromiseObj ? yield* ctx.await(raw) : raw
const toJSON = settled instanceof Obj ? get(settled, "toJSON") : undefined
const own = toJSON instanceof Callable ? yield* ctx.call(toJSON, settled, [key]) : settled
const value = replacer === undefined ? own : yield* replacer([key, own])
if (value === undefined || typeofValue(value) === "function") return undefined
if (typeof value === "number") return Number.isFinite(value) ? value : null
if (value === null || typeof value === "string" || typeof value === "boolean") return value
if (!(value instanceof Obj)) return {}
if (boundary && value instanceof Bytes) {
throw invalidData(
"A Uint8Array cannot cross to the host; pass text instead, e.g. `new TextDecoder().decode(bytes)` or `bytes.toBase64()`.",
)
}
if (boundary && value instanceof URLSearchParamsObj) return value.params.toString()
const target = boundary && value instanceof SetObj ? new Arr(ctx.builtins.Array, [...value.set]) : value
if (stack.has(target)) throw typeError("Converting circular structure to JSON.")
stack.add(target)
if (target instanceof Arr) {
const items: Array<Json> = []
for (let index = 0; index < target.items.length; index += 1) {
items.push((yield* visit(target, String(index), depth + 1)) ?? null)
}
stack.delete(target)
return items
}
const copied: Record<string, Json> = {}
// Own data property regardless of the key, so "__proto__" never reaches the Object.prototype setter.
const put = (name: string, item: Json | undefined) => {
if (item !== undefined)
Object.defineProperty(copied, name, { value: item, enumerable: true, writable: true, configurable: true })
}
// Errors serialize as { name, message, ...own }: both may be inherited, and neither is enumerable in JS.
if (target instanceof ErrorObj) {
put("name", yield* visit(target, "name", depth + 1))
put("message", yield* visit(target, "message", depth + 1))
}
for (const name of keys(target)) {
if (boundary && name === "__proto__") continue
put(name, yield* visit(target, name, depth + 1))
}
stack.delete(target)
return copied
})
return visit(record(ctx.builtins.Object, { "": value }), "", 0)
}
/**
* Brings a host-produced value into the program: program values pass through, host Date, RegExp,
* Map, Set, URL, and URLSearchParams become their built-in wrappers, and host objects and arrays
* are copied.
*/
export const toProgram = (protos: Prototypes, value: unknown, label: string): unknown =>
copy(value, label, "program", 0, new Set(), protos)
/**
* Brings host data into the program: Date and URL become strings, other host collections become
* empty objects, and objects become program copies. Used for tool results and parsed JSON.
*/
export const fromData = (protos: Prototypes, value: unknown, label: string): unknown =>
copy(value, label, "data", 0, new Set(), protos)
/**
* Takes a program value out as plain JSON: runtime values serialize like `JSON.stringify` would,
* non-finite numbers become null, and array holes become null. `undefined` object properties are
* dropped ("json") or become null ("result", for program results where the consumer must never see
* undefined); a bare `undefined` follows the same rule.
*
* At the host boundary (tool arguments and program results) `__proto__` keys are dropped and values
* `JSON.stringify` would flatten to `{}` cross in a useful form instead: a Set as an array, a RegExp
* and URLSearchParams as their strings. `JSON.stringify` itself passes `boundary: false` to keep JS
* behavior.
*/
export const toData = (
value: unknown,
label: string,
undefinedAs: "json" | "result" = "json",
boundary = true,
): unknown => copy(value, label, undefinedAs, 0, new Set(), undefined, boundary)
// "program" and "data" build program objects; "json" and "result" build ordinary objects for the host.
type Mode = "program" | "data" | "json" | "result"
const copy = (
value: unknown,
label: string,
mode: Mode,
depth: number,
seen: Set<object>,
protos?: Prototypes,
boundary = true,
): unknown => {
const next = (item: unknown) => copy(item, label, mode, depth + 1, seen, protos, boundary)
if (depth > MAX_VALUE_DEPTH) {
throw new ToolRuntimeError("InvalidDataValue", `${label} exceeds the maximum value depth of ${MAX_VALUE_DEPTH}.`)
}
if (value === undefined) return mode === "result" ? null : undefined
if (typeof value === "number") return (mode === "json" || mode === "result") && !Number.isFinite(value) ? null : value
if (value === null || typeof value === "string" || typeof value === "boolean") return value
if (typeof value !== "object") {
throw new ToolRuntimeError("InvalidDataValue", `${label} must contain data only.`)
}
if (value instanceof ProgramPromise) {
throw new ToolRuntimeError(
"InvalidDataValue",
`${label} contains an un-awaited Promise; await tool calls (e.g. \`const result = await tools.ns.tool(...)\`) before using their results.`,
/** Host JSON as program values: objects and arrays are copied, primitives pass through. */
export const fromJson = <R>(ctx: Interpreter<R>, value: unknown): unknown => {
if (value === null || typeof value !== "object") return value
if (Array.isArray(value))
return new Arr(
ctx.builtins.Array,
value.map((item) => fromJson(ctx, item)),
)
}
if ((value instanceof Callable || value instanceof ProgramGenerator) && mode !== "program") {
throw new ToolRuntimeError("InvalidDataValue", `${label} must contain data only.`)
}
if (value instanceof ProgramHandle && mode !== "program") {
throw new ToolRuntimeError(
"InvalidDataValue",
`${label} contains a ${value.instance.constructor.name}, which only extension functions accept.`,
)
}
// Host-produced input never holds program objects; one arriving here would come back as a host object.
if (value instanceof ProgramObject && mode === "data") {
throw new ToolRuntimeError("InvalidDataValue", `${label} must be host data, not a program value.`)
}
if (protos !== undefined && mode === "program") {
if (value instanceof ProgramObject) return value
if (value instanceof Date) return new ProgramDate(protos.Date, value.getTime())
if (value instanceof RegExp) return new ProgramRegExp(protos.RegExp, value.source, value.flags)
if (value instanceof Map) {
const wrapped = new ProgramMap(protos.Map)
for (const [key, item] of value.entries()) wrapped.map.set(next(key), next(item))
return wrapped
}
if (value instanceof Set) {
const wrapped = new ProgramSet(protos.Set)
for (const item of value.values()) wrapped.set.add(next(item))
return wrapped
}
if (value instanceof URL) return new ProgramURL(protos.URL, protos.URLSearchParams, new URL(value.href))
if (value instanceof URLSearchParams)
return new ProgramURLSearchParams(protos.URLSearchParams, new URLSearchParams(value))
}
if (value instanceof ProgramDate) return Number.isFinite(value.time) ? new Date(value.time).toISOString() : null
if (value instanceof Date) return Number.isFinite(value.getTime()) ? value.toISOString() : null
if (value instanceof ProgramURL) return value.url.href
if (value instanceof URL) return value.href
if (boundary && protos === undefined) {
if (value instanceof ProgramRegExp) return String(value.regex)
if (value instanceof ProgramURLSearchParams) return value.params.toString()
if (value instanceof ProgramSet) {
if (seen.has(value)) throw new ToolRuntimeError("InvalidDataValue", `${label} contains a circular value.`)
seen.add(value)
const copied = Array.from(value.set, (item) => next(item) ?? null)
seen.delete(value)
return copied
}
}
// Remaining wrappers and their host counterparts serialize as empty objects, like JSON.stringify.
if (
isWrapper(value) ||
value instanceof RegExp ||
value instanceof Map ||
value instanceof Set ||
value instanceof URLSearchParams
) {
return protos !== undefined ? new ProgramObject(protos.Object) : {}
}
if (seen.has(value)) {
throw new ToolRuntimeError("InvalidDataValue", `${label} contains a circular value.`)
}
seen.add(value)
if (value instanceof ProgramArray) {
const copied = Array.from(value.items, (item) => next(item) ?? null)
seen.delete(value)
return copied
}
if (value instanceof ProgramObject) {
const copied: Record<string, unknown> = {}
// Errors serialize as { name, message, ...own }: both may be inherited, and neither is enumerable in JS.
if (value instanceof ProgramError) {
defineHost(copied, "name", next(get(value, "name")))
defineHost(copied, "message", next(get(value, "message")))
}
for (const [key, item] of entries(value)) {
if (boundary && key === "__proto__") continue
const copiedItem = next(item)
if (copiedItem === undefined && mode === "json") continue
defineHost(copied, key, copiedItem)
}
seen.delete(value)
return copied
}
if (Array.isArray(value)) {
if (protos !== undefined) {
const copied = new ProgramArray(protos.Array, value.map(next))
for (const [key, item] of Object.entries(value)) {
if (parseArrayIndex(key) === undefined) define(copied, key, next(item))
}
seen.delete(value)
return copied
}
const copied = Array.from(value, (item) => next(item) ?? null)
seen.delete(value)
return copied
}
const prototype = Object.getPrototypeOf(value)
if (prototype !== Object.prototype && prototype !== null) {
throw new ToolRuntimeError("InvalidDataValue", `${label} must contain plain objects only.`)
}
if (protos !== undefined) {
const copied = new ProgramObject(protos.Object)
for (const [key, item] of Object.entries(value)) define(copied, key, next(item))
seen.delete(value)
return copied
}
const copied: Record<string, unknown> = {}
for (const [key, item] of Object.entries(value)) {
if (boundary && key === "__proto__") continue
const copiedItem = next(item)
if (copiedItem === undefined && mode === "json") continue
defineHost(copied, key, copiedItem)
}
seen.delete(value)
const copied = new Obj(ctx.builtins.Object)
for (const [key, item] of Object.entries(value)) define(copied, key, fromJson(ctx, item))
return copied
}
// Own data property regardless of the target's prototype, so a "__proto__" key on a host object
// never reaches the Object.prototype setter.
const defineHost = (target: object, key: string, value: unknown): void => {
Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true })
}
+4 -5
View File
@@ -1,20 +1,19 @@
export * as Extension from "./extension.js"
/**
* Host classes and functions a program uses directly, like JavaScript. Values crossing in either direction are
* converted, never shared: plain data is copied, instances of the classes stay on the host behind program-side
* handles. Extension calls are not tool calls.
* Host functions a program calls directly as globals. Values crossing in either direction are converted, never
* shared: arguments come in as copies, results go out as copies, and a function inside a result is callable the
* same way. Extension calls are not tool calls.
*/
export type Extension = {
readonly name: string
/** Each value is a class or a function; everything on a class is exposed, including statics and accessors. */
readonly globals: Readonly<Record<string, Function>>
}
export const make = (options: Extension): Extension => {
for (const [name, value] of Object.entries(options.globals)) {
if (typeof value !== "function") {
throw new TypeError(`Extension "${options.name}" global "${name}" must be a class or a function.`)
throw new TypeError(`Extension "${options.name}" global "${name}" must be a function.`)
}
}
return { name: options.name, globals: { ...options.globals } }
+1
View File
@@ -2,6 +2,7 @@ export * as CodeMode from "./codemode.js"
export * as Extension from "./extension.js"
export * as Namespace from "./namespace.js"
export * as Tool from "./tool.js"
export * as Web from "./web.js"
export * as OpenAPI from "./openapi/index.js"
export { searchSignature, toolExpression } from "./codemode.js"
export { ToolError, toolError } from "./tool-error.js"
@@ -1,8 +1,8 @@
import { Effect, Exit } from "effect"
import { coerceToNumber, coerceToString } from "../stdlib/value.js"
import type { Prototypes } from "./intrinsics.js"
import type { Interpreter } from "./interpreter.js"
import { typeError } from "./model.js"
import { Callable, get, NativeFunction, ProgramDate, ProgramObject, ProgramPromise } from "./objects.js"
import { Callable, get, Native, DateObj, Obj } from "./objects.js"
import { typeofValue } from "./references.js"
export type IteratorCursor<R> = {
@@ -10,18 +10,6 @@ export type IteratorCursor<R> = {
readonly close: Effect.Effect<void, unknown, R>
}
/** Everything a native function needs from the realm: calling back into the program and its intrinsic objects. */
export type Runner<R> = {
readonly invokeCallable: (
callable: unknown,
thisValue: unknown,
args: Array<unknown>,
) => Effect.Effect<unknown, unknown, R>
readonly settlePromise: (promise: ProgramPromise) => Effect.Effect<unknown, unknown, never>
readonly syncIterator: (value: unknown) => Effect.Effect<IteratorCursor<R> | undefined, unknown, R>
readonly prototypes: Prototypes
}
export const preserveConsumerError = <A, R>(
cursor: IteratorCursor<R>,
effect: Effect.Effect<A, unknown, R>,
@@ -37,39 +25,39 @@ export const preserveConsumerError = <A, R>(
* default hint as "string", like their `Symbol.toPrimitive`.
*/
export const toPrimitive = <R>(
runner: Runner<R>,
ctx: Interpreter<R>,
value: unknown,
hint: "number" | "string" | "default",
): Effect.Effect<unknown, unknown, R> => {
if (!(value instanceof ProgramObject)) return Effect.succeed(value)
const asString = hint === "string" || (hint === "default" && value instanceof ProgramDate)
if (!(value instanceof Obj)) return Effect.succeed(value)
const asString = hint === "string" || (hint === "default" && value instanceof DateObj)
const order = asString ? ["toString", "valueOf"] : ["valueOf", "toString"]
return Effect.gen(function* () {
for (const method of order) {
const callable = get(value, method)
if (!(callable instanceof Callable)) continue
const result = yield* runner.invokeCallable(callable, value, [])
const result = yield* ctx.call(callable, value, [])
if (result === null || (typeof result !== "object" && typeof result !== "function")) return result
}
throw typeError("Cannot convert object to primitive value.")
})
}
export const toPrimitiveString = <R>(runner: Runner<R>, value: unknown) =>
Effect.map(toPrimitive(runner, value, "string"), coerceToString)
export const toPrimitiveString = <R>(ctx: Interpreter<R>, value: unknown) =>
Effect.map(toPrimitive(ctx, value, "string"), coerceToString)
export const toPrimitiveNumber = <R>(runner: Runner<R>, value: unknown) =>
Effect.map(toPrimitive(runner, value, "number"), coerceToNumber)
export const toPrimitiveNumber = <R>(ctx: Interpreter<R>, value: unknown) =>
Effect.map(toPrimitive(ctx, value, "number"), coerceToNumber)
// The single acceptance list for callbacks: collections, sort, string replacers,
// Array.from mappers, and promise reactions all admit exactly these callables.
// Admission means dispatchable, not necessarily invocable: new-requiring
// constructors pass the gate and throw a TypeError on call, like JS.
export const isSupportedCallback = (value: unknown): value is Callable =>
value instanceof Callable && !(value instanceof NativeFunction && !value.callback)
value instanceof Callable && !(value instanceof Native && !value.callback)
export const applyCollectionCallback = <R>(
runner: Runner<R>,
ctx: Interpreter<R>,
callback: unknown,
name: string,
): ((args: Array<unknown>) => Effect.Effect<unknown, unknown, R>) => {
@@ -81,5 +69,5 @@ export const applyCollectionCallback = <R>(
}
throw typeError(`${name} expects a function callback.`)
}
return (callbackArgs) => runner.invokeCallable(callback, undefined, callbackArgs)
return (callbackArgs) => ctx.call(callback, undefined, callbackArgs)
}
+29 -41
View File
@@ -1,22 +1,14 @@
import { Effect } from "effect"
import type { Diagnostic } from "../codemode.js"
import { ToolError } from "../tool-error.js"
import { toData, ToolRuntimeError } from "../data.js"
import { type AstNode, formatLocation, PendingThrow, ProgramThrow, sourceLocation, typeError } from "./model.js"
import { ToolRuntimeError } from "../tool-runtime.js"
import { type AstNode, formatLocation, PendingThrow, Throw, sourceLocation, typeError } from "./model.js"
import { containsRuntimeReference } from "./references.js"
import { createErrorValue, type ErrorType, isErrorType } from "./intrinsics.js"
import { constructor, methods, prototypeFrom, receiver } from "./native.js"
import {
type Callable,
define,
get,
hidden,
type NativeFunction,
ProgramArray,
ProgramError,
ProgramObject,
} from "./objects.js"
import { type Runner } from "./runner.js"
import { type Callable, define, get, hidden, type Native, Arr, ErrorObj, Obj } from "./objects.js"
import type { Interpreter } from "./interpreter.js"
import { formatValue } from "../stdlib/console.js"
import { coerceToString } from "../stdlib/value.js"
export const normalizeError = (error: unknown): Diagnostic => {
@@ -41,9 +33,9 @@ export const normalizeError = (error: unknown): Diagnostic => {
return { kind: "ToolFailure", message: error.message }
}
if (error instanceof ProgramThrow) {
if (error instanceof Throw) {
const value = error.value
if (value instanceof ProgramError) {
if (value instanceof ErrorObj) {
return value.host ? normalizeError(value.host) : { kind: "ExecutionFailure", message: errorToString(value) }
}
let message: string
@@ -53,11 +45,7 @@ export const normalizeError = (error: unknown): Diagnostic => {
} else if (typeof value === "string") {
message = value
} else {
try {
message = JSON.stringify(toData(value, "Thrown value")) ?? String(value)
} catch {
message = String(value)
}
message = formatValue(value)
}
return { kind: "ExecutionFailure", message: `Uncaught: ${message}` }
}
@@ -98,22 +86,22 @@ export const locate = (error: unknown, node?: AstNode): unknown => {
}
/** The program value a handler receives for a failure; one failure always yields the same value. */
export const materialize = <R>(runner: Runner<R>, thrown: unknown): unknown => {
if (thrown instanceof ProgramThrow) return thrown.value
const prototypes = runner.prototypes
export const materialize = <R>(ctx: Interpreter<R>, thrown: unknown): unknown => {
if (thrown instanceof Throw) return thrown.value
const builtins = ctx.builtins
if (thrown instanceof PendingThrow) {
if (thrown.value === undefined) {
thrown.value = createErrorValue(prototypes[thrown.type], thrown.message)
thrown.value = createErrorValue(builtins[thrown.type], thrown.message)
thrown.value.host = thrown
}
return thrown.value
}
const type = thrown instanceof Error && isErrorType(thrown.name) ? thrown.name : "Error"
return createErrorValue(prototypes[type], normalizeError(thrown).message)
return createErrorValue(builtins[type], normalizeError(thrown).message)
}
/** Error.prototype.toString: `name: message`, omitting whichever side is empty. */
const errorToString = (self: ProgramObject): string => {
const errorToString = (self: Obj): string => {
const name = get(self, "name")
const message = get(self, "message")
const shownName = name === undefined ? "Error" : coerceToString(name)
@@ -124,53 +112,53 @@ const errorToString = (self: ProgramObject): string => {
}
export const createAggregateErrorValue = <R>(
runner: Runner<R>,
ctx: Interpreter<R>,
errors: Array<unknown>,
message: string,
proto: ProgramObject = runner.prototypes.AggregateError,
proto: Obj = ctx.builtins.AggregateError,
) => {
const value = createErrorValue(proto, message)
define(value, "errors", new ProgramArray(runner.prototypes.Array, errors), { ...hidden })
define(value, "errors", new Arr(ctx.builtins.Array, errors), { ...hidden })
return value
}
const constructAggregateErrorValue = <R>(
runner: Runner<R>,
ctx: Interpreter<R>,
args: Array<unknown>,
proto: ProgramObject,
): Effect.Effect<ProgramError, unknown, R> =>
proto: Obj,
): Effect.Effect<ErrorObj, unknown, R> =>
Effect.gen(function* () {
const cursor = yield* runner.syncIterator(args[0])
const cursor = yield* ctx.iterate(args[0])
if (cursor === undefined) throw typeError("new AggregateError(...) expects a synchronous iterable of errors.")
const errors: Array<unknown> = []
while (true) {
const step = yield* cursor.next
if (step.done) {
return createAggregateErrorValue(runner, errors, args[1] === undefined ? "" : coerceToString(args[1]), proto)
return createAggregateErrorValue(ctx, errors, args[1] === undefined ? "" : coerceToString(args[1]), proto)
}
errors.push(step.value)
}
})
/** An error constructor such as `Error` or `TypeError`; callable with or without `new`, like JS. */
export const errorGlobal = <R>(type: ErrorType, runner: Runner<R>) => {
const protos = runner.prototypes
const prototype = protos[type]
export const errorGlobal = <R>(type: ErrorType, ctx: Interpreter<R>) => {
const builtins = ctx.builtins
const prototype = builtins[type]
const construct = (args: Array<unknown>, newTarget: Callable) => {
const proto = prototypeFrom(newTarget, prototype)
return type === "AggregateError"
? constructAggregateErrorValue(runner, args, proto)
? constructAggregateErrorValue(ctx, args, proto)
: Effect.sync(() => createErrorValue(proto, args[0] === undefined ? undefined : coerceToString(args[0])))
}
const ctor: NativeFunction<R> = constructor<R>(protos, prototype, {
const ctor: Native<R> = constructor<R>(builtins, prototype, {
name: type,
length: type === "AggregateError" ? 2 : 1,
call: (_, args) => construct(args, ctor),
construct,
})
if (type === "Error") {
methods(protos, prototype, [
["toString", 0, (thisValue) => errorToString(receiver(ProgramObject, thisValue, "Error.prototype.toString"))],
methods(builtins, prototype, [
["toString", 0, (thisValue) => errorToString(receiver(Obj, thisValue, "Error.prototype.toString"))],
])
}
return ctor
+14 -23
View File
@@ -4,21 +4,20 @@ import { Cause, Effect, Scope } from "effect"
// pass-through on workerd (the compiler is ~11 MiB and can't init there).
import { transpile } from "#transpile"
import type { DataValue, Diagnostic, ResolvedExecutionLimits, Result } from "../codemode.js"
import { toData } from "../data.js"
import { toBoundary } from "../data.js"
import { ToolRuntime } from "../tool-runtime.js"
import { normalizeError } from "./errors.js"
import { createPrototypes } from "./intrinsics.js"
import type { Host } from "./globals.js"
import { createBuiltins } from "./intrinsics.js"
import { PendingThrow } from "./model.js"
import { PromiseRuntime } from "./promises.js"
import { Runtime } from "./runtime.js"
import { Pending } from "./promises.js"
import { Interpreter } from "./interpreter.js"
export const executeProgram = <R>(
code: string,
prepared: ToolRuntime.Prepared<R>,
limits: ResolvedExecutionLimits,
hooks: ToolRuntime.ToolCallHooks<R>,
extraGlobals?: (host: Host<R>) => ReadonlyArray<readonly [string, unknown]>,
globals?: (ctx: Interpreter<R>) => ReadonlyArray<readonly [string, unknown]>,
): Effect.Effect<Result, never, R> => {
if (code.trim().length === 0) {
return Effect.succeed({
@@ -30,31 +29,23 @@ export const executeProgram = <R>(
// Allocate execution state inside suspension so reused Effects never share it.
return Effect.suspend(() => {
const prototypes = createPrototypes()
const tools = ToolRuntime.make(prepared, prototypes, limits.maxToolCalls, hooks)
const builtins = createBuiltins()
const tools = ToolRuntime.make(prepared, limits.maxToolCalls, hooks)
const logs: Array<string> = []
const logged = () => (logs.length > 0 ? { logs: [...logs] } : {})
// Set only after copy-out so timeouts cannot report invalid values as completed.
let returned: { value: DataValue; promises: PromiseRuntime<R> } | undefined
let returned: { value: DataValue; pending: Pending<R> } | undefined
const base = Effect.acquireUseRelease(
Scope.make("parallel"),
(scope) =>
Effect.gen(function* () {
const program = parseProgram(code)
const promises = new PromiseRuntime<R>(scope, prototypes.Promise)
const value = yield* new Runtime<R>(
tools.execute,
tools.search,
tools.keys,
promises,
prototypes,
logs,
extraGlobals,
).run(program)
const result = toData(value, "Execution result", "result") as DataValue
returned = { value: result, promises }
const warnings = yield* promises.interrupt()
const pending = new Pending<R>(scope, builtins.Promise)
const ctx = new Interpreter<R>({ tools, pending, builtins, logs, globals })
const result = (yield* toBoundary(ctx, yield* ctx.run(program))) ?? null
returned = { value: result, pending }
const warnings = yield* pending.interrupt()
return {
ok: true,
value: result,
@@ -91,7 +82,7 @@ export const executeProgram = <R>(
kind: "TimeoutExceeded",
message: `The program returned, but background work was still running at the ${timeoutMs}ms timeout and was interrupted. Await all started promises.`,
},
...returned.promises.diagnostics(),
...returned.pending.diagnostics(),
],
...logged(),
toolCalls: tools.calls,
+78 -214
View File
@@ -1,59 +1,42 @@
import { Effect } from "effect"
import { MAX_VALUE_DEPTH } from "../data.js"
import type { Extension } from "../extension.js"
import { coerceToString } from "../stdlib/value.js"
import type { Host } from "./globals.js"
import type { Interpreter } from "./interpreter.js"
import { createErrorValue, isErrorType } from "./intrinsics.js"
import { ProgramThrow, typeError } from "./model.js"
import { constructor, fn } from "./native.js"
import { MAX_VALUE_DEPTH } from "./limits.js"
import { Throw, typeError } from "./model.js"
import { fn } from "./native.js"
import {
Callable,
define,
defineAccessor,
entries,
get,
hidden,
type NativeFunction,
ProgramArray,
ProgramDate,
ProgramError,
ProgramGenerator,
ProgramHandle,
ProgramMap,
ProgramObject,
ProgramPromise,
ProgramRegExp,
ProgramSet,
ProgramURL,
ProgramURLSearchParams,
type Native,
Arr,
Bytes,
DateObj,
ErrorObj,
GeneratorObj,
MapObj,
Obj,
PromiseObj,
RegExpObj,
SetObj,
URLObj,
URLSearchParamsObj,
} from "./objects.js"
import { describeValue } from "./references.js"
type Class = Function & { readonly prototype: object }
const isClass = (value: unknown): value is Class =>
typeof value === "function" && typeof value.prototype === "object" && value.prototype !== null
// Own keys the native function already carries.
const ownFunctionKeys = new Set(["length", "name", "prototype"])
const ownPrototypeKeys = new Set(["constructor"])
/**
* The global bindings of one run's extensions. Everything crossing the boundary is converted: plain data is
* copied, built-in wrappers are copied, instances of exposed classes travel as handles, and a host Promise becomes
* a program promise. Prototypes, constructors, and handle identity are all per run.
* The global bindings of one run's extensions. Everything crossing the boundary is converted: plain data and
* built-in wrappers are copied, a host function becomes a program function whose calls cross the same way, and a
* host Promise becomes a program promise.
*/
export const extensionGlobals = <R>(
host: Host<R>,
ctx: Interpreter<R>,
extensions: ReadonlyArray<Extension>,
): ReadonlyArray<readonly [string, unknown]> => {
const protos = host.runner.prototypes
const classes = new Set(extensions.flatMap((extension) => Object.values(extension.globals)).filter(isClass))
// Host prototype object → this run's program prototype, so an instance wraps as its most-derived exposed class.
const prototypes = new Map<object, ProgramObject>()
const exposed = new Map<Class, { ctor: NativeFunction<R>; proto: ProgramObject }>()
const classOf = new Map<unknown, Class>()
const handles = new WeakMap<object, ProgramHandle>()
const builtins = ctx.builtins
const toHost = (value: unknown, label: string, depth = 0, seen = new Set<object>()): unknown => {
if (depth > MAX_VALUE_DEPTH) throw typeError(`${label} exceeds the maximum value depth of ${MAX_VALUE_DEPTH}.`)
@@ -61,32 +44,32 @@ export const extensionGlobals = <R>(
if (isPrimitive(value)) return value
throw typeError(`${label} contains ${describeValue(value)}, which cannot be passed to an extension.`)
}
if (value instanceof ProgramHandle) return value.instance
if (value instanceof ProgramDate) return new Date(value.time)
if (value instanceof ProgramRegExp) return new RegExp(value.regex.source, value.regex.flags)
if (value instanceof ProgramURL) return new URL(value.url.href)
if (value instanceof ProgramURLSearchParams) return new URLSearchParams(value.params)
if (value instanceof Bytes) return new Uint8Array(value.bytes)
if (value instanceof DateObj) return new Date(value.time)
if (value instanceof RegExpObj) return new RegExp(value.regex.source, value.regex.flags)
if (value instanceof URLObj) return new URL(value.url.href)
if (value instanceof URLSearchParamsObj) return new URLSearchParams(value.params)
const next = (item: unknown) => toHost(item, label, depth + 1, seen)
if (value instanceof ProgramMap) return new Map([...value.map].map(([key, item]) => [next(key), next(item)]))
if (value instanceof ProgramSet) return new Set([...value.set].map(next))
if (value instanceof MapObj) return new Map([...value.map].map(([key, item]) => [next(key), next(item)]))
if (value instanceof SetObj) return new Set([...value.set].map(next))
if (
!(value instanceof ProgramObject) ||
!(value instanceof Obj) ||
value instanceof Callable ||
value instanceof ProgramGenerator ||
value instanceof ProgramPromise
value instanceof GeneratorObj ||
value instanceof PromiseObj
) {
throw typeError(`${label} contains ${describeValue(value)}, which cannot be passed to an extension.`)
}
if (value instanceof ProgramError) {
if (value instanceof ErrorObj) {
const name = coerceToString(get(value, "name"))
const message = get(value, "message")
const text = message === undefined ? "" : coerceToString(message)
return name === "AggregateError" ? new AggregateError([], text) : new (hostErrors[name] ?? Error)(text)
return name === "AggregateError" ? new AggregateError([], text) : new (hostErrors.get(name) ?? Error)(text)
}
if (seen.has(value)) throw typeError(`${label} contains a circular value.`)
seen.add(value)
const copied =
value instanceof ProgramArray
value instanceof Arr
? value.items.map(next)
: Object.fromEntries(
entries(value)
@@ -100,46 +83,44 @@ export const extensionGlobals = <R>(
const fromHost = (value: unknown, label: string, depth = 0, seen = new Set<object>()): unknown => {
if (depth > MAX_VALUE_DEPTH) throw typeError(`${label} exceeds the maximum value depth of ${MAX_VALUE_DEPTH}.`)
if (isPrimitive(value)) return value
if (typeof value === "function") return wrap(value, label)
if (value !== null && typeof value === "object") {
const existing = handles.get(value)
if (existing !== undefined) return existing
const proto = handlePrototype(value)
if (proto !== undefined) {
const handle = new ProgramHandle(proto, value)
handles.set(value, handle)
return handle
}
if (value instanceof Date) return new ProgramDate(protos.Date, value.getTime())
if (value instanceof RegExp) return new ProgramRegExp(protos.RegExp, value.source, value.flags)
if (value instanceof Date) return new DateObj(builtins.Date, value.getTime())
if (value instanceof RegExp) return new RegExpObj(builtins.RegExp, value.source, value.flags)
if (value instanceof Uint8Array) return new Bytes(builtins.Uint8Array, new Uint8Array(value))
if (value instanceof ArrayBuffer) return new Bytes(builtins.Uint8Array, new Uint8Array(value.slice(0)))
if (value instanceof Error) {
return createErrorValue(protos[isErrorType(value.name) ? value.name : "Error"], value.message)
return createErrorValue(builtins[isErrorType(value.name) ? value.name : "Error"], value.message)
}
if (value instanceof URL) return new ProgramURL(protos.URL, protos.URLSearchParams, new URL(value.href))
if (value instanceof URL) return new URLObj(builtins.URL, builtins.URLSearchParams, new URL(value.href))
if (value instanceof URLSearchParams) {
return new ProgramURLSearchParams(protos.URLSearchParams, new URLSearchParams(value))
return new URLSearchParamsObj(builtins.URLSearchParams, new URLSearchParams(value))
}
const next = (item: unknown) => fromHost(item, label, depth + 1, seen)
const next = (item: unknown, path: string) => fromHost(item, path, depth + 1, seen)
if (value instanceof Map) {
const wrapped = new ProgramMap(protos.Map)
for (const [key, item] of value) wrapped.map.set(next(key), next(item))
const wrapped = new MapObj(builtins.Map)
for (const [key, item] of value) wrapped.map.set(next(key, label), next(item, label))
return wrapped
}
if (value instanceof Set) {
const wrapped = new ProgramSet(protos.Set)
for (const item of value) wrapped.set.add(next(item))
const wrapped = new SetObj(builtins.Set)
for (const item of value) wrapped.set.add(next(item, label))
return wrapped
}
if (seen.has(value)) throw typeError(`${label} produced a circular value.`)
seen.add(value)
if (Array.isArray(value)) {
const copied = new ProgramArray(protos.Array, value.map(next))
const copied = new Arr(
builtins.Array,
value.map((item, index) => next(item, `${label}[${index}]`)),
)
seen.delete(value)
return copied
}
const prototype = Object.getPrototypeOf(value)
if (prototype === Object.prototype || prototype === null) {
const copied = new ProgramObject(protos.Object)
for (const [key, item] of Object.entries(value)) define(copied, key, next(item))
const copied = new Obj(builtins.Object)
for (const [key, item] of Object.entries(value)) define(copied, key, next(item, `${label}.${key}`))
seen.delete(value)
return copied
}
@@ -147,153 +128,37 @@ export const extensionGlobals = <R>(
throw typeError(`${label} produced ${describeHost(value)}, which the program cannot hold.`)
}
const handlePrototype = (instance: object): ProgramObject | undefined => {
for (let level = Object.getPrototypeOf(instance); level !== null; level = Object.getPrototypeOf(level)) {
const proto = prototypes.get(level)
if (proto !== undefined) return proto
}
return undefined
}
// Runs host code with already-converted inputs. Whatever it returns, resolves, throws, or rejects with crosses
// the same way, so the program catches what the author threw.
const invoke = (run: () => unknown, label: string): Effect.Effect<unknown, unknown, R> => {
const thrown = (reason: unknown) => new ProgramThrow(fromHost(reason, label))
let result: unknown
try {
result = run()
} catch (reason) {
return Effect.fail(thrown(reason))
}
if (!(result instanceof Promise)) return Effect.succeed(fromHost(result, label))
return host.promises.create(
Effect.map(Effect.tryPromise({ try: () => result, catch: thrown }), (settled) => fromHost(settled, label)),
)
}
const args = (values: Array<unknown>, label: string): Array<unknown> =>
values.map((value, index) => toHost(value, `Argument ${index + 1} to ${label}`))
// Own members of each level from `from` up to (excluding) `root`, child first, as JS resolves them.
const members = (
target: ProgramObject,
from: object,
root: object,
skip: ReadonlySet<string>,
label: string,
receiver: (thisValue: unknown, member: string) => unknown,
): void => {
for (let level: object | null = from; level !== null && level !== root; level = Object.getPrototypeOf(level)) {
for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(level))) {
if (skip.has(key) || target.props.has(key)) continue
const name = `${label}.${key}`
if (typeof descriptor.value === "function") {
const method: Function = descriptor.value
const impl = (thisValue: unknown, values: Array<unknown>) => {
const self = receiver(thisValue, name)
const converted = args(values, name)
return invoke(() => method.apply(self, converted), name)
}
define(target, key, fn<R>(protos, key, method.length, impl), hidden)
continue
}
// Data properties stay host-side: a program write to one would change the host class itself.
if ("value" in descriptor) continue
const get = descriptor.get
const set = descriptor.set
defineAccessor(
target,
key,
get === undefined
? undefined
: (thisValue) => {
const value = get.call(receiver(thisValue, name))
if (value instanceof Promise)
throw typeError(`${name} returned a Promise; a getter must be synchronous.`)
return fromHost(value, name)
},
set === undefined
? undefined
: (thisValue, value) => {
set.call(receiver(thisValue, name), toHost(value, `${name} value`))
},
)
// A host function as a program function: arguments cross in, and whatever it returns, resolves, throws, or
// rejects with crosses out, so the program catches what the author threw.
const wrap = (value: Function, label: string): Native<R> =>
fn<R>(builtins, value.name, value.length, (_, values) => {
const converted = values.map((item, index) => toHost(item, `Argument ${index + 1} to ${label}`))
const thrown = (reason: unknown) => new Throw(fromHost(reason, label))
let result: unknown
try {
result = value.apply(undefined, converted)
} catch (reason) {
return Effect.fail(thrown(reason))
}
}
}
const expose = (cls: Class): { ctor: NativeFunction<R>; proto: ProgramObject } => {
const existing = exposed.get(cls)
if (existing !== undefined) return existing
const ancestor = exposedAncestor(cls)
const base = ancestor === undefined ? undefined : expose(ancestor)
const proto = new ProgramObject(base === undefined ? protos.Object : base.proto)
prototypes.set(cls.prototype, proto)
const name = cls.name
const ctor = constructor<R>(protos, proto, {
name,
length: cls.length,
call: (_, values) => {
const converted = args(values, name)
return invoke(() => cls.apply(undefined, converted), name)
},
construct: (values) => {
const label = `new ${name}`
const construct = cls as new (...values: Array<unknown>) => object
const converted = args(values, label)
return invoke(() => new construct(...converted), label)
},
if (!(result instanceof Promise)) return fromHost(result, label)
return ctx.pending.create(
Effect.map(Effect.tryPromise({ try: () => result, catch: thrown }), (settled) => fromHost(settled, label)),
)
})
if (base !== undefined) ctor.proto = base.ctor
const entry = { ctor, proto }
exposed.set(cls, entry)
classOf.set(ctor, cls)
// A static called through an exposed subclass sees that subclass as `this`, like JS.
members(ctor, cls, ancestor ?? Function.prototype, ownFunctionKeys, name, (thisValue) => {
const called = classOf.get(thisValue)
return called !== undefined && (called === cls || called.prototype instanceof cls) ? called : cls
})
members(
proto,
cls.prototype,
ancestor?.prototype ?? Object.prototype,
ownPrototypeKeys,
`${name}.prototype`,
(thisValue, member) => {
if (thisValue instanceof ProgramHandle && thisValue.instance instanceof cls) return thisValue.instance
throw typeError(`Illegal invocation: ${member} called on ${describeValue(thisValue)}.`)
},
)
return entry
}
const exposedAncestor = (cls: Class): Class | undefined => {
for (let level = Object.getPrototypeOf(cls); isClass(level); level = Object.getPrototypeOf(level)) {
if (classes.has(level)) return level
}
return undefined
}
return extensions.flatMap((extension) =>
Object.entries(extension.globals).map(([name, value]) => {
if (isClass(value)) return [name, expose(value).ctor] as const
const impl = (_: unknown, values: Array<unknown>) => {
const converted = args(values, name)
return invoke(() => value.apply(undefined, converted), name)
}
return [name, fn<R>(protos, name, value.length, impl)] as const
}),
Object.entries(extension.globals).map(([name, value]) => [name, wrap(value, name)] as const),
)
}
const hostErrors: Record<string, ErrorConstructor | undefined> = {
TypeError,
RangeError,
SyntaxError,
ReferenceError,
EvalError,
URIError,
}
const hostErrors = new Map<string, ErrorConstructor>([
["TypeError", TypeError],
["RangeError", RangeError],
["SyntaxError", SyntaxError],
["ReferenceError", ReferenceError],
["EvalError", EvalError],
["URIError", URIError],
])
// The primitives the interpreter operates on; symbols and BigInts are not among them.
const isPrimitive = (value: unknown): boolean =>
@@ -304,7 +169,6 @@ const isPrimitive = (value: unknown): boolean =>
typeof value === "boolean"
const describeHost = (value: unknown): string => {
if (typeof value === "function") return "a function"
if (typeof value !== "object" || value === null) return `a ${typeof value}`
const name = (value as { constructor?: { name?: string } }).constructor?.name
return name === undefined || name === "" ? "an object" : `a ${name}`
+10 -11
View File
@@ -1,30 +1,29 @@
import { Effect } from "effect"
import { fn, type Method, methods, receiver } from "./native.js"
import { AsyncIteratorSymbol, type GeneratorRequestKind, IteratorSymbol } from "./model.js"
import { define, hidden, ProgramGenerator } from "./objects.js"
import type { PromiseRuntime } from "./promises.js"
import type { Runner } from "./runner.js"
import { define, hidden, GeneratorObj } from "./objects.js"
import type { Interpreter } from "./interpreter.js"
/** `next`/`return`/`throw` on the generator prototypes; async generators answer with promises. */
export const generatorGlobals = <R>(runner: Runner<R>, promises: PromiseRuntime<R>): void => {
const protos = runner.prototypes
export const generatorGlobals = <R>(ctx: Interpreter<R>): void => {
const builtins = ctx.builtins
const install = (asynchronous: boolean) => {
const proto = asynchronous ? protos.AsyncGenerator : protos.Generator
const proto = asynchronous ? builtins.AsyncGenerator : builtins.Generator
const label = asynchronous ? "AsyncGenerator" : "Generator"
const request = (kind: GeneratorRequestKind): Method => [
kind,
1,
(thisValue: unknown, args: Array<unknown>) => {
const generator = receiver(ProgramGenerator, thisValue, `${label}.prototype.${kind}`)
const generator = receiver(GeneratorObj, thisValue, `${label}.prototype.${kind}`)
const requested = generator.request(kind, args[0]) as Effect.Effect<unknown, unknown, R>
return generator.asynchronous ? promises.create(requested) : requested
return generator.asynchronous ? ctx.pending.create(requested) : requested
},
]
methods(protos, proto, [request("next"), request("return"), request("throw")])
methods(builtins, proto, [request("next"), request("return"), request("throw")])
define(
asynchronous ? protos.AsyncIterator : protos.Iterator,
asynchronous ? builtins.AsyncIterator : builtins.Iterator,
asynchronous ? AsyncIteratorSymbol : IteratorSymbol,
fn(protos, asynchronous ? "[Symbol.asyncIterator]" : "[Symbol.iterator]", 0, (thisValue) => thisValue),
fn(builtins, asynchronous ? "[Symbol.asyncIterator]" : "[Symbol.iterator]", 0, (thisValue) => thisValue),
hidden,
)
}
+45 -50
View File
@@ -1,5 +1,6 @@
import { Effect } from "effect"
import { arrayGlobal } from "../stdlib/array.js"
import { textDecoderGlobal, textEncoderGlobal, uint8ArrayGlobal } from "../stdlib/bytes.js"
import { mapGlobal, setGlobal } from "../stdlib/collections.js"
import { consoleGlobal } from "../stdlib/console.js"
import { dateGlobal } from "../stdlib/date.js"
@@ -18,25 +19,16 @@ import { errorTypes } from "./intrinsics.js"
import { constants, constructor, native } from "./native.js"
import { AsyncIteratorSymbol, IteratorSymbol, typeError } from "./model.js"
import { generatorGlobals } from "./generators.js"
import { promiseGlobal, type PromiseRuntime } from "./promises.js"
import type { Runner } from "./runner.js"
/** What the built-in globals need from the interpreter that owns them. */
export type Host<R> = {
readonly runner: Runner<R>
readonly promises: PromiseRuntime<R>
readonly search: (args: Array<unknown>) => Effect.Effect<unknown, unknown, R>
readonly toolKeys: (path: ReadonlyArray<string>) => ReadonlyArray<string>
readonly logs: Array<string>
}
import { promiseGlobal } from "./promises.js"
import type { Interpreter } from "./interpreter.js"
// Function.prototype.constructor exists so `fn.constructor === Function` holds; dynamic code is unsupported.
const functionGlobal = <R>(runner: Runner<R>) => {
const functionGlobal = <R>(ctx: Interpreter<R>) => {
const reject = () =>
Effect.sync(() => {
throw typeError("The Function constructor is not supported; write the function inline.")
})
return constructor<R>(runner.prototypes, runner.prototypes.Function, {
return constructor<R>(ctx.builtins, ctx.builtins.Function, {
name: "Function",
length: 1,
call: reject,
@@ -44,8 +36,8 @@ const functionGlobal = <R>(runner: Runner<R>) => {
})
}
const symbolGlobal = <R>(runner: Runner<R>) => {
const symbol = native<R>(runner.prototypes, {
const symbolGlobal = <R>(ctx: Interpreter<R>) => {
const symbol = native<R>(ctx.builtins, {
name: "Symbol",
call: () =>
Effect.sync(() => {
@@ -57,52 +49,55 @@ const symbolGlobal = <R>(runner: Runner<R>) => {
return symbol
}
type Factory = <R>(host: Host<R>) => unknown
type Factory = <R>(ctx: Interpreter<R>) => unknown
// A table rather than a list so the names are known before any runtime exists.
const table: Record<string, Factory> = {
tools: () => new ToolReference([]),
search: (host) =>
native(host.runner.prototypes, { name: "search", call: (_, args) => host.search(args), callback: false }),
search: (ctx) =>
native(ctx.builtins, { name: "search", call: (_, args) => ctx.tool(ctx.tools.search, args), callback: false }),
undefined: () => undefined,
NaN: () => NaN,
Infinity: () => Infinity,
Object: (host) => objectGlobal(host.runner, host.toolKeys),
Function: (host) => functionGlobal(host.runner),
Array: (host) => arrayGlobal(host.runner),
Math: (host) => mathGlobal(host.runner),
JSON: (host) => jsonGlobal(host.runner),
console: (host) => consoleGlobal(host.runner, host.logs),
Promise: (host) => promiseGlobal(host.runner, host.promises),
Symbol: (host) => symbolGlobal(host.runner),
Number: (host) => numberGlobal(host.runner),
String: (host) => stringGlobal(host.runner),
Boolean: (host) => booleanGlobal(host.runner),
parseInt: (host) => coercion(host.runner, "parseInt", 2),
parseFloat: (host) => coercion(host.runner, "parseFloat"),
isFinite: (host) => coercion(host.runner, "isFinite"),
isNaN: (host) => coercion(host.runner, "isNaN"),
Date: (host) => dateGlobal(host.runner),
RegExp: (host) => regexpGlobal(host.runner),
Map: (host) => mapGlobal(host.runner),
Set: (host) => setGlobal(host.runner),
URL: (host) => urlGlobal(host.runner),
URLSearchParams: (host) => urlSearchParamsGlobal(host.runner),
encodeURI: (host) => uriGlobal(host.runner, "encodeURI"),
encodeURIComponent: (host) => uriGlobal(host.runner, "encodeURIComponent"),
decodeURI: (host) => uriGlobal(host.runner, "decodeURI"),
decodeURIComponent: (host) => uriGlobal(host.runner, "decodeURIComponent"),
atob: (host) => base64Global(host.runner, "atob"),
btoa: (host) => base64Global(host.runner, "btoa"),
crypto: (host) => cryptoGlobal(host.runner),
...Object.fromEntries(errorTypes.map((type) => [type, <R>(host: Host<R>) => errorGlobal(type, host.runner)])),
Object: (ctx) => objectGlobal(ctx),
Function: (ctx) => functionGlobal(ctx),
Array: (ctx) => arrayGlobal(ctx),
Math: (ctx) => mathGlobal(ctx),
JSON: (ctx) => jsonGlobal(ctx),
console: (ctx) => consoleGlobal(ctx),
Promise: (ctx) => promiseGlobal(ctx),
Symbol: (ctx) => symbolGlobal(ctx),
Number: (ctx) => numberGlobal(ctx),
String: (ctx) => stringGlobal(ctx),
Boolean: (ctx) => booleanGlobal(ctx),
parseInt: (ctx) => coercion(ctx, "parseInt", 2),
parseFloat: (ctx) => coercion(ctx, "parseFloat"),
isFinite: (ctx) => coercion(ctx, "isFinite"),
isNaN: (ctx) => coercion(ctx, "isNaN"),
Date: (ctx) => dateGlobal(ctx),
RegExp: (ctx) => regexpGlobal(ctx),
Map: (ctx) => mapGlobal(ctx),
Set: (ctx) => setGlobal(ctx),
URL: (ctx) => urlGlobal(ctx),
URLSearchParams: (ctx) => urlSearchParamsGlobal(ctx),
Uint8Array: (ctx) => uint8ArrayGlobal(ctx),
TextEncoder: (ctx) => textEncoderGlobal(ctx),
TextDecoder: (ctx) => textDecoderGlobal(ctx),
encodeURI: (ctx) => uriGlobal(ctx, "encodeURI"),
encodeURIComponent: (ctx) => uriGlobal(ctx, "encodeURIComponent"),
decodeURI: (ctx) => uriGlobal(ctx, "decodeURI"),
decodeURIComponent: (ctx) => uriGlobal(ctx, "decodeURIComponent"),
atob: (ctx) => base64Global(ctx, "atob"),
btoa: (ctx) => base64Global(ctx, "btoa"),
crypto: (ctx) => cryptoGlobal(ctx),
...Object.fromEntries(errorTypes.map((type) => [type, <R>(ctx: Interpreter<R>) => errorGlobal(type, ctx)])),
}
/** Names bound in every program before extensions apply. */
export const globalNames: ReadonlySet<string> = new Set(Object.keys(table))
/** The immutable global bindings of every program, in declaration order. */
export const globals = <R>(host: Host<R>): ReadonlyArray<readonly [string, unknown]> => {
generatorGlobals(host.runner, host.promises)
return Object.entries(table).map(([name, factory]) => [name, factory(host)] as const)
export const globals = <R>(ctx: Interpreter<R>): ReadonlyArray<readonly [string, unknown]> => {
generatorGlobals(ctx)
return Object.entries(table).map(([name, factory]) => [name, factory(ctx)] as const)
}
@@ -42,8 +42,8 @@ import type {
YieldExpression,
} from "acorn"
import { Cause, Deferred, Effect, Exit } from "effect"
import { toProgram } from "../data.js"
import { ToolReference } from "../tool-runtime.js"
import { fromJson, type Json, toBoundary } from "../data.js"
import { ToolReference, type ToolRuntime } from "../tool-runtime.js"
import {
type AstNode,
AsyncIteratorSymbol,
@@ -54,7 +54,7 @@ import {
IteratorSymbol,
OptionalShortCircuit,
invalidData,
ProgramThrow,
Throw,
rangeError,
type StatementResult,
typeError,
@@ -62,8 +62,8 @@ import {
} from "./model.js"
import { checkStringLength } from "./limits.js"
import { locate, materialize } from "./errors.js"
import type { Prototypes } from "./intrinsics.js"
import { globals, type Host } from "./globals.js"
import type { Builtins } from "./intrinsics.js"
import { globals } from "./globals.js"
import {
assign,
Callable,
@@ -72,23 +72,24 @@ import {
has,
hasPrototype,
keys,
NativeFunction,
Native,
parseArrayIndex,
ProgramArray,
ProgramDate,
ProgramFunction,
ProgramGenerator,
ProgramMap,
ProgramObject,
ProgramPromise,
ProgramSet,
ProgramURLSearchParams,
Arr,
Bytes,
DateObj,
Fn,
GeneratorObj,
MapObj,
Obj,
PromiseObj,
SetObj,
URLSearchParamsObj,
record,
remove,
set,
} from "./objects.js"
import { preserveConsumerError, type Runner } from "./runner.js"
import { PromiseRuntime, resolvePromise, resolvePromiseValue } from "./promises.js"
import { preserveConsumerError } from "./callback.js"
import { Pending, resolvePromise, resolvePromiseValue } from "./promises.js"
import { containsOpaqueReference, describeValue, rejectCircularInsertion, typeofValue } from "./references.js"
import { ScopeStack } from "./scope.js"
import { constructRegExp } from "../stdlib/regexp.js"
@@ -129,7 +130,7 @@ const instanceofValue = (lhs: unknown, rhs: unknown, node: AstNode): boolean =>
throw typeError("The right-hand side of 'instanceof' is not callable.", node)
}
const prototype = get(rhs, "prototype")
if (!(prototype instanceof ProgramObject)) {
if (!(prototype instanceof Obj)) {
throw typeError("The right-hand side of 'instanceof' has no 'prototype' object.", node)
}
return hasPrototype(lhs, prototype)
@@ -220,14 +221,14 @@ const loopDeclaration = (left: VariableDeclaration | Pattern, statement: "for...
}
type CustomIterator = {
iterator: ProgramObject
iterator: Obj
next: unknown
asynchronous: boolean
}
/** A resolved member: the object to read through and the receiver that inherited accessors see. */
type MemberReference = {
target: ProgramObject
target: Obj
key: PropertyKey
receiver: unknown
}
@@ -249,29 +250,28 @@ type GeneratorState = {
}
/** One program execution: the tool bridge, promise scheduler, captured logs, and the global scope built once. */
export class Runtime<R> {
readonly runner: Runner<R>
export class Interpreter<R> {
readonly tools: ToolRuntime<R>
readonly pending: Pending<R>
readonly builtins: Builtins
readonly logs: Array<string>
private readonly root: Frame<R>
constructor(
readonly executeTool: (path: ReadonlyArray<string>, args: Array<unknown>) => Effect.Effect<unknown, unknown, R>,
readonly search: (args: Array<unknown>) => Effect.Effect<unknown, unknown, R>,
readonly toolKeys: (path: ReadonlyArray<string>) => ReadonlyArray<string>,
readonly promises: PromiseRuntime<R>,
readonly prototypes: Prototypes,
readonly logs: Array<string> = [],
extraGlobals: (host: Host<R>) => ReadonlyArray<readonly [string, unknown]> = () => [],
) {
constructor(options: {
readonly tools: ToolRuntime<R>
readonly pending: Pending<R>
readonly builtins: Builtins
readonly logs?: Array<string>
readonly globals?: (ctx: Interpreter<R>) => ReadonlyArray<readonly [string, unknown]>
}) {
this.tools = options.tools
this.pending = options.pending
this.builtins = options.builtins
this.logs = options.logs ?? []
const globalScope = new Map<string, Binding>()
// Calling back into the program never reads frame state, so any frame serves; the root is always alive.
this.root = new Frame(this, new ScopeStack([globalScope]))
this.runner = {
invokeCallable: (callable, thisValue, args) => this.root.invokeCallable(callable, thisValue, args),
settlePromise: (promise) => this.root.settlePromise(promise),
syncIterator: (value) => this.root.syncIterator(value),
prototypes,
}
for (const [name, value] of [...globals(this), ...extraGlobals(this)]) {
for (const [name, value] of [...globals(this), ...(options.globals?.(this) ?? [])]) {
globalScope.set(name, { mutable: false, value })
}
}
@@ -279,6 +279,30 @@ export class Runtime<R> {
run(program: Program): Effect.Effect<unknown, unknown, R> {
return this.root.run(program)
}
call(callable: unknown, thisValue: unknown, args: Array<unknown>): Effect.Effect<unknown, unknown, R> {
return this.root.call(callable, thisValue, args)
}
await(promise: PromiseObj): Effect.Effect<unknown, unknown, never> {
return this.root.await(promise)
}
iterate(value: unknown) {
return this.root.iterate(value)
}
/** Runs one host tool: arguments cross as JSON and the result comes back as program values. */
tool(
run: (args: Array<Json | undefined>) => Effect.Effect<Json | undefined, unknown, R>,
args: Array<unknown>,
): Effect.Effect<unknown, unknown, R> {
const ctx = this
return Effect.gen(function* () {
const json = yield* Effect.forEach(args, (arg) => toBoundary(ctx, arg))
return fromJson(ctx, yield* run(json))
})
}
}
const MAX_CALL_DEPTH = 10_000
@@ -289,7 +313,7 @@ class Frame<R> {
private generatorAsync = false
constructor(
private readonly runtime: Runtime<R>,
private readonly ctx: Interpreter<R>,
private scopes: ScopeStack,
/** Nested call depth; resets when an await resumes, since the continuation runs from the job queue. */
private depth = 0,
@@ -322,7 +346,7 @@ class Frame<R> {
}
// The implicit async body adopts returned promises before copy-out.
value = yield* resolvePromiseValue(self.runtime.runner, value)
value = yield* resolvePromiseValue(self.ctx, value)
return value
}).pipe(Effect.ensuring(Effect.sync(() => self.scopes.pop())))
}
@@ -331,16 +355,16 @@ class Frame<R> {
private createToolCallPromise(
path: ReadonlyArray<string>,
args: Array<unknown>,
): Effect.Effect<ProgramPromise, never, R> {
return this.runtime.promises.create(Effect.suspend(() => this.runtime.executeTool(path, args)))
): Effect.Effect<PromiseObj, never, R> {
return this.ctx.pending.create(this.ctx.tool((json) => this.ctx.tools.execute(path, json), args))
}
// Fiber exits make settlement idempotent; yielding prevents inline continuation.
settlePromise(promise: ProgramPromise): Effect.Effect<unknown, unknown, never> {
const promises = this.runtime.promises
await(promise: PromiseObj): Effect.Effect<unknown, unknown, never> {
const pending = this.ctx.pending
return Effect.suspend(() => {
promises.markObserved(promise)
return Effect.flatMap(promises.await(promise), (exit) => Effect.andThen(Effect.yieldNow, exit))
pending.markObserved(promise)
return Effect.flatMap(pending.await(promise), (exit) => Effect.andThen(Effect.yieldNow, exit))
})
}
@@ -414,9 +438,9 @@ class Frame<R> {
private createFunction(
node: FunctionDeclaration | FunctionExpression | ArrowFunctionExpression,
name = node.type === "ArrowFunctionExpression" ? "" : (node.id?.name ?? ""),
): ProgramFunction {
return new ProgramFunction(
this.runtime.prototypes.Function,
): Fn {
return new Fn(
this.ctx.builtins.Function,
name,
node.params,
node.body,
@@ -626,7 +650,7 @@ class Frame<R> {
const right = yield* self.evaluateExpression(node.right)
const iterator = yield* self.customIterator(right, node, awaiting)
const cursor = iterator === undefined ? yield* self.syncIterator(right, node) : undefined
const cursor = iterator === undefined ? yield* self.iterate(right, node) : undefined
if (iterator === undefined && cursor === undefined) {
throw invalidData(
`${awaiting ? "for await...of" : "for...of"} requires an array, string, Map, Set, or URLSearchParams, or custom iterator value.`,
@@ -695,9 +719,9 @@ class Frame<R> {
}
private awaitValue(value: unknown): Effect.Effect<unknown, unknown, R> {
return Effect.flatMap(resolvePromise(this.runtime.runner, this.runtime.promises, value), (promise) =>
return Effect.flatMap(resolvePromise(this.ctx, value), (promise) =>
Effect.ensuring(
this.settlePromise(promise),
this.await(promise),
Effect.sync(() => (this.depth = 0)),
),
)
@@ -720,27 +744,29 @@ class Frame<R> {
})
}
syncIterator(value: unknown, node?: AstNode) {
iterate(value: unknown, node?: AstNode) {
const iterator =
value instanceof ProgramArray
value instanceof Arr
? value.items[Symbol.iterator]()
: typeof value === "string"
? value[Symbol.iterator]()
: value instanceof ProgramMap
: value instanceof MapObj
? value.map.entries()
: value instanceof ProgramSet
: value instanceof SetObj
? value.set.values()
: value instanceof ProgramURLSearchParams
: value instanceof URLSearchParamsObj
? value.params.entries()
: undefined
: value instanceof Bytes
? value.bytes.values()
: undefined
if (iterator !== undefined) {
const proto = this.runtime.prototypes.Array
const proto = this.ctx.builtins.Array
return Effect.succeed({
next: Effect.sync(() => {
const step = iterator.next()
return {
done: Boolean(step.done),
value: Array.isArray(step.value) ? new ProgramArray(proto, step.value) : step.value,
value: Array.isArray(step.value) ? new Arr(proto, step.value) : step.value,
}
}),
close: Effect.void,
@@ -758,13 +784,13 @@ class Frame<R> {
}
private customIterator(value: unknown, node: AstNode | undefined, allowAsync = true) {
if (!(value instanceof ProgramObject)) return Effect.undefined
if (!(value instanceof Obj)) return Effect.undefined
const asyncMethod = allowAsync ? get(value, AsyncIteratorSymbol) : undefined
const method = asyncMethod ?? get(value, IteratorSymbol)
if (method === undefined || method === null) return Effect.undefined
const self = this
return Effect.map(
this.invokeCallable(this.requireIteratorMethod(method, "Iterator method", node), value, [], node),
this.call(this.requireIteratorMethod(method, "Iterator method", node), value, [], node),
(iterator) => {
const object = self.requireIteratorObject(iterator, "Iterator method result", node)
return {
@@ -781,14 +807,14 @@ class Frame<R> {
return Effect.gen(function* () {
if (iterator.asynchronous) {
const object = self.requireIteratorObject(
yield* self.awaitValue(yield* self.invokeCallable(iterator.next, iterator.iterator, [], node)),
yield* self.awaitValue(yield* self.call(iterator.next, iterator.iterator, [], node)),
"Iterator next() result",
node,
)
return { done: Boolean(get(object, "done")), value: get(object, "value") }
}
const called = yield* Effect.exit(self.invokeCallable(iterator.next, iterator.iterator, [], node))
const called = yield* Effect.exit(self.call(iterator.next, iterator.iterator, [], node))
if (!Exit.isSuccess(called)) {
if (awaiting) yield* Effect.yieldNow
return yield* Effect.failCause(called.cause)
@@ -824,14 +850,14 @@ class Frame<R> {
const method = self.requireIteratorMethod(close, "Iterator return", node)
if (iterator.asynchronous) {
self.requireIteratorObject(
yield* self.awaitValue(yield* self.invokeCallable(method, iterator.iterator, [], node)),
yield* self.awaitValue(yield* self.call(method, iterator.iterator, [], node)),
"Iterator return() result",
node,
)
return
}
const called = yield* Effect.exit(self.invokeCallable(method, iterator.iterator, [], node))
const called = yield* Effect.exit(self.call(method, iterator.iterator, [], node))
if (!Exit.isSuccess(called)) {
if (awaiting) yield* Effect.yieldNow
return yield* Effect.failCause(called.cause)
@@ -847,8 +873,8 @@ class Frame<R> {
})
}
private requireIteratorObject(value: unknown, context: string, node?: AstNode): ProgramObject {
if (value instanceof ProgramObject) return value
private requireIteratorObject(value: unknown, context: string, node?: AstNode): Obj {
if (value instanceof Obj) return value
throw typeError(`${context} must be an object.`, node)
}
@@ -859,9 +885,9 @@ class Frame<R> {
// for...in over null/undefined iterates nothing, like JS.
private enumerableKeys(value: unknown, node: AstNode): Array<string> {
if (value instanceof ToolReference) return [...this.runtime.toolKeys(value.path)]
if (value instanceof ToolReference) return [...this.ctx.tools.keys(value.path)]
if (value === null || value === undefined) return []
return keys(enumerableSource(this.runtime.runner, "for...in", value, node))
return keys(enumerableSource(this.ctx, "for...in", value, node))
}
private evaluateForInStatement(
@@ -951,7 +977,7 @@ class Frame<R> {
}
private evaluateThrowStatement(node: ThrowStatement): Effect.Effect<StatementResult, unknown, R> {
return Effect.flatMap(this.evaluateExpression(node.argument), (value) => Effect.fail(new ProgramThrow(value)))
return Effect.flatMap(this.evaluateExpression(node.argument), (value) => Effect.fail(new Throw(value)))
}
private evaluateTryStatement(node: TryStatement): Effect.Effect<StatementResult, unknown, R> {
@@ -966,7 +992,7 @@ class Frame<R> {
return Effect.failCause(cause)
}
const caught = materialize(self.runtime.runner, Cause.squash(cause))
const caught = materialize(self.ctx, Cause.squash(cause))
const parameter = handler.param
self.scopes.push()
return Effect.gen(function* () {
@@ -1043,7 +1069,7 @@ class Frame<R> {
}
if (pattern.type === "ObjectPattern") {
if (!(value instanceof ProgramObject)) {
if (!(value instanceof Obj)) {
throw typeError(
`Object destructuring requires a data object or array value, received ${describeValue(value)}.`,
pattern,
@@ -1053,7 +1079,7 @@ class Frame<R> {
const consumed = new Set<PropertyKey>()
for (const property of pattern.properties) {
if (property.type === "RestElement") {
const rest = new ProgramObject(self.runtime.prototypes.Object)
const rest = new Obj(self.ctx.builtins.Object)
assign(rest, value, consumed)
yield* self.declarePattern(property.argument, rest, mutable, property, initialize)
continue
@@ -1102,7 +1128,7 @@ class Frame<R> {
}
if (pattern.type === "ObjectPattern") {
if (!(value instanceof ProgramObject)) {
if (!(value instanceof Obj)) {
throw invalidData(
`Object destructuring requires a data object or array value, received ${describeValue(value)}.`,
pattern,
@@ -1112,7 +1138,7 @@ class Frame<R> {
const consumed = new Set<PropertyKey>()
for (const property of pattern.properties) {
if (property.type === "RestElement") {
const rest = new ProgramObject(self.runtime.prototypes.Object)
const rest = new Obj(self.ctx.builtins.Object)
assign(rest, value, consumed)
yield* self.assignPattern(property.argument, rest, property)
continue
@@ -1147,7 +1173,7 @@ class Frame<R> {
): Effect.Effect<void, unknown, R> {
const self = this
return Effect.gen(function* () {
const cursor = yield* self.syncIterator(value, pattern)
const cursor = yield* self.iterate(value, pattern)
if (cursor === undefined) {
throw typeError("Array destructuring requires a supported iterable value.", pattern)
}
@@ -1157,7 +1183,7 @@ class Frame<R> {
if (element === null) continue
yield* consume(
element.type === "RestElement" ? element.argument : element,
element.type === "RestElement" ? new ProgramArray(self.runtime.prototypes.Array) : undefined,
element.type === "RestElement" ? new Arr(self.ctx.builtins.Array) : undefined,
element,
)
if (element.type === "RestElement") return
@@ -1174,7 +1200,7 @@ class Frame<R> {
done = next.done
if (!done) rest.push(next.value)
}
yield* consume(element.argument, new ProgramArray(self.runtime.prototypes.Array, rest), element)
yield* consume(element.argument, new Arr(self.ctx.builtins.Array, rest), element)
return
}
const consumed = consume(element, step.done ? undefined : step.value, pattern)
@@ -1201,8 +1227,9 @@ class Frame<R> {
switch (node.type) {
case "Literal": {
const regex = node.regex
if (regex) return Effect.sync(() => constructRegExp(this.runtime.prototypes, [regex.pattern, regex.flags]))
return Effect.sync(() => toProgram(this.runtime.prototypes, node.value, "Literal"))
if (regex) return Effect.sync(() => constructRegExp(this.ctx.builtins, [regex.pattern, regex.flags]))
if (typeof node.value === "bigint") throw typeError("BigInt literals are not supported.", node)
return Effect.succeed(node.value)
}
case "Identifier":
return Effect.sync(() => this.scopes.get(node.name, node))
@@ -1263,22 +1290,22 @@ class Frame<R> {
return Effect.gen(function* () {
const callee = yield* self.evaluateExpression(node.callee)
// Globals are built with this interpreter's R; `instanceof` cannot recover the type argument.
const construct = callee instanceof NativeFunction ? (callee as NativeFunction<R>).construct : undefined
const construct = callee instanceof Native ? (callee as Native<R>).construct : undefined
if (construct === undefined) {
// `new` itself is supported, so a non-constructible callee is a TypeError like JS rather than
// unsupported syntax. Built-ins like Number are real constructors in JS, so do not claim
// otherwise; say `new` is unsupported for them and point at the plain call.
const name = calleeDescription(node.callee)
const message =
callee instanceof ProgramFunction
callee instanceof Fn
? `${name} cannot be constructed: user-defined constructors and classes are not supported. Call it as a function that returns a plain object instead.`
: callee instanceof NativeFunction
: callee instanceof Native
? `new ${name}(...) is not supported; call ${name}(...) without new instead.`
: `${name} is not a constructor.`
throw typeError(message, node)
}
const args = yield* self.evaluateCallArguments(node.arguments)
return yield* self.native(() => construct(args, callee as NativeFunction<R>), node)
return yield* self.native(() => construct(args, callee as Native<R>), node)
})
}
@@ -1291,18 +1318,14 @@ class Frame<R> {
const lhs = yield* self.evaluateExpression(left)
const rhs = yield* self.evaluateExpression(node.right)
if (operator === "instanceof") return instanceofValue(lhs, rhs, node)
return toProgram(
self.runtime.prototypes,
self.applyBinaryOperator(operator, lhs, rhs, node),
"Binary expression result",
)
return self.applyBinaryOperator(operator, lhs, rhs, node)
})
}
private applyBinaryOperator(operator: string, lhs: unknown, rhs: unknown, node: AstNode): unknown {
if (operator === "===") return lhs === rhs
if (operator === "!==") return lhs !== rhs
if (operator === "in" && rhs instanceof ProgramObject && !containsOpaqueReference(lhs)) {
if (operator === "in" && rhs instanceof Obj && !containsOpaqueReference(lhs)) {
return has(rhs, lhs !== null && typeof lhs === "object" ? coerceToString(lhs) : (lhs as PropertyKey))
}
if (containsOpaqueReference(lhs) || containsOpaqueReference(rhs)) {
@@ -1311,7 +1334,7 @@ class Frame<R> {
// Null-prototype data needs explicit primitive coercion; identity and `in` retain raw objects.
// Dates use their default string hint for addition and loose equality, and epoch time elsewhere.
const coerceOperand = (operand: unknown): unknown => {
if (operand instanceof ProgramDate) {
if (operand instanceof DateObj) {
return operator === "+" || operator === "==" || operator === "!=" ? coerceToString(operand) : operand.time
}
return operand !== null && typeof operand === "object" ? coerceToString(operand) : operand
@@ -1360,7 +1383,7 @@ class Frame<R> {
case ">>>":
return (l as number) >>> (r as number)
case "in":
if (!(rhs instanceof ProgramObject)) {
if (!(rhs instanceof Obj)) {
throw typeError("The 'in' operator requires a data object on the right-hand side.", node)
}
return has(rhs, coerceOperand(lhs) as PropertyKey)
@@ -1396,7 +1419,7 @@ class Frame<R> {
throw invalidData("Unary operators require data values.", node)
}
const operand =
value instanceof ProgramDate
value instanceof DateObj
? value.time
: value !== null && typeof value === "object"
? coerceToString(value)
@@ -1415,7 +1438,7 @@ class Frame<R> {
default:
throw typeError(`Unsupported unary operator '${operator}'.`, node)
}
return toProgram(this.runtime.prototypes, result, "Unary expression result")
return result
})
}
@@ -1437,12 +1460,7 @@ class Frame<R> {
if (operator !== "=") {
const current = self.scopes.get(name, left)
const rightValue = yield* self.evaluateExpression(node.right)
const next = toProgram(
self.runtime.prototypes,
self.applyCompoundAssignment(operator, current, rightValue, node),
"Assignment result",
)
return self.scopes.set(name, next, left)
return self.scopes.set(name, self.applyCompoundAssignment(operator, current, rightValue, node), left)
}
const rightValue = yield* self.evaluateNamed(node.right, name)
return self.scopes.set(name, rightValue, left)
@@ -1451,11 +1469,7 @@ class Frame<R> {
return yield* self.modifyMember(left, (current) =>
Effect.map(self.evaluateExpression(node.right), (rightValue) => {
if (operator === "=") return { write: true, next: rightValue, result: rightValue }
const next = toProgram(
self.runtime.prototypes,
self.applyCompoundAssignment(operator, current, rightValue, node),
"Assignment result",
)
const next = self.applyCompoundAssignment(operator, current, rightValue, node)
return { write: true, next, result: next }
}),
)
@@ -1551,7 +1565,7 @@ class Frame<R> {
if ((callable === null || callable === undefined) && node.optional) return OptionalShortCircuit
const args = yield* self.evaluateCallArguments(node.arguments)
return yield* self.invokeCallable(callable, thisValue, args, node, callee)
return yield* self.call(callable, thisValue, args, node, callee)
})
}
@@ -1565,7 +1579,7 @@ class Frame<R> {
}
// The single dispatch for every invocation: call expressions and callbacks share it.
invokeCallable(
call(
callable: unknown,
thisValue: unknown,
args: Array<unknown>,
@@ -1580,9 +1594,9 @@ class Frame<R> {
}
return yield* self.createToolCallPromise(callable.path, args)
}
if (callable instanceof ProgramFunction) return yield* self.invokeFunction(callable, args, node)
if (callable instanceof NativeFunction) {
return yield* self.native(() => (callable as NativeFunction<R>).call(thisValue, args), node)
if (callable instanceof Fn) return yield* self.invokeFunction(callable, args, node)
if (callable instanceof Native) {
return yield* self.native(() => (callable as Native<R>).call(thisValue, args), node)
}
throw typeError(`${calleeDescription(callee)} is not a function.`, callee ?? node)
})
@@ -1606,7 +1620,7 @@ class Frame<R> {
for (const argNode of argNodes) {
if (argNode.type === "SpreadElement") {
const spread = yield* self.evaluateExpression(argNode.argument)
const cursor = yield* self.syncIterator(spread, argNode)
const cursor = yield* self.iterate(spread, argNode)
if (cursor === undefined) throw typeError("Spread arguments require a synchronous iterable.", argNode)
while (true) {
const step = yield* cursor.next
@@ -1622,12 +1636,12 @@ class Frame<R> {
}
// A callback invoked by a built-in runs below the call that invoked the built-in, so the deeper of the two counts.
invokeFunction(fn: ProgramFunction, args: Array<unknown>, node?: AstNode): Effect.Effect<unknown, unknown, R> {
invokeFunction(fn: Fn, args: Array<unknown>, node?: AstNode): Effect.Effect<unknown, unknown, R> {
const self = this
return Effect.flatMap(CallSite, (site) => {
const depth = Math.max(self.depth, site.depth) + 1
if (depth > MAX_CALL_DEPTH) throw rangeError("Maximum call stack size exceeded", node)
const invocation = new Frame(this.runtime, new ScopeStack([...fn.capturedScopes, new Map()]), depth)
const invocation = new Frame(this.ctx, new ScopeStack([...fn.capturedScopes, new Map()]), depth)
const run = Effect.gen(function* () {
// Seed all parameters first so defaults cannot fall through to same-named outer bindings.
const paramScope = invocation.scopes.current()
@@ -1640,7 +1654,7 @@ class Frame<R> {
if (parameter.type === "RestElement") {
yield* invocation.declarePattern(
parameter.argument,
new ProgramArray(self.runtime.prototypes.Array, args.slice(index)),
new Arr(self.ctx.builtins.Array, args.slice(index)),
true,
parameter,
true,
@@ -1661,8 +1675,8 @@ class Frame<R> {
})
if (fn.generator) return Effect.succeed(this.createGenerator(invocation, run, fn.async))
if (!fn.async) return run
return this.runtime.promises.createWithSelf((self) =>
Effect.flatMap(run, (value) => resolvePromiseValue(invocation.runtime.runner, value, self)),
return this.ctx.pending.createWithSelf((self) =>
Effect.flatMap(run, (value) => resolvePromiseValue(invocation.ctx, value, self)),
)
})
}
@@ -1671,12 +1685,12 @@ class Frame<R> {
invocation: Frame<R>,
run: Effect.Effect<unknown, unknown, R>,
asynchronous: boolean,
): ProgramGenerator {
): GeneratorObj {
const state: GeneratorState = { started: false, completed: false, draining: false, pending: [], pendingIndex: 0 }
invocation.generatorState = state
invocation.generatorAsync = asynchronous
const protos = this.runtime.prototypes
const result = (value: unknown, done: boolean) => record(protos.Object, { value, done })
const builtins = this.ctx.builtins
const result = (value: unknown, done: boolean) => record(builtins.Object, { value, done })
const request = (kind: GeneratorRequestKind, value: unknown) => {
const request = { kind, value, response: Deferred.makeUnsafe<unknown, unknown>() }
if (!asynchronous && state.active) return Effect.die(typeError("Generator is already running."))
@@ -1687,7 +1701,7 @@ class Frame<R> {
if (state.draining) return Deferred.await(request.response)
state.draining = true
return Effect.andThen(
this.runtime.promises.fork(
this.ctx.pending.fork(
invocation
.completeGeneratorRequests(state, true)
.pipe(Effect.ensuring(Effect.sync(() => (state.draining = false)))),
@@ -1696,12 +1710,12 @@ class Frame<R> {
)
}
if (state.completed) {
if (kind === "throw") return Effect.fail(new ProgramThrow(value))
if (kind === "throw") return Effect.fail(new Throw(value))
return Effect.succeed(result(kind === "return" ? value : undefined, true))
}
if (!state.started && kind !== "next") {
state.completed = true
if (kind === "throw") return Effect.fail(new ProgramThrow(value))
if (kind === "throw") return Effect.fail(new Throw(value))
return Effect.succeed(result(value, true))
}
@@ -1735,12 +1749,12 @@ class Frame<R> {
yield* invocation.completeGeneratorRequests(state, asynchronous)
state.completed = true
})
return Effect.andThen(this.runtime.promises.fork(body), Deferred.await(request.response))
return Effect.andThen(this.ctx.pending.fork(body), Deferred.await(request.response))
}
return Deferred.await(request.response)
}
const generator = new ProgramGenerator(
asynchronous ? protos.AsyncGenerator : protos.Generator,
const generator = new GeneratorObj(
asynchronous ? builtins.AsyncGenerator : builtins.Generator,
asynchronous,
request,
)
@@ -1749,13 +1763,13 @@ class Frame<R> {
private completeGeneratorRequests(state: GeneratorState, asynchronous: boolean): Effect.Effect<void, never, R> {
const self = this
const result = (value: unknown, done: boolean) => record(self.runtime.prototypes.Object, { value, done })
const result = (value: unknown, done: boolean) => record(self.ctx.builtins.Object, { value, done })
return Effect.gen(function* () {
while (true) {
const pending = self.dequeueGeneratorRequest(state)
if (!pending) return
if (pending.kind === "throw") {
Deferred.doneUnsafe(pending.response, Exit.fail(new ProgramThrow(pending.value)))
Deferred.doneUnsafe(pending.response, Exit.fail(new Throw(pending.value)))
continue
}
if (asynchronous && pending.kind === "return") {
@@ -1813,15 +1827,12 @@ class Frame<R> {
private suspendGenerator(value: unknown, node: AstNode): Effect.Effect<unknown, unknown, R> {
const state = this.generatorState
if (!state?.active) throw typeError("Generator has no active request.", node)
Deferred.doneUnsafe(
state.active.response,
Exit.succeed(record(this.runtime.prototypes.Object, { value, done: false })),
)
Deferred.doneUnsafe(state.active.response, Exit.succeed(record(this.ctx.builtins.Object, { value, done: false })))
state.active = undefined
return Effect.flatMap(this.takeGeneratorRequest(state), (request) => {
state.active = request
if (request.kind === "next") return Effect.succeed(request.value)
if (request.kind === "throw") return Effect.fail(new ProgramThrow(request.value))
if (request.kind === "throw") return Effect.fail(new Throw(request.value))
return this.generatorAsync
? Effect.flatMap(this.awaitValue(request.value), (value) => Effect.fail(new GeneratorReturn(value)))
: Effect.fail(new GeneratorReturn(request.value))
@@ -1832,13 +1843,14 @@ class Frame<R> {
const self = this
return Effect.gen(function* () {
if (
value instanceof ProgramArray ||
value instanceof Arr ||
typeof value === "string" ||
value instanceof ProgramMap ||
value instanceof ProgramSet ||
value instanceof ProgramURLSearchParams
value instanceof MapObj ||
value instanceof SetObj ||
value instanceof URLSearchParamsObj ||
value instanceof Bytes
) {
const cursor = yield* self.syncIterator(value, node)
const cursor = yield* self.iterate(value, node)
if (!cursor) throw typeError("Built-in iterator is unavailable.", node)
while (true) {
const step = yield* cursor.next
@@ -1852,7 +1864,7 @@ class Frame<R> {
yield* cursor.close
return yield* Effect.fail(error)
}
if (error instanceof ProgramThrow) {
if (error instanceof Throw) {
yield* cursor.close
throw typeError("The delegated iterator does not provide a throw() method.", node)
}
@@ -1871,7 +1883,7 @@ class Frame<R> {
yield* self.closeIterator(iterator, node, self.generatorAsync)
throw typeError("The delegated iterator does not provide a throw() method.", node)
}
const called = yield* self.invokeCallable(
const called = yield* self.call(
self.requireIteratorMethod(method, `Iterator ${kind}`, node),
iterator.iterator,
[input],
@@ -1899,7 +1911,7 @@ class Frame<R> {
continue
}
const error: unknown = Cause.squash(resumed.cause)
if (!(error instanceof GeneratorReturn) && !(error instanceof ProgramThrow)) {
if (!(error instanceof GeneratorReturn) && !(error instanceof Throw)) {
return yield* Effect.failCause(resumed.cause)
}
kind = error instanceof GeneratorReturn ? "return" : "throw"
@@ -1908,15 +1920,15 @@ class Frame<R> {
})
}
private evaluateObjectExpression(node: ObjectExpression): Effect.Effect<ProgramObject, unknown, R> {
const objectValue = new ProgramObject(this.runtime.prototypes.Object)
private evaluateObjectExpression(node: ObjectExpression): Effect.Effect<Obj, unknown, R> {
const objectValue = new Obj(this.ctx.builtins.Object)
const self = this
return Effect.gen(function* () {
for (const property of node.properties) {
if (property.type === "SpreadElement") {
const spread = yield* self.evaluateExpression(property.argument)
if (spread === null || spread === undefined) continue
assign(objectValue, enumerableSource(self.runtime.runner, "Object spread", spread, property))
assign(objectValue, enumerableSource(self.ctx, "Object spread", spread, property))
continue
}
@@ -1951,7 +1963,7 @@ class Frame<R> {
})
}
private evaluateArrayExpression(node: ArrayExpression): Effect.Effect<ProgramArray, unknown, R> {
private evaluateArrayExpression(node: ArrayExpression): Effect.Effect<Arr, unknown, R> {
const values: Array<unknown> = []
const self = this
@@ -1964,7 +1976,7 @@ class Frame<R> {
}
if (element.type === "SpreadElement") {
const spread = yield* self.evaluateExpression(element.argument)
const cursor = yield* self.syncIterator(spread, element)
const cursor = yield* self.iterate(spread, element)
if (cursor === undefined) throw typeError("Array spread requires a synchronous iterable.", element)
while (true) {
const step = yield* cursor.next
@@ -1975,7 +1987,7 @@ class Frame<R> {
values.push(yield* self.evaluateExpression(element))
}
}
return new ProgramArray(self.runtime.prototypes.Array, values)
return new Arr(self.ctx.builtins.Array, values)
})
}
@@ -1998,7 +2010,7 @@ class Frame<R> {
if (index < expressions.length) {
const raw = yield* self.evaluateExpression(expressions[index])
output += coerceToString(toProgram(self.runtime.prototypes, raw, "Template interpolation"))
output += coerceToString(raw)
checkStringLength(output.length)
}
}
@@ -2046,18 +2058,18 @@ class Frame<R> {
return new ToolReference([...objectValue.path, key])
}
if (objectValue instanceof ProgramObject) return { target: objectValue, key, receiver: objectValue }
if (objectValue instanceof Obj) return { target: objectValue, key, receiver: objectValue }
// Primitives read through their wrapper prototype without being boxed; strings own length and indexes.
const protos = self.runtime.prototypes
const builtins = self.ctx.builtins
if (typeof objectValue === "string") {
if (key === "length") return { value: objectValue.length }
const index = typeof key === "symbol" ? undefined : parseArrayIndex(key)
if (index !== undefined) return { value: objectValue[index] }
return { target: protos.String, key, receiver: objectValue }
return { target: builtins.String, key, receiver: objectValue }
}
if (typeof objectValue === "number") return { target: protos.Number, key, receiver: objectValue }
if (typeof objectValue === "boolean") return { target: protos.Boolean, key, receiver: objectValue }
if (typeof objectValue === "number") return { target: builtins.Number, key, receiver: objectValue }
if (typeof objectValue === "boolean") return { target: builtins.Boolean, key, receiver: objectValue }
if (objectValue === null || objectValue === undefined) {
throw typeError(`Cannot read properties of ${objectValue} (reading '${String(key)}').`, objectNode)
@@ -2068,7 +2080,7 @@ class Frame<R> {
private readReference(reference: MemberReference, node: MemberExpression): unknown {
// Reject unknown promise properties so a missing await cannot hide.
if (reference.target instanceof ProgramPromise && !has(reference.target, reference.key)) {
if (reference.target instanceof PromiseObj && !has(reference.target, reference.key)) {
throw invalidData(
"This value is an un-awaited Promise; await it first - e.g. `const result = await tools.ns.tool(...)`.",
node.object,
@@ -2078,7 +2090,7 @@ class Frame<R> {
}
// Accessors throw without a location; the member or pattern that read them supplies it.
private readProperty(target: ProgramObject, key: PropertyKey, node: AstNode, receiver: unknown = target): unknown {
private readProperty(target: Obj, key: PropertyKey, node: AstNode, receiver: unknown = target): unknown {
try {
return get(target, key, receiver)
} catch (error) {
@@ -2138,13 +2150,13 @@ class Frame<R> {
})
}
private assignToReference(target: ProgramObject, key: PropertyKey, next: unknown, node: AstNode): void {
private assignToReference(target: Obj, key: PropertyKey, next: unknown, node: AstNode): void {
const written = (() => {
try {
rejectCircularInsertion(
target,
next,
target instanceof ProgramArray ? "Array assignment result" : "Object assignment result",
target instanceof Arr ? "Array assignment result" : "Object assignment result",
)
return set(target, key, next)
} catch (error) {
@@ -2152,7 +2164,7 @@ class Frame<R> {
}
})()
if (written) return
if (target instanceof ProgramArray && key === "length") throw rangeError("Invalid array length", node)
if (target instanceof Arr && key === "length") throw rangeError("Invalid array length", node)
throw typeError(`Cannot assign to read only property '${String(key)}'.`, node)
}
+18 -12
View File
@@ -1,5 +1,5 @@
import { Effect } from "effect"
import { define, hidden, NativeFunction, ProgramArray, ProgramError, ProgramObject } from "./objects.js"
import { define, hidden, Native, Arr, ErrorObj, Obj } from "./objects.js"
export const errorTypes = [
"Error",
@@ -29,6 +29,9 @@ const builtins = [
"Set",
"URL",
"URLSearchParams",
"Uint8Array",
"TextEncoder",
"TextDecoder",
"Promise",
"Iterator",
"AsyncIterator",
@@ -40,24 +43,24 @@ const builtins = [
* The built-in prototype objects of one runtime, allocated empty in dependency order. The globals populate them
* and attach their constructors when the runtime is built.
*/
export type Prototypes = Readonly<Record<(typeof builtins)[number] | ErrorType, ProgramObject>>
export type Builtins = Readonly<Record<(typeof builtins)[number] | ErrorType, Obj>>
export const createErrorValue = (prototype: ProgramObject, message: string | undefined): ProgramError => {
const value = new ProgramError(prototype)
export const createErrorValue = (prototype: Obj, message: string | undefined): ErrorObj => {
const value = new ErrorObj(prototype)
if (message !== undefined) define(value, "message", message, hidden)
return value
}
export const createPrototypes = (): Prototypes => {
const object = new ProgramObject(null)
export const createBuiltins = (): Builtins => {
const object = new Obj(null)
// Function.prototype is itself callable and returns undefined.
const fn = new NativeFunction(object, { name: "", call: () => Effect.undefined })
const plain = () => new ProgramObject(object)
const fn = new Native(object, { name: "", call: () => Effect.undefined })
const plain = () => new Obj(object)
const error = plain()
define(error, "name", "Error", hidden)
define(error, "message", "", hidden)
const derived = (type: ErrorType) => {
const proto = new ProgramObject(error)
const proto = new Obj(error)
define(proto, "name", type, hidden)
define(proto, "message", "", hidden)
return proto
@@ -67,7 +70,7 @@ export const createPrototypes = (): Prototypes => {
return {
Object: object,
Function: fn,
Array: new ProgramArray(object),
Array: new Arr(object),
String: plain(),
Number: plain(),
Boolean: plain(),
@@ -77,11 +80,14 @@ export const createPrototypes = (): Prototypes => {
Set: plain(),
URL: plain(),
URLSearchParams: plain(),
Uint8Array: plain(),
TextEncoder: plain(),
TextDecoder: plain(),
Promise: plain(),
Iterator: iterator,
AsyncIterator: asyncIterator,
Generator: new ProgramObject(iterator),
AsyncGenerator: new ProgramObject(asyncIterator),
Generator: new Obj(iterator),
AsyncGenerator: new Obj(asyncIterator),
Error: error,
TypeError: derived("TypeError"),
RangeError: derived("RangeError"),
@@ -9,6 +9,8 @@ export const MAX_STRING_LENGTH = 1 << 24
export const MAX_ARRAY_LENGTH = 10_000_000
/** Most promises that may be pending at once. */
export const MAX_PENDING_PROMISES = 10_000
/** Deepest nesting a value may have when it crosses to or from the host. */
export const MAX_VALUE_DEPTH = 32
export const checkStringLength = (length: number): void => {
if (length > MAX_STRING_LENGTH) throw rangeError("Invalid string length")

Some files were not shown because too many files have changed in this diff Show More