dsh-lcx-codex 0.4.2 → 0.4.3-pre.12

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 (49) hide show
  1. package/README.md +89 -205
  2. package/cordis.patch.yml +3 -20
  3. package/lib/client.js +309 -146
  4. package/lib/compact-v2.js +218 -199
  5. package/lib/dsh-compat.js +232 -100
  6. package/lib/dsh-responses.js +452 -277
  7. package/lib/grok-native-search.js +254 -0
  8. package/lib/index.js +1013 -757
  9. package/lib/json-store.js +50 -31
  10. package/lib/native-checkpoint.js +520 -194
  11. package/lib/responses-request.js +109 -121
  12. package/lib/responses-stream.js +1249 -447
  13. package/lib/route.js +411 -369
  14. package/lib/service-mutex.js +73 -64
  15. package/lib/token-budget.js +176 -108
  16. package/lib/transport.js +280 -68
  17. package/lib/types/client/index.d.ts +6 -0
  18. package/lib/types/compact-v2.d.ts +104 -0
  19. package/lib/types/dsh-compat.d.ts +78 -0
  20. package/lib/types/dsh-responses.d.ts +82 -0
  21. package/lib/types/grok-native-search.d.ts +34 -0
  22. package/lib/types/index.d.ts +83 -0
  23. package/lib/types/json-store.d.ts +10 -0
  24. package/lib/types/native-checkpoint.d.ts +213 -0
  25. package/lib/types/responses-request.d.ts +59 -0
  26. package/lib/types/responses-stream.d.ts +64 -0
  27. package/lib/types/route.d.ts +153 -0
  28. package/lib/types/service-mutex.d.ts +14 -0
  29. package/lib/types/token-budget.d.ts +50 -0
  30. package/lib/types/transport.d.ts +21 -0
  31. package/lib/types/web-run-output.d.ts +29 -0
  32. package/lib/types/web-search-alpha.d.ts +286 -0
  33. package/lib/types/web-search-capability.d.ts +26 -0
  34. package/lib/types/web-search-hosted.d.ts +246 -0
  35. package/lib/types/web-search-ref-store.d.ts +22 -0
  36. package/lib/web-run-output.js +167 -18
  37. package/lib/web-search-alpha.js +865 -163
  38. package/lib/web-search-capability.js +55 -65
  39. package/lib/web-search-hosted.js +210 -32
  40. package/lib/web-search-ref-store.js +63 -59
  41. package/package.json +89 -29
  42. package/ARCHITECTURE.md +0 -117
  43. package/CHANGELOG.md +0 -224
  44. package/README_EN.md +0 -277
  45. package/assets/dsh-lcx-codex-banner.jpg +0 -0
  46. package/lib/legacy-v3.js +0 -20
  47. package/lib/responses-replay.js +0 -68
  48. package/scripts/probe-alpha.mjs +0 -43
  49. package/scripts/validate-dsh-schema.mjs +0 -31
@@ -0,0 +1,254 @@
1
+ import { createHash } from "node:crypto";
2
+ export const GROK_NATIVE_SERVER_TOOL_TYPES = new Set([
3
+ "web_search_call",
4
+ "x_search_call",
5
+ ]);
6
+ const GROK_EXCLUDED_SEARCH_FUNCTIONS = new Set([
7
+ "web_search",
8
+ "websearch_gpt_advanced",
9
+ "websearch_alpha",
10
+ ]);
11
+ const GROK_NATIVE_VISIBLE_ITEM_TYPES = new Set([
12
+ "message",
13
+ "reasoning",
14
+ "function_call",
15
+ "custom_tool_call",
16
+ ]);
17
+ const GROK_NATIVE_REPLAY_MAX_ITEMS = 256;
18
+ const GROK_NATIVE_REPLAY_MAX_CHARS = 8 * 1024 * 1024;
19
+ function isRecord(value) {
20
+ return value !== null && typeof value === "object" && !Array.isArray(value);
21
+ }
22
+ function canonicalHeaders(headers) {
23
+ return Object.entries(headers ?? {})
24
+ .map(([name, value]) => [name.toLowerCase(), value])
25
+ .sort(([left], [right]) => left.localeCompare(right));
26
+ }
27
+ export function grokNativeReplayRouteFingerprint(route) {
28
+ return createHash("sha256")
29
+ .update(JSON.stringify({
30
+ provider: route.provider,
31
+ model: route.model,
32
+ sessionId: route.sessionId,
33
+ baseURL: route.baseURL.trim().replace(/\/+$/u, ""),
34
+ apiKeyEnv: route.apiKeyEnv,
35
+ headers: canonicalHeaders(route.headers),
36
+ }), "utf8")
37
+ .digest("hex");
38
+ }
39
+ export function grokNativeSearchEnabled(state) {
40
+ return state.web || state.x;
41
+ }
42
+ export function grokVisibleFunctionTools(tools, state = { web: false, x: false }) {
43
+ return tools?.filter((tool) => !GROK_EXCLUDED_SEARCH_FUNCTIONS.has(tool.name) &&
44
+ !(state.x && tool.name === "x_search"));
45
+ }
46
+ export function grokWireTools(tools, state) {
47
+ const nativeTypes = new Set(["web_search", "x_search"]);
48
+ const result = (tools ?? [])
49
+ .filter((tool) => {
50
+ if (!isRecord(tool))
51
+ return true;
52
+ if (nativeTypes.has(String(tool.type ?? "")))
53
+ return false;
54
+ return !(state.x && tool.type === "function" && tool.name === "x_search");
55
+ })
56
+ .map((tool) => structuredClone(tool))
57
+ .filter(isRecord);
58
+ if (state.web)
59
+ result.push({ type: "web_search" });
60
+ if (state.x)
61
+ result.push({ type: "x_search" });
62
+ return result;
63
+ }
64
+ function validVisibleItem(item) {
65
+ if (item.type === "message")
66
+ return typeof item.id === "string" && item.role === "assistant" &&
67
+ Array.isArray(item.content);
68
+ if (item.type === "reasoning")
69
+ return typeof item.id === "string" && Array.isArray(item.summary);
70
+ if (item.type === "function_call")
71
+ return typeof item.id === "string" && typeof item.call_id === "string" &&
72
+ typeof item.name === "string" && typeof item.arguments === "string";
73
+ if (item.type === "custom_tool_call")
74
+ return typeof item.id === "string" && typeof item.call_id === "string" &&
75
+ typeof item.name === "string" && typeof item.input === "string";
76
+ return false;
77
+ }
78
+ function readGrokNativeReplayEnvelope(value, route) {
79
+ if (!isRecord(value) ||
80
+ value.kind !== "xai-responses-native-search" ||
81
+ value.version !== 3)
82
+ return undefined;
83
+ if (value.provider !== route.provider ||
84
+ value.model !== route.model ||
85
+ value.sourceSessionId !== route.sessionId ||
86
+ value.routeAuthorityFingerprint !== grokNativeReplayRouteFingerprint(route) ||
87
+ !Array.isArray(value.output) ||
88
+ value.output.length === 0 ||
89
+ value.output.length > GROK_NATIVE_REPLAY_MAX_ITEMS)
90
+ return undefined;
91
+ let encoded;
92
+ try {
93
+ encoded = JSON.stringify(value.output);
94
+ }
95
+ catch {
96
+ return undefined;
97
+ }
98
+ if (encoded === undefined || encoded.length > GROK_NATIVE_REPLAY_MAX_CHARS)
99
+ return undefined;
100
+ const output = [];
101
+ const itemKinds = new Map();
102
+ const callIds = new Set();
103
+ for (const candidate of value.output) {
104
+ if (!isRecord(candidate) || typeof candidate.type !== "string")
105
+ return undefined;
106
+ const item = candidate;
107
+ if (GROK_NATIVE_SERVER_TOOL_TYPES.has(item.type)) {
108
+ if (typeof item.id !== "string" || item.id.length === 0)
109
+ return undefined;
110
+ }
111
+ else if (!GROK_NATIVE_VISIBLE_ITEM_TYPES.has(item.type) || !validVisibleItem(item)) {
112
+ return undefined;
113
+ }
114
+ if (typeof item.id === "string") {
115
+ const previousKind = itemKinds.get(item.id);
116
+ // Compatible Grok gateways can repeat a reasoning ID around server tools.
117
+ // These are ordered opaque items, never executable client calls.
118
+ if (previousKind !== undefined &&
119
+ !(previousKind === "reasoning" && item.type === "reasoning"))
120
+ return undefined;
121
+ itemKinds.set(item.id, item.type);
122
+ }
123
+ if (item.type === "function_call" || item.type === "custom_tool_call") {
124
+ const callId = String(item.call_id);
125
+ if (callIds.has(callId))
126
+ return undefined;
127
+ callIds.add(callId);
128
+ }
129
+ output.push(structuredClone(item));
130
+ }
131
+ if (output.length === 0)
132
+ return undefined;
133
+ return {
134
+ kind: "xai-responses-native-search",
135
+ version: 3,
136
+ provider: route.provider,
137
+ model: route.model,
138
+ sourceSessionId: route.sessionId,
139
+ routeAuthorityFingerprint: grokNativeReplayRouteFingerprint(route),
140
+ output,
141
+ };
142
+ }
143
+ export function createGrokNativeReplayEnvelope(output, route) {
144
+ if (!route || !Array.isArray(output))
145
+ return undefined;
146
+ return readGrokNativeReplayEnvelope({
147
+ kind: "xai-responses-native-search",
148
+ version: 3,
149
+ provider: route.provider,
150
+ model: route.model,
151
+ sourceSessionId: route.sessionId,
152
+ routeAuthorityFingerprint: grokNativeReplayRouteFingerprint(route),
153
+ output,
154
+ }, route);
155
+ }
156
+ function itemText(item) {
157
+ if (item.type === "message")
158
+ return (Array.isArray(item.content) ? item.content : [])
159
+ .map((part) => isRecord(part) ? String(part.text ?? part.refusal ?? "") : "")
160
+ .join("");
161
+ if (item.type === "reasoning") {
162
+ const parts = Array.isArray(item.summary) && item.summary.length > 0
163
+ ? item.summary
164
+ : Array.isArray(item.content) ? item.content : [];
165
+ return parts.map((part) => isRecord(part) ? String(part.text ?? "") : "").join("\n\n");
166
+ }
167
+ return "";
168
+ }
169
+ function visibleItemMatchesBlock(item, block) {
170
+ if (item.type === "message")
171
+ return block.type === "text" && block.text === itemText(item);
172
+ if (item.type === "reasoning")
173
+ return block.type === "reasoning" && block.text === itemText(item);
174
+ if (item.type === "function_call")
175
+ return block.type === "tool-call" && block.id === `${String(item.call_id)}|${String(item.id)}` &&
176
+ block.name === item.name && block.arguments === item.arguments;
177
+ if (item.type === "custom_tool_call")
178
+ return block.type === "tool-call" && block.id === `${String(item.call_id)}|${String(item.id)}` &&
179
+ block.name === item.name;
180
+ return false;
181
+ }
182
+ function visibleItemMatchesInput(item, candidate) {
183
+ if (!isRecord(candidate) || candidate.type !== item.type)
184
+ return false;
185
+ if (candidate.id !== item.id)
186
+ return false;
187
+ if (item.type === "function_call" || item.type === "custom_tool_call")
188
+ return candidate.call_id === item.call_id;
189
+ return true;
190
+ }
191
+ function replayForMessage(message, route) {
192
+ if (message.role !== "assistant" || message.source.kind !== "model")
193
+ return undefined;
194
+ if (message.source.provider !== route.provider || message.source.model !== route.model)
195
+ return undefined;
196
+ const replayState = message.source.replayState;
197
+ if (!isRecord(replayState))
198
+ return undefined;
199
+ const envelope = readGrokNativeReplayEnvelope(replayState.grokNative, route);
200
+ if (!envelope)
201
+ return undefined;
202
+ const visible = envelope.output.filter((item) => !GROK_NATIVE_SERVER_TOOL_TYPES.has(item.type));
203
+ if (visible.length === 0)
204
+ return undefined;
205
+ const reasoningCounts = new Map();
206
+ for (const item of visible)
207
+ if (item.type === "reasoning")
208
+ reasoningCounts.set(String(item.id), (reasoningCounts.get(String(item.id)) ?? 0) + 1);
209
+ const anchors = [];
210
+ let blockIndex = 0;
211
+ for (const item of visible) {
212
+ const block = message.content[blockIndex];
213
+ if (isRecord(block) && visibleItemMatchesBlock(item, block)) {
214
+ anchors.push(item);
215
+ blockIndex += 1;
216
+ }
217
+ else if (!(item.type === "reasoning" &&
218
+ (!itemText(item).trim() || (reasoningCounts.get(String(item.id)) ?? 0) > 1))) {
219
+ return undefined;
220
+ }
221
+ }
222
+ // Empty Grok reasoning has no UI block. Pi can coalesce repeated reasoning IDs
223
+ // in terminal-only gateway responses; every actual DSH-visible block must still match.
224
+ if (anchors.length === 0)
225
+ return undefined;
226
+ if (blockIndex !== message.content.length)
227
+ return undefined;
228
+ return { envelope, visible: anchors };
229
+ }
230
+ export function restoreGrokNativeReplay(input, messages, route) {
231
+ const restored = structuredClone(input);
232
+ if (!route)
233
+ return restored;
234
+ let cursor = 0;
235
+ for (const message of messages) {
236
+ const replay = replayForMessage(message, route);
237
+ if (!replay)
238
+ continue;
239
+ const { envelope, visible } = replay;
240
+ const replacedLength = visible.length;
241
+ let start = -1;
242
+ for (let index = cursor; index <= restored.length - replacedLength; index += 1) {
243
+ if (visible.every((item, offset) => visibleItemMatchesInput(item, restored[index + offset]))) {
244
+ start = index;
245
+ break;
246
+ }
247
+ }
248
+ if (start < 0)
249
+ continue;
250
+ restored.splice(start, replacedLength, ...structuredClone(envelope.output));
251
+ cursor = start + envelope.output.length;
252
+ }
253
+ return restored;
254
+ }