@volter/world-core 2.0.36 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/README.md +4 -5
  2. package/app-route.cjs +12 -6
  3. package/app-route.d.cts +1 -1
  4. package/dist/app-route.cjs +12 -6
  5. package/dist/app-route.d.cts +1 -1
  6. package/dist/generated/pack-facts.json +1410 -3069
  7. package/dist/inject.cjs +64 -9
  8. package/dist/pack-facts.cjs +44 -0
  9. package/dist/src/actions.d.ts +3 -3
  10. package/dist/src/actions.js +22 -16
  11. package/dist/src/ancestry.d.ts +14 -2
  12. package/dist/src/ancestry.js +92 -2
  13. package/dist/src/anthropic-wire.d.ts +39 -0
  14. package/dist/src/anthropic-wire.js +136 -0
  15. package/dist/src/bytes.d.ts +7 -0
  16. package/dist/src/bytes.js +35 -0
  17. package/dist/src/changeset.d.ts +1 -1
  18. package/dist/src/changeset.js +0 -0
  19. package/dist/src/clickhouse/index.d.ts +3 -0
  20. package/dist/src/clickhouse/index.js +6 -0
  21. package/dist/src/clickhouse/sql.d.ts +233 -0
  22. package/dist/src/clickhouse/sql.js +4329 -0
  23. package/dist/src/clickhouse/types.d.ts +18 -0
  24. package/dist/src/clickhouse/types.js +47 -0
  25. package/dist/src/clickhouse/values.d.ts +146 -0
  26. package/dist/src/clickhouse/values.js +858 -0
  27. package/dist/src/client-bundle.js +2 -3
  28. package/dist/src/cors.d.ts +15 -0
  29. package/dist/src/cors.js +31 -0
  30. package/dist/src/derived-core.d.ts +487 -24
  31. package/dist/src/derived-core.js +788 -144
  32. package/dist/src/derived-real.d.ts +13 -0
  33. package/dist/src/derived-real.js +518 -0
  34. package/dist/src/derived.d.ts +35 -1
  35. package/dist/src/derived.js +61 -9
  36. package/dist/src/emit.js +1 -2
  37. package/dist/src/events.d.ts +206 -0
  38. package/dist/src/events.js +341 -0
  39. package/dist/src/executor.d.ts +3 -0
  40. package/dist/src/executor.js +19 -2
  41. package/dist/src/file-response.d.ts +6 -0
  42. package/dist/src/file-response.js +30 -0
  43. package/dist/src/fork.js +3 -2
  44. package/dist/src/git/history.d.ts +7 -0
  45. package/dist/src/git/history.js +24 -0
  46. package/dist/src/git/index.d.ts +1 -0
  47. package/dist/src/git/index.js +1 -0
  48. package/dist/src/git/lfs.d.ts +28 -0
  49. package/dist/src/git/lfs.js +66 -0
  50. package/dist/src/git/objects.js +3 -8
  51. package/dist/src/git/smart-http.d.ts +3 -1
  52. package/dist/src/git/smart-http.js +67 -6
  53. package/dist/src/graphql-wire.d.ts +29 -0
  54. package/dist/src/graphql-wire.js +101 -0
  55. package/dist/src/grpc-wire.d.ts +67 -0
  56. package/dist/src/grpc-wire.js +170 -0
  57. package/dist/src/h2.d.ts +40 -0
  58. package/dist/src/h2.js +656 -0
  59. package/dist/src/head.d.ts +32 -3
  60. package/dist/src/head.js +161 -40
  61. package/dist/src/history.d.ts +1 -1
  62. package/dist/src/history.js +6 -6
  63. package/dist/src/hpack.json +1 -0
  64. package/dist/src/index.d.ts +64 -75
  65. package/dist/src/index.js +58 -101
  66. package/dist/src/log.js +28 -19
  67. package/dist/src/machines.d.ts +50 -0
  68. package/dist/src/machines.js +151 -0
  69. package/dist/src/managed-database.d.ts +86 -0
  70. package/dist/src/managed-database.js +283 -0
  71. package/dist/src/multipart.d.ts +11 -0
  72. package/dist/src/multipart.js +51 -0
  73. package/dist/src/observe.d.ts +15 -5
  74. package/dist/src/observe.js +23 -9
  75. package/dist/src/openai-wire.d.ts +108 -0
  76. package/dist/src/openai-wire.js +337 -0
  77. package/dist/src/pack-assets.d.ts +3 -4
  78. package/dist/src/pack-assets.js +15 -10
  79. package/dist/src/pack-fetch.d.ts +77 -0
  80. package/dist/src/pack-fetch.js +449 -0
  81. package/dist/src/pack-paths.d.ts +12 -0
  82. package/dist/src/pack-paths.js +86 -0
  83. package/dist/src/packRegistry.d.ts +69 -162
  84. package/dist/src/packRegistry.js +55 -20
  85. package/dist/src/people.d.ts +13 -0
  86. package/dist/src/people.js +18 -0
  87. package/dist/src/placeholder-image.d.ts +5 -0
  88. package/dist/src/placeholder-image.js +114 -0
  89. package/dist/src/protobuf.d.ts +28 -0
  90. package/dist/src/protobuf.js +332 -0
  91. package/dist/src/redis/engine.js +1 -1
  92. package/dist/src/request-scope.d.ts +1 -1
  93. package/dist/src/request-scope.js +6 -4
  94. package/dist/src/resource-blob.d.ts +5 -0
  95. package/dist/src/resource-blob.js +11 -0
  96. package/dist/src/runtime.d.ts +85 -0
  97. package/dist/src/runtime.js +104 -0
  98. package/dist/src/s3/wire.d.ts +60 -0
  99. package/dist/src/s3/wire.js +157 -0
  100. package/dist/src/scenario.d.ts +3 -0
  101. package/dist/src/scenario.js +2 -0
  102. package/dist/src/schema-sample.d.ts +1 -0
  103. package/dist/src/schema-sample.js +21 -0
  104. package/dist/src/sealed-box.d.ts +14 -0
  105. package/dist/src/sealed-box.js +225 -0
  106. package/dist/src/serve-http.d.ts +14 -0
  107. package/dist/src/serve-http.js +27 -3
  108. package/dist/src/serve.d.ts +6 -0
  109. package/dist/src/serve.js +69 -14
  110. package/dist/src/signing.d.ts +135 -0
  111. package/dist/src/signing.js +222 -0
  112. package/dist/src/sigv4.d.ts +48 -0
  113. package/dist/src/sigv4.js +167 -0
  114. package/dist/src/smtp.d.ts +16 -0
  115. package/dist/src/smtp.js +72 -0
  116. package/dist/src/sockets.d.ts +51 -0
  117. package/dist/src/sockets.js +90 -0
  118. package/dist/src/state-system.d.ts +1 -0
  119. package/dist/src/state-system.js +1 -1
  120. package/dist/src/storage.d.ts +1 -1
  121. package/dist/src/storage.js +3 -3
  122. package/dist/src/trace-context.js +1 -1
  123. package/dist/src/twin-fetch.d.ts +0 -7
  124. package/dist/src/twin-fetch.js +0 -14
  125. package/dist/src/vendor-call.d.ts +6 -0
  126. package/dist/src/vendor-call.js +41 -0
  127. package/dist/src/world-store.js +1 -1
  128. package/dist/vendor-hosts.cjs +36 -125
  129. package/dist/vendor-hosts.d.cts +8 -0
  130. package/generated/pack-facts.json +1410 -3069
  131. package/inject.cjs +64 -9
  132. package/pack-facts.cjs +44 -0
  133. package/package.json +17 -3
  134. package/src/actions.ts +23 -16
  135. package/src/ancestry.ts +74 -2
  136. package/src/anthropic-wire.ts +137 -0
  137. package/src/bytes.ts +42 -0
  138. package/src/changeset.ts +5 -5
  139. package/src/clickhouse/index.ts +6 -0
  140. package/src/clickhouse/sql.ts +3059 -0
  141. package/src/clickhouse/types.ts +44 -0
  142. package/src/clickhouse/values.ts +697 -0
  143. package/src/client-bundle.ts +2 -3
  144. package/src/cors.ts +34 -0
  145. package/src/derived-core.ts +1013 -146
  146. package/src/derived-real.ts +434 -0
  147. package/src/derived.ts +73 -3
  148. package/src/emit.ts +1 -2
  149. package/src/events.ts +449 -0
  150. package/src/executor.ts +24 -2
  151. package/src/file-response.ts +27 -0
  152. package/src/fork.ts +3 -2
  153. package/src/git/history.ts +19 -0
  154. package/src/git/index.ts +1 -0
  155. package/src/git/lfs.ts +67 -0
  156. package/src/git/objects.ts +3 -5
  157. package/src/git/smart-http.ts +56 -6
  158. package/src/graphql-wire.ts +106 -0
  159. package/src/grpc-wire.ts +159 -0
  160. package/src/h2.ts +627 -0
  161. package/src/head.ts +132 -41
  162. package/src/history.ts +6 -6
  163. package/src/hpack.json +1 -0
  164. package/src/index.ts +82 -329
  165. package/src/log.ts +27 -18
  166. package/src/machines.ts +151 -0
  167. package/src/managed-database.ts +299 -0
  168. package/src/multipart.ts +51 -0
  169. package/src/observe.ts +31 -15
  170. package/src/openai-wire.ts +371 -0
  171. package/src/pack-assets.ts +15 -11
  172. package/src/pack-fetch.ts +458 -0
  173. package/src/pack-paths.ts +72 -0
  174. package/src/packRegistry.ts +79 -167
  175. package/src/people.ts +31 -0
  176. package/src/placeholder-image.ts +88 -0
  177. package/src/protobuf.ts +251 -0
  178. package/src/redis/engine.ts +1 -1
  179. package/src/request-scope.ts +8 -4
  180. package/src/resource-blob.ts +13 -0
  181. package/src/runtime.ts +344 -0
  182. package/src/s3/wire.ts +172 -0
  183. package/src/scenario.ts +4 -0
  184. package/src/schema-sample.ts +24 -0
  185. package/src/sealed-box.ts +182 -0
  186. package/src/serve-http.ts +31 -3
  187. package/src/serve.ts +58 -14
  188. package/src/signing.ts +231 -0
  189. package/src/sigv4.ts +158 -0
  190. package/src/smtp.ts +76 -0
  191. package/src/sockets.ts +140 -0
  192. package/src/state-system.ts +2 -2
  193. package/src/storage.ts +3 -3
  194. package/src/trace-context.ts +1 -1
  195. package/src/twin-fetch.ts +0 -20
  196. package/src/vendor-call.ts +41 -0
  197. package/src/world-store.ts +1 -1
  198. package/vendor-hosts.cjs +36 -125
  199. package/vendor-hosts.d.cts +8 -0
  200. package/dist/src/mirror-shell.d.ts +0 -2
  201. package/dist/src/mirror-shell.js +0 -13
  202. package/dist/src/v1-removed.d.ts +0 -159
  203. package/dist/src/v1-removed.js +0 -124
  204. package/src/mirror-shell.ts +0 -15
  205. package/src/v1-removed.ts +0 -172
@@ -0,0 +1,371 @@
1
+ // The OpenAI-compatible wire (architecture, "The kernel owns shared mechanics"): the chat completions and Responses
2
+ // shapes that many vendors serve as OpenAI defines them (OpenRouter, xAI, DeepSeek, Groq, Together, Fireworks, Cerebras
3
+ // and more), for generative packs that run no model. A pack decides its turn (the World's scenario's, else a stub) and
4
+ // its own facts (its ids, a provider field, a keep-alive comment); this module shapes the answer and its Server-Sent
5
+ // Events as the wire defines them (https://platform.openai.com/docs/api-reference/chat and /responses). Importing it does
6
+ // nothing.
7
+ import type { HandlerContext } from './derived-core.ts';
8
+ import type { PackScenario } from './pack-fetch.ts';
9
+ import type { PackScenarioAdapter, ScenarioDecision, ScenarioFeatures } from './scenario.ts';
10
+ import { sampleFromSchema } from './schema-sample.ts';
11
+
12
+ type Row = Record<string, unknown>;
13
+ const obj = (v: unknown): Row => (v && typeof v === 'object' && !Array.isArray(v) ? v as Row : {});
14
+ const arr = (v: unknown): Row[] => (Array.isArray(v) ? v as Row[] : []);
15
+
16
+ /** A turn: the text, or tool calls, the reasoning when there is some, and why it finished. */
17
+ export type WireTurn = { text?: string | null; toolCalls?: Array<{ name: string; arguments: Row }>; reasoning?: string | null; finishReason?: string };
18
+
19
+ /** Tokens, estimated at about four characters a token: a twin's count, not a tokenizer's. */
20
+ export const estimateTokens = (text: string): number => Math.max(1, Math.ceil(text.length / 4));
21
+
22
+ // ── chat completions: requests ─────────────────────────────────────────────────────────────────
23
+
24
+ /** A message's content as text: a string, or its text parts joined. */
25
+ export const chatText = (content: unknown): string => (typeof content === 'string' ? content : arr(content).map((p) => (typeof p.text === 'string' ? p.text : '')).join(''));
26
+ /** The last user message's text. */
27
+ export function chatLastUserText(messages: Row[]): string {
28
+ for (let i = messages.length - 1; i >= 0; i--) if (messages[i]!.role === 'user') return chatText(messages[i]!.content);
29
+ return '';
30
+ }
31
+ /** The tool results the last message carries (a `tool` message), by its name or call id. */
32
+ export const chatToolResults = (messages: Row[]): string[] => (messages.at(-1)?.role === 'tool' ? [String(messages.at(-1)!.name ?? messages.at(-1)!.tool_call_id ?? '')] : []);
33
+
34
+ /** The labeled stub for a chat request: with tools offered (`tool_choice` not `none`) and the last message not a tool's
35
+ * result, it calls the tool the choice names, else the first, with arguments its schema admits; with a JSON response
36
+ * format, it answers a value the schema admits; otherwise it echoes the last user message. `label` names the twin. */
37
+ export function chatStubTurn(body: Row, label: string): WireTurn {
38
+ const model = String(body.model);
39
+ const messages = arr(body.messages);
40
+ const tools = arr(body.tools).filter((t) => obj(t.function).name !== undefined);
41
+ const choice = body.tool_choice ?? 'auto';
42
+ const named = typeof choice === 'object' ? String(obj(obj(choice).function).name ?? '') : '';
43
+ if (tools.length && choice !== 'none' && (chatToolResults(messages).length === 0 || choice === 'required' || named)) {
44
+ const f = obj(((named ? tools.find((t) => obj(t.function).name === named) : undefined) ?? tools[0]!).function);
45
+ return { toolCalls: [{ name: String(f.name), arguments: sampleFromSchema(f.parameters, String(f.name)) as Row }] };
46
+ }
47
+ const format = obj(body.response_format);
48
+ if (format.type === 'json_schema') return { text: JSON.stringify(sampleFromSchema(obj(format.json_schema).schema)) };
49
+ if (format.type === 'json_object') return { text: JSON.stringify({ twin_stub: `deterministic stub from the ${label} (${model}): no model runs` }) };
50
+ const said = chatLastUserText(messages).trim();
51
+ return { text: `[twin-stub:${model}] This is a deterministic stub from the ${label} (no model runs). Your last message: ${said.length > 200 ? `${said.slice(0, 200)}…` : said || '(empty)'}` };
52
+ }
53
+
54
+ // ── chat completions: answers ──────────────────────────────────────────────────────────────────
55
+
56
+ export type ChatShape = {
57
+ id: string; model: string; created: number; turn: WireTurn; messages: Row[];
58
+ /** the request's max_tokens / max_completion_tokens: a longer text is cut there and finishes `length` */
59
+ maxTokens?: number;
60
+ /** each tool call's id */
61
+ callId: (i: number) => string;
62
+ /** fields a vendor adds at the top of the answer and of each chunk (OpenRouter's `provider`) */
63
+ extra?: Row;
64
+ /** top-level fields on interim stream chunks instead of extra; terminal chunks retain extra */
65
+ interimExtra?: Row;
66
+ /** fields a vendor adds to the usage (OpenRouter's `cost`) */
67
+ usageExtra?: Row;
68
+ /** the vendor also answers the provider's own finish reason (`native_finish_reason`) */
69
+ nativeFinish?: boolean;
70
+ /** the name the reasoning takes in a message (`reasoning`, or `reasoning_content`) */
71
+ reasoningField?: string;
72
+ };
73
+
74
+ function chatParts(s: ChatShape): { text: string | null; finish: string; calls: Row[]; usage: Row; reasoning: string | null } {
75
+ let text = s.turn.text ?? null;
76
+ let finish = s.turn.finishReason ?? (s.turn.toolCalls?.length ? 'tool_calls' : 'stop');
77
+ if (text !== null && s.maxTokens !== undefined && estimateTokens(text) > s.maxTokens) { text = text.slice(0, s.maxTokens * 4); finish = 'length'; }
78
+ const calls = (s.turn.toolCalls ?? []).map((c, i) => ({ id: s.callId(i), type: 'function', function: { name: c.name, arguments: JSON.stringify(c.arguments ?? {}) } }));
79
+ const reasoning = s.turn.reasoning ?? null;
80
+ const prompt = estimateTokens(s.messages.map((m) => chatText(m.content)).join('\n'));
81
+ const completion = estimateTokens([text ?? '', ...calls.map((c) => (c.function as Row).arguments as string)].join(''));
82
+ const reasoningTokens = reasoning ? estimateTokens(reasoning) : 0;
83
+ const usage = {
84
+ prompt_tokens: prompt, completion_tokens: completion + reasoningTokens, total_tokens: prompt + completion + reasoningTokens,
85
+ prompt_tokens_details: { cached_tokens: 0 }, completion_tokens_details: { reasoning_tokens: reasoningTokens }, ...(s.usageExtra ?? {}),
86
+ };
87
+ return { text, finish, calls, usage, reasoning };
88
+ }
89
+
90
+ /** A chat completion's body (`object: chat.completion`), one choice. */
91
+ export function chatCompletion(s: ChatShape): Row {
92
+ const p = chatParts(s);
93
+ const field = s.reasoningField ?? 'reasoning';
94
+ const message = { role: 'assistant', content: p.text, refusal: null, ...(p.reasoning ? { [field]: p.reasoning } : {}), ...(p.calls.length ? { tool_calls: p.calls } : {}) };
95
+ return {
96
+ id: s.id, ...(s.extra ?? {}), model: s.model, object: 'chat.completion', created: s.created,
97
+ choices: [{ index: 0, message, logprobs: null, finish_reason: p.finish, ...(s.nativeFinish ? { native_finish_reason: p.finish } : {}) }], usage: p.usage,
98
+ };
99
+ }
100
+
101
+ /** A chat completion streamed: its chunks (`object: chat.completion.chunk`) as Server-Sent Events, the text in two
102
+ * deltas, the last chunk carrying the finish reason and the usage, then `data: [DONE]`. `comment` is a keep-alive a
103
+ * vendor sends first (OpenRouter's `: OPENROUTER PROCESSING`). */
104
+ export function chatCompletionSSE(s: ChatShape & { comment?: string }): string {
105
+ const p = chatParts(s);
106
+ const field = s.reasoningField ?? 'reasoning';
107
+ const chunk = (delta: Row, last: boolean): string => `data: ${JSON.stringify({
108
+ id: s.id, ...((last ? s.extra : s.interimExtra ?? s.extra) ?? {}), model: s.model, object: 'chat.completion.chunk', created: s.created,
109
+ choices: [{ index: 0, delta, logprobs: null, finish_reason: last ? p.finish : null, ...(s.nativeFinish ? { native_finish_reason: last ? p.finish : null } : {}) }],
110
+ ...(last ? { usage: p.usage } : {}),
111
+ })}\n\n`;
112
+ const half = p.text ? Math.ceil(p.text.length / 2) : 0;
113
+ return [
114
+ ...(s.comment ? [`: ${s.comment}\n\n`] : []),
115
+ ...(p.reasoning ? [chunk({ role: 'assistant', content: '', [field]: p.reasoning }, false)] : []),
116
+ ...(p.text ? [chunk({ role: 'assistant', content: p.text.slice(0, half) }, false), ...(p.text.length > half ? [chunk({ content: p.text.slice(half) }, false)] : [])] : []),
117
+ ...(p.calls.length ? [chunk({ role: 'assistant', content: null, tool_calls: p.calls.map((c, i) => ({ index: i, ...c })) }, false)] : []),
118
+ chunk({}, true),
119
+ 'data: [DONE]\n\n',
120
+ ].join('');
121
+ }
122
+
123
+ // ── the AI SDK's language-model protocol (Vercel's AI Gateway) ─────────────────────────────────────
124
+ // `@ai-sdk/gateway` posts a model call's options as `@ai-sdk/provider`'s LanguageModelV4 defines them (the model in the
125
+ // `ai-language-model-id` header) and reads back its generate result, or its stream parts as Server-Sent Events
126
+ // (https://unpkg.com/@ai-sdk/provider@4.0.14/dist/index.d.ts, `LanguageModelV4GenerateResult`, `LanguageModelV4StreamPart`).
127
+
128
+ /** A V4 call's options as the chat completions request they mean: its prompt as messages, its tools, tool choice,
129
+ * response format and output limit; so the chat stub and the wire's scenario read it. */
130
+ export function aiSdkChatBody(call: Row, model: string): Row {
131
+ const text = (content: unknown): string => (typeof content === 'string' ? content : arr(content).filter((p) => p.type === 'text' || p.type === 'reasoning').map((p) => String(p.text ?? '')).join(''));
132
+ const messages: Row[] = [];
133
+ for (const m of arr(call.prompt)) {
134
+ if (m.role === 'tool') for (const r of arr(m.content).filter((p) => p.type === 'tool-result')) messages.push({ role: 'tool', tool_call_id: r.toolCallId, name: r.toolName, content: JSON.stringify(r.output ?? null) });
135
+ else if (m.role === 'assistant') {
136
+ const calls = arr(m.content).filter((p) => p.type === 'tool-call').map((c) => ({ id: c.toolCallId, type: 'function', function: { name: c.toolName, arguments: typeof c.input === 'string' ? c.input : JSON.stringify(c.input ?? {}) } }));
137
+ messages.push({ role: 'assistant', content: text(m.content), ...(calls.length ? { tool_calls: calls } : {}) });
138
+ } else messages.push({ role: String(m.role ?? 'user'), content: text(m.content) });
139
+ }
140
+ const tools = arr(call.tools).filter((x) => x.type === 'function').map((x) => ({ type: 'function', function: { name: x.name, description: x.description, parameters: x.inputSchema } }));
141
+ const choice = obj(call.toolChoice);
142
+ const format = obj(call.responseFormat);
143
+ return {
144
+ model, messages,
145
+ ...(tools.length ? { tools } : {}),
146
+ ...(choice.type === 'tool' ? { tool_choice: { type: 'function', function: { name: choice.toolName } } } : typeof choice.type === 'string' ? { tool_choice: choice.type } : {}),
147
+ ...(format.type === 'json' ? { response_format: format.schema ? { type: 'json_schema', json_schema: { name: format.name ?? 'response', schema: format.schema } } : { type: 'json_object' } } : {}),
148
+ ...(typeof call.maxOutputTokens === 'number' ? { max_tokens: call.maxOutputTokens } : {}),
149
+ };
150
+ }
151
+
152
+ const UNIFIED: Record<string, string> = { stop: 'stop', length: 'length', tool_calls: 'tool-calls', content_filter: 'content-filter' };
153
+ function lmParts(s: ChatShape): { content: Row[]; finishReason: Row; usage: Row } {
154
+ const p = chatParts(s);
155
+ const u = p.usage as { prompt_tokens: number; completion_tokens: number; completion_tokens_details: { reasoning_tokens: number } };
156
+ return {
157
+ content: [
158
+ ...(p.reasoning ? [{ type: 'reasoning', text: p.reasoning }] : []),
159
+ ...(p.text !== null ? [{ type: 'text', text: p.text }] : []),
160
+ ...p.calls.map((c) => ({ type: 'tool-call', toolCallId: c.id, toolName: (c.function as Row).name, input: (c.function as Row).arguments })),
161
+ ],
162
+ finishReason: { unified: UNIFIED[p.finish] ?? 'other', raw: p.finish },
163
+ usage: {
164
+ inputTokens: { total: u.prompt_tokens, noCache: u.prompt_tokens, cacheRead: 0, cacheWrite: 0 },
165
+ outputTokens: { total: u.completion_tokens, text: u.completion_tokens - u.completion_tokens_details.reasoning_tokens, reasoning: u.completion_tokens_details.reasoning_tokens },
166
+ },
167
+ };
168
+ }
169
+
170
+ /** A V4 generate result: the turn's content (reasoning, text, tool calls with their input as JSON text), its finish
171
+ * reason (unified, and the raw one) and its usage; `extra` is what the server adds (the gateway's provider metadata). */
172
+ export function languageModelResult(s: ChatShape): Row {
173
+ const r = lmParts(s);
174
+ return { ...r, response: { id: s.id, modelId: s.model, timestamp: new Date(s.created * 1000).toISOString() }, warnings: [], ...(s.extra ?? {}) };
175
+ }
176
+
177
+ /** A V4 stream: its parts as Server-Sent Events (stream-start, response-metadata, the reasoning, the text in two deltas,
178
+ * each tool call's input then the call, and the finish), with no `[DONE]`: the SDK reads every event as a part. */
179
+ export function languageModelSSE(s: ChatShape): string {
180
+ const r = lmParts(s);
181
+ const out: Row[] = [{ type: 'stream-start', warnings: [] }, { type: 'response-metadata', id: s.id, modelId: s.model, timestamp: new Date(s.created * 1000).toISOString() }];
182
+ for (const [i, c] of r.content.entries()) {
183
+ if (c.type === 'reasoning') out.push({ type: 'reasoning-start', id: `reasoning-${i}` }, { type: 'reasoning-delta', id: `reasoning-${i}`, delta: c.text }, { type: 'reasoning-end', id: `reasoning-${i}` });
184
+ else if (c.type === 'text') {
185
+ const text = String(c.text); const half = Math.ceil(text.length / 2);
186
+ out.push({ type: 'text-start', id: `text-${i}` }, { type: 'text-delta', id: `text-${i}`, delta: text.slice(0, half) }, ...(text.length > half ? [{ type: 'text-delta', id: `text-${i}`, delta: text.slice(half) }] : []), { type: 'text-end', id: `text-${i}` });
187
+ } else out.push({ type: 'tool-input-start', id: c.toolCallId, toolName: c.toolName }, { type: 'tool-input-delta', id: c.toolCallId, delta: c.input }, { type: 'tool-input-end', id: c.toolCallId }, c);
188
+ }
189
+ out.push({ type: 'finish', finishReason: r.finishReason, usage: r.usage, ...(s.extra ?? {}) });
190
+ return out.map((part) => `data: ${JSON.stringify(part)}\n\n`).join('');
191
+ }
192
+
193
+ // ── Responses: requests ────────────────────────────────────────────────────────────────────────
194
+
195
+ /** A Responses request's input as items: a string is one user message. */
196
+ export const responsesItems = (input: unknown): Row[] => (typeof input === 'string' ? [{ type: 'message', role: 'user', content: input }] : arr(input));
197
+ const itemText = (content: unknown): string => (typeof content === 'string' ? content : arr(content).map((p) => (typeof p.text === 'string' ? p.text : '')).join(''));
198
+ /** The last user message's text in a Responses input. */
199
+ export function responsesLastUserText(input: unknown): string {
200
+ const items = responsesItems(input);
201
+ for (let i = items.length - 1; i >= 0; i--) if ((items[i]!.role ?? (items[i]!.type === 'message' ? 'user' : '')) === 'user') return itemText(items[i]!.content);
202
+ return '';
203
+ }
204
+ /** The function-call outputs the input ends with. */
205
+ export const responsesToolResults = (input: unknown): string[] => responsesItems(input).filter((i) => i.type === 'function_call_output').map((i) => String(i.call_id ?? ''));
206
+
207
+ /** The labeled stub for a Responses request: a function tool is called (unless the input already carries its output or
208
+ * `tool_choice` is `none`), a JSON schema format answers a value it admits, otherwise the last user text is echoed. */
209
+ export function responsesStubTurn(body: Row, label: string): WireTurn {
210
+ const model = String(body.model);
211
+ const tools = arr(body.tools).filter((t) => t.type === 'function' && typeof t.name === 'string');
212
+ const choice = body.tool_choice ?? 'auto';
213
+ const named = typeof choice === 'object' ? String(obj(choice).name ?? '') : '';
214
+ if (tools.length && choice !== 'none' && (responsesToolResults(body.input).length === 0 || choice === 'required' || named)) {
215
+ const t = (named ? tools.find((x) => x.name === named) : undefined) ?? tools[0]!;
216
+ return { toolCalls: [{ name: String(t.name), arguments: sampleFromSchema(t.parameters, String(t.name)) as Row }] };
217
+ }
218
+ const format = obj(obj(body.text).format);
219
+ if (format.type === 'json_schema') return { text: JSON.stringify(sampleFromSchema(format.schema)) };
220
+ if (format.type === 'json_object') return { text: JSON.stringify({ twin_stub: `deterministic stub from the ${label} (${model}): no model runs` }) };
221
+ const said = responsesLastUserText(body.input).trim();
222
+ return { text: `[twin-stub:${model}] This is a deterministic stub from the ${label} (no model runs). Your last message: ${said.length > 200 ? `${said.slice(0, 200)}…` : said || '(empty)'}` };
223
+ }
224
+
225
+ // ── Responses: answers ─────────────────────────────────────────────────────────────────────────
226
+
227
+ export type ResponsesShape = {
228
+ id: string; model: string; createdAt: number; turn: WireTurn; body: Row;
229
+ /** ids for the output items: the message, the reasoning, each function call and its call id */
230
+ itemId: (kind: 'msg' | 'rs' | 'fc' | 'call', i: number) => string;
231
+ extra?: Row;
232
+ };
233
+
234
+ /** A Responses object (`object: response`, `status: completed`): its output items (the reasoning when there is some,
235
+ * the message with its output text, or function calls) and its usage. */
236
+ export function responsesAnswer(s: ResponsesShape): Row {
237
+ const b = s.body;
238
+ let text = s.turn.text ?? null;
239
+ const max = typeof b.max_output_tokens === 'number' ? b.max_output_tokens : undefined;
240
+ let incomplete: Row | null = null;
241
+ if (text !== null && max !== undefined && estimateTokens(text) > max) { text = text.slice(0, max * 4); incomplete = { reason: 'max_output_tokens' }; }
242
+ const output: Row[] = [
243
+ ...(s.turn.reasoning ? [{ type: 'reasoning', id: s.itemId('rs', 0), summary: [{ type: 'summary_text', text: s.turn.reasoning }], status: 'completed' }] : []),
244
+ ...(text !== null ? [{ type: 'message', id: s.itemId('msg', 0), status: incomplete ? 'incomplete' : 'completed', role: 'assistant', content: [{ type: 'output_text', text, annotations: [], logprobs: [] }] }] : []),
245
+ ...(s.turn.toolCalls ?? []).map((c, i) => ({ type: 'function_call', id: s.itemId('fc', i), call_id: s.itemId('call', i), name: c.name, arguments: JSON.stringify(c.arguments ?? {}), status: 'completed' })),
246
+ ];
247
+ const input = estimateTokens([String(b.instructions ?? ''), ...responsesItems(b.input).map((i) => itemText(i.content ?? i.output))].join('\n'));
248
+ const out = estimateTokens([text ?? '', ...(s.turn.toolCalls ?? []).map((c) => JSON.stringify(c.arguments ?? {}))].join(''));
249
+ const reasoningTokens = s.turn.reasoning ? estimateTokens(s.turn.reasoning) : 0;
250
+ return {
251
+ id: s.id, object: 'response', created_at: s.createdAt, status: incomplete ? 'incomplete' : 'completed', ...(s.extra ?? {}),
252
+ error: null, incomplete_details: incomplete, instructions: b.instructions ?? null, max_output_tokens: max ?? null, model: s.model, output,
253
+ parallel_tool_calls: b.parallel_tool_calls ?? true, previous_response_id: b.previous_response_id ?? null,
254
+ reasoning: { effort: obj(b.reasoning).effort ?? null, summary: obj(b.reasoning).summary ?? null }, store: b.store ?? true,
255
+ temperature: b.temperature ?? 1, text: b.text ?? { format: { type: 'text' } }, tool_choice: b.tool_choice ?? 'auto', tools: b.tools ?? [], top_p: b.top_p ?? 1,
256
+ usage: { input_tokens: input, input_tokens_details: { cached_tokens: 0 }, output_tokens: out + reasoningTokens, output_tokens_details: { reasoning_tokens: reasoningTokens }, total_tokens: input + out + reasoningTokens },
257
+ metadata: b.metadata ?? {},
258
+ };
259
+ }
260
+
261
+ /** A Responses object streamed: the events the wire sends (`response.created`, `response.in_progress`, each output
262
+ * item's added, its text deltas and done, `response.completed`), each `event:` and `data:` with its sequence number. */
263
+ export function responsesSSE(s: ResponsesShape): string {
264
+ const done = responsesAnswer(s);
265
+ const pending = { ...done, status: 'in_progress', output: [], usage: null };
266
+ const events: Row[] = [{ type: 'response.created', response: pending }, { type: 'response.in_progress', response: pending }];
267
+ for (const [i, item] of (done.output as Row[]).entries()) {
268
+ if (item.type === 'message') {
269
+ const text = String(((item.content as Row[])[0] ?? {}).text ?? '');
270
+ events.push({ type: 'response.output_item.added', output_index: i, item: { ...item, status: 'in_progress', content: [] } });
271
+ events.push({ type: 'response.content_part.added', item_id: item.id, output_index: i, content_index: 0, part: { type: 'output_text', text: '', annotations: [] } });
272
+ const half = Math.ceil(text.length / 2);
273
+ for (const delta of [text.slice(0, half), text.slice(half)].filter(Boolean)) events.push({ type: 'response.output_text.delta', item_id: item.id, output_index: i, content_index: 0, delta, logprobs: [] });
274
+ events.push({ type: 'response.output_text.done', item_id: item.id, output_index: i, content_index: 0, text, logprobs: [] });
275
+ events.push({ type: 'response.content_part.done', item_id: item.id, output_index: i, content_index: 0, part: (item.content as Row[])[0] });
276
+ } else if (item.type === 'function_call') {
277
+ events.push({ type: 'response.output_item.added', output_index: i, item: { ...item, arguments: '', status: 'in_progress' } });
278
+ events.push({ type: 'response.function_call_arguments.delta', item_id: item.id, output_index: i, delta: item.arguments });
279
+ events.push({ type: 'response.function_call_arguments.done', item_id: item.id, output_index: i, arguments: item.arguments });
280
+ } else {
281
+ events.push({ type: 'response.output_item.added', output_index: i, item });
282
+ }
283
+ events.push({ type: 'response.output_item.done', output_index: i, item });
284
+ }
285
+ events.push({ type: done.status === 'incomplete' ? 'response.incomplete' : 'response.completed', response: done });
286
+ return events.map((e, n) => `event: ${String(e.type)}\ndata: ${JSON.stringify({ ...e, sequence_number: n })}\n\n`).join('');
287
+ }
288
+
289
+ // ── the scenario ───────────────────────────────────────────────────────────────────────────────
290
+ // A generative pack's turn is the World's scenario's when it scripts one (architecture, "Behaviour"): the same request
291
+ // features, `on` matchers and `respond` keys for every vendor on this wire, over a chat completion or a Responses
292
+ // request alike. The pack names its vendor, its operations and its fault's error body.
293
+
294
+ export type WireRequest = { model: string; text: string; lastUser: string; tools: string[]; results: string[]; toolsForbidden?: boolean };
295
+
296
+ /** Whether a request forbids every tool call: OpenAI's `tool_choice: 'none'`, Anthropic's `{ type: 'none' }`. */
297
+ export const forbidsTools = (choice: unknown): boolean => choice === 'none' || obj(choice).type === 'none';
298
+
299
+ /** The turn a scenario's decision scripts (`respond`: text, toolCalls, reasoning, finishReason), or undefined. */
300
+ export function scenarioTurn(decision: ScenarioDecision | undefined): WireTurn | undefined {
301
+ if (!decision || decision.kind !== 'handler') return undefined;
302
+ const r = obj(decision.respond);
303
+ return {
304
+ ...(typeof r.text === 'string' ? { text: r.text } : {}),
305
+ ...(r.toolCalls !== undefined ? { toolCalls: (Array.isArray(r.toolCalls) ? r.toolCalls : [r.toolCalls]) as WireTurn['toolCalls'] } : {}),
306
+ ...(typeof r.reasoning === 'string' ? { reasoning: r.reasoning } : {}),
307
+ ...(typeof r.finishReason === 'string' ? { finishReason: r.finishReason } : {}),
308
+ };
309
+ }
310
+
311
+ const nonEmpty = (v: unknown): v is string => typeof v === 'string' && v.length > 0;
312
+ const RESPOND_KEYS = new Set(['text', 'toolCalls', 'reasoning', 'finishReason']);
313
+
314
+ /** A request as the scenario reads it: its model, its text, its last user text, the tools it offers and the tool
315
+ * results it ends with, from a chat completion's messages or a Responses input. */
316
+ export function wireRequest(body: Row): WireRequest {
317
+ const messages = arr(body.messages);
318
+ const tools = arr(body.tools).map((t) => String(obj(t.function).name ?? t.name ?? '')).filter(Boolean);
319
+ const forbidden = forbidsTools(body.tool_choice) ? { toolsForbidden: true } : {};
320
+ if (messages.length) return { model: String(body.model ?? ''), text: messages.map((m) => chatText(m.content)).join('\n'), lastUser: chatLastUserText(messages), tools, results: chatToolResults(messages), ...forbidden };
321
+ const items = responsesItems(body.input);
322
+ return { model: String(body.model ?? ''), text: [String(body.instructions ?? ''), ...items.map((i) => itemText(i.content))].join('\n'), lastUser: responsesLastUserText(body.input), tools, results: responsesToolResults(body.input), ...forbidden };
323
+ }
324
+
325
+ /** The scenario of a vendor on this wire: its operations, and its error body for a scripted fault. `readers` reads an
326
+ * operation whose request is another wire's (a gateway's Anthropic Messages surface: anthropicWire's
327
+ * `messagesWireRequest`), so one scenario scripts every surface's turns alike. */
328
+ export function scenario(vendor: string, operations: string[], faultBody: (status: number, message: string | undefined) => unknown, readers: Record<string, (body: Row) => WireRequest> = {}): PackScenario<WireRequest> {
329
+ const adapter: PackScenarioAdapter<WireRequest> = {
330
+ vendor,
331
+ renderFault: (f) => ({ body: faultBody(f.status, f.message) }),
332
+ features: (r): ScenarioFeatures => ({ model: r.model, lastUserText: r.lastUser.slice(0, 300), tools: r.tools, lastMessageIsToolResult: r.results.length > 0, toolResultFor: r.results }),
333
+ matchers: {
334
+ modelEquals: (r, c) => nonEmpty(c) && r.model === c,
335
+ userTextIncludes: (r, c) => nonEmpty(c) && r.lastUser.toLowerCase().includes(c.toLowerCase()),
336
+ anyTextIncludes: (r, c) => nonEmpty(c) && r.text.toLowerCase().includes(c.toLowerCase()),
337
+ lastMessageIsToolResult: (r, c) => typeof c === 'boolean' && (r.results.length > 0) === c,
338
+ toolResultFor: (r, c) => nonEmpty(c) && r.results.includes(c),
339
+ hasTool: (r, c) => nonEmpty(c) && r.tools.includes(c),
340
+ },
341
+ text: (r) => r.text,
342
+ // source: https://platform.openai.com/docs/api-reference/chat/create "none means the model will not call any tool and instead generates a message"
343
+ admits: (r, respond) => !(r.toolsForbidden && obj(respond).toolCalls !== undefined),
344
+ validateOn: (on) => {
345
+ for (const k of ['modelEquals', 'userTextIncludes', 'anyTextIncludes', 'toolResultFor', 'hasTool'] as const) if (on[k] !== undefined && !nonEmpty(on[k])) return `on.${k} is a non-empty string`;
346
+ if (on.lastMessageIsToolResult !== undefined && typeof on.lastMessageIsToolResult !== 'boolean') return 'on.lastMessageIsToolResult is a boolean';
347
+ return null;
348
+ },
349
+ validateRespond: (respond) => {
350
+ if (typeof respond !== 'object' || respond === null || Array.isArray(respond)) return 'respond is an object { text?, toolCalls?, reasoning?, finishReason? }';
351
+ const r = respond as Row;
352
+ for (const k of Object.keys(r)) if (!RESPOND_KEYS.has(k)) return `respond: unknown key "${k}" (valid: ${[...RESPOND_KEYS].join(', ')})`;
353
+ if (r.text !== undefined && typeof r.text !== 'string') return 'respond.text is a string';
354
+ if (r.reasoning !== undefined && typeof r.reasoning !== 'string') return 'respond.reasoning is a string';
355
+ for (const c of r.toolCalls === undefined ? [] : Array.isArray(r.toolCalls) ? r.toolCalls : [r.toolCalls]) {
356
+ const u = c as Row;
357
+ if (!u || typeof u !== 'object' || Array.isArray(u) || !nonEmpty(u.name)) return 'respond.toolCalls[].name is a non-empty string';
358
+ }
359
+ if (r.text === undefined && r.toolCalls === undefined) return 'respond needs text or toolCalls';
360
+ return null;
361
+ },
362
+ };
363
+ // a V4 call (the AI SDK's protocol) is read as the chat request it means, its model in its header
364
+ const request = (ctx: HandlerContext): WireRequest => {
365
+ const body = (ctx.body ?? {}) as Row;
366
+ const read = Object.hasOwn(readers, ctx.call.operation.id) ? readers[ctx.call.operation.id] : undefined;
367
+ if (read) return read(body);
368
+ return wireRequest(Array.isArray(body.prompt) ? aiSdkChatBody(body, ctx.call.request.headers.get('ai-language-model-id') ?? '') : body);
369
+ };
370
+ return { adapter, operations, request };
371
+ }
@@ -1,15 +1,14 @@
1
1
  // THE PACK-ASSET SEAM (runtime contract R20). A pack may ship bytes it serves byte-for-byte —
2
- // clerk's vendored clerk-js dist (136 files, 14.5 MB), a mirror shell — that are neither
2
+ // clerk's vendored clerk-js dist (136 files, 14.5 MB) — that are neither
3
3
  // committed text (size) nor world-state blobs (R11's seam is keyed under a world's state root
4
4
  // and filled by requests). They are PACK assets: read-only, versioned with the package, the
5
5
  // same on every world. This seam is how a pack reaches them without touching a host: the
6
6
  // pack declares them on its descriptor (`assets`), reads them through `getActivePackAssets()`,
7
7
  // and the SHELL binds the store — the Bun shell reads the pack's own tree, workerd the
8
- // static-assets binding the mirror shells already deploy through. A pack that reads its tree
8
+ // static-assets binding. A pack that reads its tree
9
9
  // with Bun.file or import.meta is host-bound (R12b); one that reads through here is not.
10
- // No node builtins at module scope: `@volter/world-core`'s index re-exports this module and every mirror
11
- // client bundle (browser target) pulls the index in — a static `node:fs`/`node:url` import here
12
- // broke every mirror bundle the day the seam landed. The local store reads through Bun's file
10
+ // No node builtins at module scope: `@volter/world-core`'s index re-exports this module and a browser
11
+ // bundle pulls the index in, so a static `node:fs`/`node:url` import here would break it. The local store reads through Bun's file
13
12
  // API at call time; the worker store never touches a filesystem.
14
13
 
15
14
  import { existsSync, readFileSync, statSync } from 'node:fs';
@@ -38,21 +37,22 @@ export function confinedAssetPath(relPath: string): string | null {
38
37
  return segs.join('/');
39
38
  }
40
39
 
41
- /** The Bun shell's store: the pack's own directory under the catalog (`packages/twin/<vendor>`).
42
- * `twinDir` ends with a slash. */
40
+ /** The host's store: a pack-directory resolver, or a catalog root ending in a slash. */
43
41
  export class LocalPackAssets implements PackAssetStore {
44
- constructor(private readonly twinDir: string) {}
42
+ constructor(private readonly twinDir: string | ((vendor: string) => string | undefined | Promise<string | undefined>)) {}
45
43
  async fetch(vendor: string, relPath: string): Promise<Response | null> {
46
44
  const safe = confinedAssetPath(relPath);
47
45
  if (safe === null || !/^[a-z][a-z0-9-]*$/.test(vendor)) return null;
48
- const file = `${this.twinDir}${vendor}/${safe}`; // confined: no `..`, no absolute, no empty segment (above)
46
+ const dir = typeof this.twinDir === 'string' ? `${this.twinDir}${vendor}` : await this.twinDir(vendor);
47
+ if (dir === undefined) return null;
48
+ const file = `${dir}/${safe}`; // confined: no `..`, no absolute, no empty segment (above)
49
49
  if (!existsSync(file) || !statSync(file).isFile()) return null;
50
50
  return new Response(readFileSync(file), { status: 200, headers: { 'content-type': assetContentType(safe) } });
51
51
  }
52
52
  }
53
53
 
54
54
  /** The workerd shell's store: the static-assets binding, where the deploy step places each
55
- * pack's declared assets under `/{vendor}/pack/<relPath>` beside the mirror shells. */
55
+ * pack's declared assets under `/{vendor}/pack/<relPath>`. */
56
56
  export class BindingPackAssets implements PackAssetStore {
57
57
  constructor(private readonly assets: { fetch(request: Request): Promise<Response> }) {}
58
58
  async fetch(vendor: string, relPath: string): Promise<Response | null> {
@@ -67,7 +67,11 @@ export class BindingPackAssets implements PackAssetStore {
67
67
  let active: PackAssetStore | undefined;
68
68
  /** The bound store — the Bun shell's local tree unless a shell bound another. */
69
69
  export function getActivePackAssets(): PackAssetStore {
70
- if (active === undefined) active = new LocalPackAssets(new URL('../../twin/', import.meta.url).pathname);
70
+ if (active === undefined) active = new LocalPackAssets(async (vendor) => {
71
+ const { vendorPackageDir } = await import('./pack-paths.ts');
72
+ const { fileURLToPath } = await import('node:url');
73
+ return vendorPackageDir(process.cwd(), vendor) ?? vendorPackageDir(fileURLToPath(new URL('.', import.meta.url)), vendor);
74
+ });
71
75
  return active;
72
76
  }
73
77
  export function setActivePackAssets(store: PackAssetStore): PackAssetStore | undefined {