@juspay/neurolink 12.34.3 → 12.35.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.
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Exhaustiveness enforcement for the translation IR.
3
+ *
4
+ * A translation layer fails quietly: an unhandled content part or event variant
5
+ * becomes a missing tool call, and the only symptom is the next hop rejecting a
6
+ * request nobody can explain. `assertProxyIRExhaustive` makes that a compile
7
+ * error instead. Passing a value whose type is not `never` does not type-check,
8
+ * so a new IR variant breaks every `switch` that has not handled it.
9
+ *
10
+ * It throws at runtime as well, because the compile-time guarantee only holds for
11
+ * values that really are typed. A wire payload narrowed from `unknown` can reach a
12
+ * default branch at runtime, and failing loudly there beats translating a request
13
+ * with a silently dropped element.
14
+ */
15
+ import type { ProxyIRContentPart, ProxyIRResponseEvent, ProxyIRTerminalOutcome, ProxyIRUnmappableValue } from "../types/index.js";
16
+ /**
17
+ * Refuse an unhandled IR variant.
18
+ *
19
+ * `context` names the switch that failed, since the value alone rarely says which
20
+ * of several exhaustive switches over the same union let it through.
21
+ */
22
+ export declare function assertProxyIRExhaustive(value: never, context: string): never;
23
+ /** Mark a wire value no codec could represent, preserving it for the caller. */
24
+ export declare function proxyIRUnmappable(sourceKind: string, reason: string, raw: unknown): ProxyIRUnmappableValue;
25
+ /** Narrow a collected codec result to the unmappable marker. */
26
+ export declare function isProxyIRUnmappable(value: unknown): value is ProxyIRUnmappableValue;
27
+ /**
28
+ * Name a content part for a diagnostic.
29
+ *
30
+ * This lives in `src` rather than in the suite that asserts on it because
31
+ * `tsconfig.json` excludes `test`: an exhaustive switch written only in a test
32
+ * gives no compile-time guarantee at all. These describers are the shipped
33
+ * consumers that pin every union, so adding an IR variant fails `tsc --noEmit`
34
+ * here before it can reach a codec that silently drops it.
35
+ *
36
+ * Deliberately excludes the payload of text, thinking and tool arguments: a
37
+ * describer's output reaches logs and span attributes, and prompt content does
38
+ * not belong there.
39
+ */
40
+ export declare function describeProxyIRPart(part: ProxyIRContentPart): string;
41
+ /** Name a stream event for a diagnostic, without its payload. */
42
+ export declare function describeProxyIRResponseEvent(event: ProxyIRResponseEvent): string;
43
+ /** Name a terminal outcome, keeping an unmodelled upstream stop reason visible. */
44
+ export declare function describeProxyIRTerminalOutcome(outcome: ProxyIRTerminalOutcome): string;
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Exhaustiveness enforcement for the translation IR.
3
+ *
4
+ * A translation layer fails quietly: an unhandled content part or event variant
5
+ * becomes a missing tool call, and the only symptom is the next hop rejecting a
6
+ * request nobody can explain. `assertProxyIRExhaustive` makes that a compile
7
+ * error instead. Passing a value whose type is not `never` does not type-check,
8
+ * so a new IR variant breaks every `switch` that has not handled it.
9
+ *
10
+ * It throws at runtime as well, because the compile-time guarantee only holds for
11
+ * values that really are typed. A wire payload narrowed from `unknown` can reach a
12
+ * default branch at runtime, and failing loudly there beats translating a request
13
+ * with a silently dropped element.
14
+ */
15
+ /**
16
+ * Refuse an unhandled IR variant.
17
+ *
18
+ * `context` names the switch that failed, since the value alone rarely says which
19
+ * of several exhaustive switches over the same union let it through.
20
+ */
21
+ export function assertProxyIRExhaustive(value, context) {
22
+ throw new Error(`[proxy-ir] ${context} did not handle variant: ${describeProxyIRVariant(value)}`);
23
+ }
24
+ /**
25
+ * Describe a value for the exhaustiveness error without assuming its shape.
26
+ *
27
+ * The parameter is `never` at the call site, so nothing about it can be trusted
28
+ * here: it may be a primitive, or an object with no discriminant at all — and an
29
+ * untagged object is exactly the shape a malformed wire payload takes. Reviewed
30
+ * finding (PR #1826): serializing that object's fields defeated the point of a
31
+ * log/span-safe diagnostic, since the object can carry the same text or tool
32
+ * arguments the typed describers deliberately withhold. The fallback is now a
33
+ * fixed, shape-only marker — never the object's own content — for every
34
+ * untagged case, not only the ones this file happens to call today.
35
+ */
36
+ function describeProxyIRVariant(value) {
37
+ if (value === null || typeof value !== "object") {
38
+ return String(value);
39
+ }
40
+ const record = value;
41
+ for (const discriminant of ["kind", "status", "source", "format"]) {
42
+ const tag = record[discriminant];
43
+ if (typeof tag === "string") {
44
+ return `${discriminant}=${tag}`;
45
+ }
46
+ }
47
+ return "[object without recognized discriminant]";
48
+ }
49
+ /** Mark a wire value no codec could represent, preserving it for the caller. */
50
+ export function proxyIRUnmappable(sourceKind, reason, raw) {
51
+ return { proxyIRUnmappable: true, sourceKind, reason, raw };
52
+ }
53
+ /** Narrow a collected codec result to the unmappable marker. */
54
+ export function isProxyIRUnmappable(value) {
55
+ return (value !== null &&
56
+ typeof value === "object" &&
57
+ value.proxyIRUnmappable === true);
58
+ }
59
+ /**
60
+ * Name a content part for a diagnostic.
61
+ *
62
+ * This lives in `src` rather than in the suite that asserts on it because
63
+ * `tsconfig.json` excludes `test`: an exhaustive switch written only in a test
64
+ * gives no compile-time guarantee at all. These describers are the shipped
65
+ * consumers that pin every union, so adding an IR variant fails `tsc --noEmit`
66
+ * here before it can reach a codec that silently drops it.
67
+ *
68
+ * Deliberately excludes the payload of text, thinking and tool arguments: a
69
+ * describer's output reaches logs and span attributes, and prompt content does
70
+ * not belong there.
71
+ */
72
+ export function describeProxyIRPart(part) {
73
+ switch (part.kind) {
74
+ case "text":
75
+ return `text(${part.text.length}b)`;
76
+ case "thinking":
77
+ return `thinking(${part.text.length}b)`;
78
+ case "image":
79
+ return `image(${part.encoding})`;
80
+ case "tool_call":
81
+ return `tool_call(${part.toolName})`;
82
+ case "tool_result":
83
+ return `tool_result(${part.content.length} parts${part.isError ? ", error" : ""})`;
84
+ case "unmapped":
85
+ return `unmapped(${part.sourceKind}: ${part.reason})`;
86
+ default:
87
+ return assertProxyIRExhaustive(part, "describeProxyIRPart");
88
+ }
89
+ }
90
+ /** Name a stream event for a diagnostic, without its payload. */
91
+ export function describeProxyIRResponseEvent(event) {
92
+ switch (event.kind) {
93
+ case "text_delta":
94
+ return `text_delta(${event.text.length}b)`;
95
+ case "thinking_delta":
96
+ return `thinking_delta(${event.text.length}b)`;
97
+ case "tool_call_start":
98
+ return `tool_call_start(${event.toolName})`;
99
+ case "tool_call_arguments_delta":
100
+ return `tool_call_arguments_delta(${event.partialArguments.length}b)`;
101
+ case "tool_call_done":
102
+ return "tool_call_done";
103
+ case "item_done":
104
+ return "item_done";
105
+ case "usage":
106
+ return "usage";
107
+ case "terminal":
108
+ return `terminal(${describeProxyIRTerminalOutcome(event.outcome)})`;
109
+ default:
110
+ return assertProxyIRExhaustive(event, "describeProxyIRResponseEvent");
111
+ }
112
+ }
113
+ /** Name a terminal outcome, keeping an unmodelled upstream stop reason visible. */
114
+ export function describeProxyIRTerminalOutcome(outcome) {
115
+ switch (outcome.status) {
116
+ case "completed":
117
+ return outcome.finishReason === "other"
118
+ ? `completed:${outcome.rawFinishReason}`
119
+ : `completed:${outcome.finishReason}`;
120
+ case "failed":
121
+ return `failed:${outcome.error.code}`;
122
+ case "incomplete":
123
+ return `incomplete:${outcome.reason}`;
124
+ default:
125
+ return assertProxyIRExhaustive(outcome, "describeProxyIRTerminalOutcome");
126
+ }
127
+ }
@@ -55,6 +55,7 @@ export * from "./providerCatalog.js";
55
55
  export * from "./providerCatalog.generated.js";
56
56
  export * from "./providers.js";
57
57
  export * from "./proxy.js";
58
+ export * from "./proxyIR.js";
58
59
  export * from "./proxyClient.js";
59
60
  export * from "./proxyContext.js";
60
61
  export * from "./rag.js";
@@ -59,6 +59,7 @@ export * from "./providerCatalog.js";
59
59
  export * from "./providerCatalog.generated.js";
60
60
  export * from "./providers.js";
61
61
  export * from "./proxy.js";
62
+ export * from "./proxyIR.js";
62
63
  export * from "./proxyClient.js";
63
64
  export * from "./proxyContext.js";
64
65
  export * from "./rag.js";
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Wire-neutral intermediate representation for proxy protocol translation.
3
+ *
4
+ * Translation between provider wire formats was previously pairwise and ad hoc:
5
+ * `codexFallback.ts` converts an Anthropic request into a Codex one and casts a
6
+ * role to reach Claude Code's inline-system convention inside a converter that is
7
+ * meant to be neutral. Adding the reverse direction pairwise would double that,
8
+ * and a third provider would square it.
9
+ *
10
+ * So each format gets a codec to and from this IR, and adding a provider is a new
11
+ * codec rather than new branching. The IR is deliberately not `ClaudeRequest`:
12
+ * that type is simultaneously the literal Anthropic wire shape, so using it as
13
+ * the hub makes every other provider's quirks leak into the Anthropic hot path.
14
+ * `ClaudeRequest` and `ClaudeResponse` stay untouched.
15
+ *
16
+ * Every union here is discriminated on a literal `kind`, `status` or `source`, so
17
+ * `assertProxyIRExhaustive` turns an unhandled variant into a compile error. That
18
+ * is the point of the IR: a field that cannot be represented must be stated, via
19
+ * `ProxyIRUnmappedPart`, rather than dropped silently.
20
+ */
21
+ import type { CodexReasoningEffort } from "./codex.js";
22
+ import type { UsageContext } from "./proxy.js";
23
+ /**
24
+ * A wire dialect a codec can speak.
25
+ *
26
+ * Distinct from `ProxyFormat` ("claude" | "openai" | "gemini"), which selects a
27
+ * request-translation surface for a different subsystem. These must not be
28
+ * conflated: this one names the two dialects the fallback codecs translate.
29
+ */
30
+ export type ProxyIRWireFormat = "anthropic-messages" | "codex-responses";
31
+ /**
32
+ * Message roles across both dialects.
33
+ *
34
+ * `developer` has no Anthropic equivalent and is native Codex's own role for
35
+ * instruction blocks that sit inside the ordered input array rather than in a
36
+ * leading system field. It is kept distinct from `system` so a codec targeting
37
+ * Anthropic can decide where it belongs instead of inheriting a lossy merge.
38
+ */
39
+ export type ProxyIRRole = "system" | "developer" | "user" | "assistant";
40
+ /**
41
+ * A request to cache the prefix up to and including the annotated element.
42
+ *
43
+ * Only meaningful for Anthropic, whose caching is explicit and breakpoint-driven.
44
+ * A codec targeting a dialect with automatic caching ignores it, which is correct
45
+ * rather than lossy: nothing on that wire can carry it.
46
+ */
47
+ export type ProxyIRCacheHint = {
48
+ ttl?: "5m" | "1h";
49
+ };
50
+ export type ProxyIRTextPart = {
51
+ kind: "text";
52
+ text: string;
53
+ cacheHint?: ProxyIRCacheHint;
54
+ };
55
+ /** `signature` is reserved for Anthropic's signed thinking blocks and unused today. */
56
+ export type ProxyIRThinkingPart = {
57
+ kind: "thinking";
58
+ text: string;
59
+ signature?: string;
60
+ };
61
+ export type ProxyIRImagePart = {
62
+ kind: "image";
63
+ encoding: "base64" | "url";
64
+ mediaType?: string;
65
+ data: string;
66
+ };
67
+ /**
68
+ * A model-emitted call to a tool.
69
+ *
70
+ * `argumentsRaw` is authoritative and always present; `argumentsJson` is a
71
+ * convenience for the common case where the raw text parses. Keeping the raw text
72
+ * means a call whose arguments are streamed in fragments, or are not valid JSON,
73
+ * survives translation instead of being normalised into something the next hop
74
+ * never sent.
75
+ */
76
+ export type ProxyIRToolCallPart = {
77
+ kind: "tool_call";
78
+ callId: string;
79
+ toolName: string;
80
+ argumentsRaw: string;
81
+ argumentsJson?: Record<string, unknown>;
82
+ };
83
+ export type ProxyIRToolResultPart = {
84
+ kind: "tool_result";
85
+ callId: string;
86
+ content: (ProxyIRTextPart | ProxyIRImagePart)[];
87
+ isError?: boolean;
88
+ cacheHint?: ProxyIRCacheHint;
89
+ };
90
+ /**
91
+ * A wire element no codec could represent, carried forward verbatim.
92
+ *
93
+ * The alternative is dropping it, which is how a translation layer loses tool
94
+ * calls: silently, and only visibly as a downstream rejection. `raw` keeps the
95
+ * original so a renderer can pass it through or a test can assert on it, and
96
+ * `reason` records why it could not be mapped.
97
+ */
98
+ export type ProxyIRUnmappedPart = {
99
+ kind: "unmapped";
100
+ sourceKind: string;
101
+ reason: string;
102
+ raw: unknown;
103
+ };
104
+ export type ProxyIRContentPart = ProxyIRTextPart | ProxyIRThinkingPart | ProxyIRImagePart | ProxyIRToolCallPart | ProxyIRToolResultPart | ProxyIRUnmappedPart;
105
+ /**
106
+ * One message, with system and developer messages kept in sequence.
107
+ *
108
+ * There is no separate bucket for leading instructions: native Codex sends
109
+ * instruction blocks as ordered `developer` items inside the input array, and
110
+ * lifting them out would lose their position relative to the conversation. A
111
+ * codec that needs a leading system field extracts it; the IR does not presume
112
+ * one exists.
113
+ */
114
+ export type ProxyIRMessage = {
115
+ role: ProxyIRRole;
116
+ content: ProxyIRContentPart[];
117
+ };
118
+ export type ProxyIRFunctionToolDeclaration = {
119
+ kind: "function";
120
+ name: string;
121
+ description?: string;
122
+ parametersSchema: Record<string, unknown>;
123
+ cacheHint?: ProxyIRCacheHint;
124
+ };
125
+ /** A tool constrained by a grammar rather than a JSON schema; Codex-only today. */
126
+ export type ProxyIRCustomGrammarToolDeclaration = {
127
+ kind: "custom_grammar";
128
+ name: string;
129
+ description?: string;
130
+ grammar: string;
131
+ };
132
+ export type ProxyIRToolDeclaration = ProxyIRFunctionToolDeclaration | ProxyIRCustomGrammarToolDeclaration;
133
+ export type ProxyIRToolChoice = {
134
+ kind: "auto";
135
+ } | {
136
+ kind: "none";
137
+ } | {
138
+ kind: "any";
139
+ } | {
140
+ kind: "named";
141
+ toolName: string;
142
+ };
143
+ /**
144
+ * Reasoning configuration, tagged by the dialect that expressed it.
145
+ *
146
+ * Anthropic budgets thinking in tokens; Codex selects a named effort level. There
147
+ * is no faithful numeric mapping between them, so the source is recorded and each
148
+ * codec decides how to honour the other's form rather than a lossy conversion
149
+ * happening once, invisibly, in the middle.
150
+ */
151
+ export type ProxyIRReasoning = {
152
+ source: "none";
153
+ } | {
154
+ source: "anthropic_thinking";
155
+ type: string;
156
+ budgetTokens?: number;
157
+ } | {
158
+ source: "codex_effort";
159
+ effort: CodexReasoningEffort;
160
+ };
161
+ export type ProxyIRRequest = {
162
+ sourceFormat: ProxyIRWireFormat;
163
+ sourceModel: string;
164
+ originSessionId?: string;
165
+ originThreadId?: string;
166
+ messages: ProxyIRMessage[];
167
+ tools: ProxyIRToolDeclaration[];
168
+ toolChoice?: ProxyIRToolChoice;
169
+ reasoning: ProxyIRReasoning;
170
+ stream: boolean;
171
+ /** Prefix-derived routing hint. Never serialized onto any wire. */
172
+ promptCachePrefixKey?: string;
173
+ };
174
+ export type ProxyIRRequestTargetOptions = {
175
+ model: string;
176
+ reasoningEffort?: CodexReasoningEffort;
177
+ };
178
+ export type ProxyIRProviderError = {
179
+ code: string;
180
+ httpStatus?: number;
181
+ retryable?: boolean;
182
+ message?: string;
183
+ };
184
+ /**
185
+ * How a response ended.
186
+ *
187
+ * `finishReason: "other"` carries `rawFinishReason` because Anthropic's
188
+ * `stop_reason` is typed `string | null` on the wire, deliberately open. A closed
189
+ * union here would make any legitimate but unmodelled stop reason an
190
+ * exhaustiveness throw at runtime, turning a successful upstream response into a
191
+ * proxy failure.
192
+ */
193
+ export type ProxyIRTerminalOutcome = {
194
+ status: "completed";
195
+ finishReason: "end_turn" | "tool_use" | "max_tokens" | "stop_sequence";
196
+ } | {
197
+ status: "completed";
198
+ finishReason: "other";
199
+ rawFinishReason: string;
200
+ } | {
201
+ status: "failed";
202
+ error: ProxyIRProviderError;
203
+ } | {
204
+ status: "incomplete";
205
+ reason: string;
206
+ };
207
+ export type ProxyIRResponseEvent = {
208
+ kind: "text_delta";
209
+ text: string;
210
+ } | {
211
+ kind: "thinking_delta";
212
+ text: string;
213
+ } | {
214
+ kind: "tool_call_start";
215
+ callId: string;
216
+ toolName: string;
217
+ } | {
218
+ kind: "tool_call_arguments_delta";
219
+ callId: string;
220
+ partialArguments: string;
221
+ } | {
222
+ kind: "tool_call_done";
223
+ callId: string;
224
+ } | {
225
+ kind: "item_done";
226
+ } | {
227
+ kind: "usage";
228
+ usage: UsageContext;
229
+ } | {
230
+ kind: "terminal";
231
+ outcome: ProxyIRTerminalOutcome;
232
+ };
233
+ export type ProxyIROutputItem = {
234
+ kind: "text";
235
+ text: string;
236
+ } | {
237
+ kind: "thinking";
238
+ text: string;
239
+ } | {
240
+ kind: "tool_call";
241
+ callId: string;
242
+ toolName: string;
243
+ argumentsRaw: string;
244
+ argumentsJson?: Record<string, unknown>;
245
+ };
246
+ export type ProxyIRResponse = {
247
+ items: ProxyIROutputItem[];
248
+ usage?: UsageContext;
249
+ outcome: ProxyIRTerminalOutcome;
250
+ };
251
+ /**
252
+ * Codec capabilities, kept separate rather than bundled into one interface.
253
+ *
254
+ * No dialect needs all four in every direction: for outbound Codex fallback, the
255
+ * Codex side is only ever parsed from and rendered to, never dispatched to, so a
256
+ * single monolithic codec type would force stub implementations whose only
257
+ * possible body is a throw.
258
+ */
259
+ export type ProxyIRRequestParser<TWireRequest> = {
260
+ format: ProxyIRWireFormat;
261
+ parseRequest(wire: TWireRequest): ProxyIRRequest;
262
+ };
263
+ export type ProxyIRRequestBuilder<TWireRequest> = {
264
+ format: ProxyIRWireFormat;
265
+ buildRequest(ir: ProxyIRRequest, target: ProxyIRRequestTargetOptions): TWireRequest;
266
+ };
267
+ export type ProxyIRResponseParser = {
268
+ format: ProxyIRWireFormat;
269
+ parseBufferedResponse(wireText: string): ProxyIRResponse;
270
+ parseResponseStream(upstream: Response): AsyncGenerator<ProxyIRResponseEvent, ProxyIRResponse>;
271
+ };
272
+ export type ProxyIRResponseRenderer = {
273
+ format: ProxyIRWireFormat;
274
+ renderResponseEvent(event: ProxyIRResponseEvent): string[];
275
+ renderTerminalOutcome(outcome: ProxyIRTerminalOutcome, usage?: UsageContext): string[];
276
+ };
277
+ /** Marks a value a codec refused to map, for callers that collect rather than throw. */
278
+ export type ProxyIRUnmappableValue = {
279
+ proxyIRUnmappable: true;
280
+ sourceKind: string;
281
+ reason: string;
282
+ raw: unknown;
283
+ };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Wire-neutral intermediate representation for proxy protocol translation.
3
+ *
4
+ * Translation between provider wire formats was previously pairwise and ad hoc:
5
+ * `codexFallback.ts` converts an Anthropic request into a Codex one and casts a
6
+ * role to reach Claude Code's inline-system convention inside a converter that is
7
+ * meant to be neutral. Adding the reverse direction pairwise would double that,
8
+ * and a third provider would square it.
9
+ *
10
+ * So each format gets a codec to and from this IR, and adding a provider is a new
11
+ * codec rather than new branching. The IR is deliberately not `ClaudeRequest`:
12
+ * that type is simultaneously the literal Anthropic wire shape, so using it as
13
+ * the hub makes every other provider's quirks leak into the Anthropic hot path.
14
+ * `ClaudeRequest` and `ClaudeResponse` stay untouched.
15
+ *
16
+ * Every union here is discriminated on a literal `kind`, `status` or `source`, so
17
+ * `assertProxyIRExhaustive` turns an unhandled variant into a compile error. That
18
+ * is the point of the IR: a field that cannot be represented must be stated, via
19
+ * `ProxyIRUnmappedPart`, rather than dropped silently.
20
+ */
21
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "12.34.3",
3
+ "version": "12.35.0",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002/call) for routing, tool selection and context budgeting. MCP-native (4 transports), voice TTS/STT/realtime, RAG, agents, memory, compaction, 9 observability exporters. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · TypeSafe Jev and more.",
6
6
  "author": {