@juspay/neurolink 12.12.16 → 12.14.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.
- package/CHANGELOG.md +3 -3
- package/dist/browser/neurolink.min.js +381 -381
- package/dist/cli/commands/proxy.js +11 -9
- package/dist/cli/proxy-clients/grok.d.ts +45 -0
- package/dist/cli/proxy-clients/grok.js +342 -0
- package/dist/cli/proxy-clients/registry.d.ts +2 -2
- package/dist/cli/proxy-clients/registry.js +4 -2
- package/dist/constants/proxyModels.d.ts +7 -1
- package/dist/constants/proxyModels.js +7 -1
- package/dist/core/baseProvider.d.ts +10 -0
- package/dist/core/baseProvider.js +21 -0
- package/dist/core/constants.d.ts +14 -1
- package/dist/core/constants.js +18 -1
- package/dist/core/loopEngine.js +150 -7
- package/dist/core/toolExecutionGuards.js +6 -1
- package/dist/providers/amazonBedrock/client.js +10 -0
- package/dist/providers/anthropic/client.d.ts +8 -0
- package/dist/providers/anthropic/client.js +132 -3
- package/dist/providers/anthropic/loopAdapter.js +352 -244
- package/dist/providers/googleAiStudio/client.js +12 -0
- package/dist/providers/googleVertex/client.js +19 -5
- package/dist/proxy/clientAttribution.js +3 -0
- package/dist/proxy/proxyConfig.d.ts +4 -0
- package/dist/proxy/proxyConfig.js +13 -1
- package/dist/types/generate.d.ts +22 -9
- package/dist/types/loopEngine.d.ts +97 -2
- package/dist/types/proxyClient.d.ts +34 -1
- package/dist/types/stream.d.ts +141 -5
- package/dist/utils/parameterValidation.d.ts +30 -0
- package/dist/utils/parameterValidation.js +101 -0
- package/dist/utils/timeout.js +25 -2
- package/package.json +2 -1
|
@@ -8,7 +8,7 @@ import { BaseProvider } from "../../core/baseProvider.js";
|
|
|
8
8
|
import { unwrapImagePayload } from "../../adapters/imageFormatSupport.js";
|
|
9
9
|
import { appendNativeAudioParts } from "../googleNativeGemini3/utils.js";
|
|
10
10
|
import { getMimeTypeForExtension } from "../../processors/config/mimeConstants.js";
|
|
11
|
-
import { DEFAULT_GEMINI_STREAM_TIMEOUT_MS, DEFAULT_MAX_STEPS,
|
|
11
|
+
import { DEFAULT_GEMINI_STREAM_TIMEOUT_MS, DEFAULT_MAX_STEPS, DEFAULT_TOOL_MAX_RETRIES, GLOBAL_LOCATION_MODELS, resolveToolTimeoutMs, TOOL_STORAGE_TIMEOUT_MS, } from "../../core/constants.js";
|
|
12
12
|
import { resolveRequestKind } from "../../core/resolveRequestKind.js";
|
|
13
13
|
import { ModelConfigurationManager } from "../../core/modelConfiguration.js";
|
|
14
14
|
import { isSchemaComplexityError } from "../../core/modules/structuredOutputPolicy.js";
|
|
@@ -1560,7 +1560,7 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1560
1560
|
// (whole-turn deadline + optional stall detector) all trip it, and every
|
|
1561
1561
|
// request/tool-exec receives effectiveSignal.
|
|
1562
1562
|
const streamTimeoutMs = parseTimeout(options.timeout) ?? DEFAULT_GEMINI_STREAM_TIMEOUT_MS;
|
|
1563
|
-
const toolExecTimeoutMs = options.toolTimeoutMs
|
|
1563
|
+
const toolExecTimeoutMs = resolveToolTimeoutMs(options.toolTimeoutMs);
|
|
1564
1564
|
const effectiveTurnDeadlineMs = options.turnTimeoutMs ?? streamTimeoutMs;
|
|
1565
1565
|
const internalAbort = new AbortController();
|
|
1566
1566
|
const onCallerAbort = () => internalAbort.abort();
|
|
@@ -1790,6 +1790,10 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
1790
1790
|
onProgress: () => turnClock.noteProgress(),
|
|
1791
1791
|
}),
|
|
1792
1792
|
abortSignal: effectiveSignal,
|
|
1793
|
+
// The engine bounds every tool call itself. Passing the value this
|
|
1794
|
+
// loop already gave `buildDedupedEngineTools` keeps the engine's
|
|
1795
|
+
// backstop from being tighter than what the caller asked for.
|
|
1796
|
+
toolTimeoutMs: toolExecTimeoutMs,
|
|
1793
1797
|
});
|
|
1794
1798
|
// Collected, NOT forwarded to the consumer here. This loop replays the
|
|
1795
1799
|
// gathered text after the turn rather than streaming it live, and a
|
|
@@ -2356,7 +2360,7 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
2356
2360
|
// (whole-turn deadline + optional stall detector) all trip it, and every
|
|
2357
2361
|
// request/tool-exec receives effectiveSignal.
|
|
2358
2362
|
const streamTimeoutMs = parseTimeout(options.timeout) ?? DEFAULT_GEMINI_STREAM_TIMEOUT_MS;
|
|
2359
|
-
const toolExecTimeoutMs = options.toolTimeoutMs
|
|
2363
|
+
const toolExecTimeoutMs = resolveToolTimeoutMs(options.toolTimeoutMs);
|
|
2360
2364
|
const effectiveTurnDeadlineMs = options.turnTimeoutMs ?? streamTimeoutMs;
|
|
2361
2365
|
const internalAbort = new AbortController();
|
|
2362
2366
|
const onCallerAbort = () => internalAbort.abort();
|
|
@@ -2585,6 +2589,10 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
2585
2589
|
onProgress: () => turnClock.noteProgress(),
|
|
2586
2590
|
}),
|
|
2587
2591
|
abortSignal: effectiveSignal,
|
|
2592
|
+
// The engine bounds every tool call itself. Passing the value this
|
|
2593
|
+
// loop already gave `buildDedupedEngineTools` keeps the engine's
|
|
2594
|
+
// backstop from being tighter than what the caller asked for.
|
|
2595
|
+
toolTimeoutMs: toolExecTimeoutMs,
|
|
2588
2596
|
});
|
|
2589
2597
|
// generate() returns one result rather than streaming, so the engine's
|
|
2590
2598
|
// chunks are drained and discarded — the answer comes off the turn's
|
|
@@ -3304,7 +3312,7 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
3304
3312
|
if (options.abortSignal?.aborted) {
|
|
3305
3313
|
internalAbort.abort();
|
|
3306
3314
|
}
|
|
3307
|
-
const toolExecTimeoutMs = options.toolTimeoutMs
|
|
3315
|
+
const toolExecTimeoutMs = resolveToolTimeoutMs(options.toolTimeoutMs);
|
|
3308
3316
|
const effectiveTurnDeadlineMs = options.turnTimeoutMs ?? streamTimeoutMs;
|
|
3309
3317
|
// Whole-turn deadline + optional stall watchdog. When the caller sets no
|
|
3310
3318
|
// explicit turn budget, keep the pre-existing defensive bound
|
|
@@ -3668,6 +3676,9 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
3668
3676
|
tools: engineTools,
|
|
3669
3677
|
abortSignal: internalAbort.signal,
|
|
3670
3678
|
...(activeSpan ? { span: activeSpan } : {}),
|
|
3679
|
+
// Same value `guardToolExecutor` already received above, so the
|
|
3680
|
+
// engine's own per-tool bound cannot undercut it.
|
|
3681
|
+
toolTimeoutMs: toolExecTimeoutMs,
|
|
3671
3682
|
});
|
|
3672
3683
|
const pump = (async () => {
|
|
3673
3684
|
for await (const chunk of engineStream) {
|
|
@@ -4474,7 +4485,7 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
4474
4485
|
if (options.abortSignal?.aborted) {
|
|
4475
4486
|
internalAbort.abort();
|
|
4476
4487
|
}
|
|
4477
|
-
const toolExecTimeoutMs = options.toolTimeoutMs
|
|
4488
|
+
const toolExecTimeoutMs = resolveToolTimeoutMs(options.toolTimeoutMs);
|
|
4478
4489
|
// Whole-turn deadline + optional stall watchdog. NOTE: unlike the stream
|
|
4479
4490
|
// twin, this path historically had NO whole-turn bound (only the per-call
|
|
4480
4491
|
// withTimeout) — so no defensive default is introduced here: without an
|
|
@@ -4668,6 +4679,9 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
4668
4679
|
const { stream: engineStream, resultPromise } = runAgenticLoop(adapter, currentMessages.slice(), {
|
|
4669
4680
|
tools: engineTools,
|
|
4670
4681
|
abortSignal: internalAbort.signal,
|
|
4682
|
+
// Same value `guardToolExecutor` already received above, so the
|
|
4683
|
+
// engine's own per-tool bound cannot undercut it.
|
|
4684
|
+
toolTimeoutMs: toolExecTimeoutMs,
|
|
4671
4685
|
});
|
|
4672
4686
|
// Drained and discarded: generate() returns one result rather than
|
|
4673
4687
|
// streaming, and the per-step text is accumulated in
|
|
@@ -39,6 +39,9 @@ const CLIENT_PREFIXES = [
|
|
|
39
39
|
// different token, and it is left unmapped until someone measures it rather
|
|
40
40
|
// than pattern-matched on a guess.
|
|
41
41
|
["codex_exec/", "codex"],
|
|
42
|
+
// "grok-shell/1.0.30 (macos; aarch64)" — measured 2026-09-12 on
|
|
43
|
+
// POST /v1/messages from Grok Build 1.0.30.
|
|
44
|
+
["grok-shell/", "grok"],
|
|
42
45
|
];
|
|
43
46
|
/**
|
|
44
47
|
* Deliberately NOT mapped, and why each was ruled out.
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
* falls back to JSON.parse.
|
|
12
12
|
*/
|
|
13
13
|
import type { LoadProxyConfigOptions, ProxyConfigFile } from "../types/index.js";
|
|
14
|
+
/** Default on-disk path used when `proxy start` is not given `--config`. */
|
|
15
|
+
export declare function defaultProxyConfigPath(): string;
|
|
16
|
+
/** Resolve `--config` the same way `proxy start` / `proxy install` do. */
|
|
17
|
+
export declare function resolveProxyConfigPath(explicit?: string): string;
|
|
14
18
|
/**
|
|
15
19
|
* Replace all `${VAR}` / `${VAR:-default}` references in a string.
|
|
16
20
|
*
|
|
@@ -11,9 +11,21 @@
|
|
|
11
11
|
* falls back to JSON.parse.
|
|
12
12
|
*/
|
|
13
13
|
import { readFile } from "node:fs/promises";
|
|
14
|
-
import {
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
import { extname, join, resolve } from "node:path";
|
|
15
16
|
import { MAX_MAX_INFLIGHT_PER_ACCOUNT, MIN_MAX_INFLIGHT_PER_ACCOUNT, } from "./modelRouter.js";
|
|
16
17
|
import { logger } from "../utils/logger.js";
|
|
18
|
+
/** Default on-disk path used when `proxy start` is not given `--config`. */
|
|
19
|
+
export function defaultProxyConfigPath() {
|
|
20
|
+
return join(homedir(), ".neurolink", "proxy-config.yaml");
|
|
21
|
+
}
|
|
22
|
+
/** Resolve `--config` the same way `proxy start` / `proxy install` do. */
|
|
23
|
+
export function resolveProxyConfigPath(explicit) {
|
|
24
|
+
const trimmed = explicit?.trim();
|
|
25
|
+
return trimmed && trimmed.length > 0
|
|
26
|
+
? resolve(trimmed)
|
|
27
|
+
: defaultProxyConfigPath();
|
|
28
|
+
}
|
|
17
29
|
// ---------------------------------------------------------------------------
|
|
18
30
|
// Environment variable resolution
|
|
19
31
|
// ---------------------------------------------------------------------------
|
package/dist/types/generate.d.ts
CHANGED
|
@@ -410,11 +410,24 @@ export type GenerateOptions = {
|
|
|
410
410
|
*/
|
|
411
411
|
wrapupTimeLeadMs?: number;
|
|
412
412
|
/**
|
|
413
|
-
* Per-tool-execution timeout in milliseconds (default 300_000)
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
|
|
417
|
-
|
|
413
|
+
* Per-tool-execution timeout in milliseconds (default 300_000), or `null`
|
|
414
|
+
* for no bound at all.
|
|
415
|
+
*
|
|
416
|
+
* A tool that exceeds it is told to stop — the AbortSignal it was handed is
|
|
417
|
+
* aborted — and then fails with an error tool_result costing one step, so
|
|
418
|
+
* the turn continues instead of hanging on a wedged tool. A tool that
|
|
419
|
+
* ignores its signal keeps running to completion in the background; nothing
|
|
420
|
+
* here can stop it, and its eventual result is discarded.
|
|
421
|
+
*
|
|
422
|
+
* `null` removes the bound and awaits `execute` unguarded, which is what the
|
|
423
|
+
* native loops did before they had a per-tool timer. It is the way to keep a
|
|
424
|
+
* legitimately long-running tool, since a finite number is always a ceiling
|
|
425
|
+
* and `Infinity` silently becomes `setTimeout`'s ~24.9-day cap. The one
|
|
426
|
+
* combination refused is `null` together with
|
|
427
|
+
* `executionControl.lifetimeTimeoutMs: null`, which would leave the turn
|
|
428
|
+
* with no bound anywhere.
|
|
429
|
+
*/
|
|
430
|
+
toolTimeoutMs?: number | null;
|
|
418
431
|
/** AbortSignal for external cancellation of the AI call */
|
|
419
432
|
abortSignal?: AbortSignal;
|
|
420
433
|
/**
|
|
@@ -1201,8 +1214,8 @@ export type TextGenerationOptions = {
|
|
|
1201
1214
|
stallTimeoutMs?: number;
|
|
1202
1215
|
/** Remaining-time threshold that triggers the wrap-up nudge (ms). See GenerateOptions.wrapupTimeLeadMs. */
|
|
1203
1216
|
wrapupTimeLeadMs?: number;
|
|
1204
|
-
/** Per-tool-execution timeout (ms, default 300_000). See GenerateOptions.toolTimeoutMs. */
|
|
1205
|
-
toolTimeoutMs?: number;
|
|
1217
|
+
/** Per-tool-execution timeout (ms, default 300_000; `null` for no bound). See GenerateOptions.toolTimeoutMs. */
|
|
1218
|
+
toolTimeoutMs?: number | null;
|
|
1206
1219
|
/** AbortSignal for external cancellation of the AI call */
|
|
1207
1220
|
abortSignal?: AbortSignal;
|
|
1208
1221
|
/** Bounds for tool execution capture. See GenerateOptions.toolExecutionCapture. */
|
|
@@ -1587,8 +1600,8 @@ export type NativeGenerateLoopArgs = {
|
|
|
1587
1600
|
maxOutputTokens?: number;
|
|
1588
1601
|
temperature?: number;
|
|
1589
1602
|
abortSignal?: AbortSignal;
|
|
1590
|
-
/** Per-tool-execution cap, forwarded into `guardToolExecutor`. */
|
|
1591
|
-
toolTimeoutMs?: number;
|
|
1603
|
+
/** Per-tool-execution cap, forwarded into `guardToolExecutor`. `null` for no bound. */
|
|
1604
|
+
toolTimeoutMs?: number | null;
|
|
1592
1605
|
/** Wraps one step: retry ladder plus provider error classification. */
|
|
1593
1606
|
runStep: (call: () => Promise<Record<string, unknown>>) => Promise<Record<string, unknown>>;
|
|
1594
1607
|
};
|
|
@@ -2,6 +2,7 @@ import type Anthropic from "@anthropic-ai/sdk";
|
|
|
2
2
|
import type { Span } from "@opentelemetry/api";
|
|
3
3
|
import type { Tool } from "./tools.js";
|
|
4
4
|
import type { CollectedChunkResult, NativeFunctionCall, NativeToolDeclarationsResult } from "./providers.js";
|
|
5
|
+
import type { ExecutionControlDecision, ExecutionControlStepContext } from "./stream.js";
|
|
5
6
|
/**
|
|
6
7
|
* One chunk on the engine's stream.
|
|
7
8
|
*
|
|
@@ -163,6 +164,17 @@ export type AgenticLoopAdapter<TConversation = unknown, TRaw = unknown> = {
|
|
|
163
164
|
readonly maxSteps: number;
|
|
164
165
|
/** Set only for adapter instances whose client has the TOOL_NOT_FOUND strike breaker today: both Gemini adapters (AI Studio, Vertex+Gemini) AND the Vertex+Claude call to createAnthropicLoopAdapter — NOT the native-Anthropic call to that same factory, and not Bedrock. See Verified Fact 4. */
|
|
165
166
|
readonly toolFailureBreaker?: AgenticLoopToolFailureBreaker;
|
|
167
|
+
/**
|
|
168
|
+
* Append a planning nudge to the conversation, in the provider's own message
|
|
169
|
+
* type. Supplied only by adapters whose caller can pass a step-boundary
|
|
170
|
+
* callback; the engine skips a nudge it has no way to write.
|
|
171
|
+
*
|
|
172
|
+
* Provider-supplied for the same reason `buildToolResultMessages` is: the
|
|
173
|
+
* engine does not know what a valid user turn looks like on this wire, and
|
|
174
|
+
* on Anthropic a nudge appended after a tool-result turn has to merge into
|
|
175
|
+
* that turn rather than open a second consecutive user message.
|
|
176
|
+
*/
|
|
177
|
+
readonly appendPlanningNudge?: (conversation: TConversation, text: string) => TConversation;
|
|
166
178
|
/**
|
|
167
179
|
* Second lookup path, consulted when a tool call names nothing executable
|
|
168
180
|
* in the caller's `options.tools` — used by adapters supporting mid-turn
|
|
@@ -265,6 +277,30 @@ export type AnthropicLoopAdapterConfig<TMessage = Anthropic.Messages.MessagePara
|
|
|
265
277
|
* tell them apart would be guesswork, so the adapter says which happened.
|
|
266
278
|
*/
|
|
267
279
|
onTerminalResult?: (text: string) => void;
|
|
280
|
+
/**
|
|
281
|
+
* Hard deadline for ONE `messages.create` request (ms). Composed with the
|
|
282
|
+
* engine's signal for the duration of that step only, so it bounds a stalled
|
|
283
|
+
* upstream even when the turn itself has no lifetime ceiling — and is armed
|
|
284
|
+
* fresh per step, which is why a step boundary can never extend a deadline
|
|
285
|
+
* already running.
|
|
286
|
+
*
|
|
287
|
+
* When the deadline fires, the step throws the timer's own TimeoutError
|
|
288
|
+
* rather than returning what it had: the Anthropic SDK's stream iterator
|
|
289
|
+
* exits WITHOUT throwing on an aborted read, so a truncated step would
|
|
290
|
+
* otherwise be reported as a model turn that simply said less.
|
|
291
|
+
*/
|
|
292
|
+
requestTimeoutMs?: number;
|
|
293
|
+
/**
|
|
294
|
+
* Require the upstream terminal event (`message_stop`) before a step counts
|
|
295
|
+
* as complete.
|
|
296
|
+
*
|
|
297
|
+
* A response that ends early carries syntactically complete content blocks —
|
|
298
|
+
* including tool_use blocks — so without this the loop dispatches tools the
|
|
299
|
+
* model never finished asking for and reports the turn as a normal stop.
|
|
300
|
+
* Off by default: turning it on unconditionally would change what every
|
|
301
|
+
* existing caller sees from a flaky connection.
|
|
302
|
+
*/
|
|
303
|
+
requireTerminalEvent?: boolean;
|
|
268
304
|
toolFailureBreaker?: AgenticLoopToolFailureBreaker;
|
|
269
305
|
/**
|
|
270
306
|
* In-turn context reclaim, run once per step before the request is built.
|
|
@@ -305,8 +341,15 @@ export type AnthropicLoopAdapterConfig<TMessage = Anthropic.Messages.MessagePara
|
|
|
305
341
|
* never had.
|
|
306
342
|
*/
|
|
307
343
|
export type ToolExecutionGuards = {
|
|
308
|
-
/**
|
|
309
|
-
|
|
344
|
+
/**
|
|
345
|
+
* Upper bound on a single execute(). Omit, or pass `null`, for no bound.
|
|
346
|
+
*
|
|
347
|
+
* `null` is accepted as well as `undefined` because it is what a caller's
|
|
348
|
+
* own `toolTimeoutMs: null` resolves to, and the two must not diverge: a
|
|
349
|
+
* value that means "unbounded" at the public surface cannot arrive here
|
|
350
|
+
* meaning "0ms".
|
|
351
|
+
*/
|
|
352
|
+
toolTimeoutMs?: number | null;
|
|
310
353
|
/**
|
|
311
354
|
* Turn-level abort, raced against the call so a deadline or caller cancel is
|
|
312
355
|
* observed immediately instead of after the tool settles.
|
|
@@ -491,6 +534,44 @@ export type AgenticLoopOptions = {
|
|
|
491
534
|
* because only they threaded a span before moving onto the engine.
|
|
492
535
|
*/
|
|
493
536
|
span?: Span;
|
|
537
|
+
/**
|
|
538
|
+
* Upper bound on a single `tool.execute()` (ms), or `null` for no bound.
|
|
539
|
+
* Defaults to `DEFAULT_TOOL_EXECUTION_TIMEOUT_MS` (300_000) when omitted.
|
|
540
|
+
*
|
|
541
|
+
* The turn-level timers do not cover this: a per-request deadline bounds one
|
|
542
|
+
* model call and is disposed when the step settles, and the step cap only
|
|
543
|
+
* advances when a step completes. Between two steps, a tool that never
|
|
544
|
+
* returns has nothing watching it, and a turn that opted out of a lifetime
|
|
545
|
+
* ceiling then hangs forever. A tool that exceeds the bound is told to stop
|
|
546
|
+
* — the signal it was handed is aborted — and fails with an error tool
|
|
547
|
+
* result costing one step, exactly as it does on the native generate path.
|
|
548
|
+
*
|
|
549
|
+
* `null` restores the behaviour of a loop with no per-tool timer at all:
|
|
550
|
+
* `execute` is awaited unguarded, with the turn's own signal. Callers that
|
|
551
|
+
* relied on unbounded tool execution say so with it. The one combination
|
|
552
|
+
* refused is `null` together with `executionControl.lifetimeTimeoutMs: null`
|
|
553
|
+
* — that would leave the turn with no bound anywhere.
|
|
554
|
+
*
|
|
555
|
+
* Callers that already guard their executors (Vertex and the Gemini
|
|
556
|
+
* adapters, via `guardToolExecutor`) should pass the SAME value they gave
|
|
557
|
+
* those guards, so this backstop can never be tighter than what the caller
|
|
558
|
+
* asked for.
|
|
559
|
+
*/
|
|
560
|
+
toolTimeoutMs?: number | null;
|
|
561
|
+
/**
|
|
562
|
+
* Step-boundary callback. Runs after a step's tool results have settled and
|
|
563
|
+
* been written into the conversation, and BEFORE the loop re-checks the step
|
|
564
|
+
* cap — the one point in a turn where raising the cap changes what happens
|
|
565
|
+
* next without replaying anything that already happened.
|
|
566
|
+
*
|
|
567
|
+
* Already bounded and cancellable by the time it reaches the engine: the
|
|
568
|
+
* caller owns the callback's own budget, because the caller is what the
|
|
569
|
+
* public option was validated against. The engine only calls it, applies a
|
|
570
|
+
* strictly-larger finite cap if one comes back, and asks the adapter to
|
|
571
|
+
* write the nudge. It never restarts a step, retries a request, or replays a
|
|
572
|
+
* tool.
|
|
573
|
+
*/
|
|
574
|
+
beforeStep?: (context: ExecutionControlStepContext) => Promise<ExecutionControlDecision | undefined>;
|
|
494
575
|
};
|
|
495
576
|
export type AgenticLoopResult<TConversation> = {
|
|
496
577
|
text: string;
|
|
@@ -518,4 +599,18 @@ export type AgenticLoopResult<TConversation> = {
|
|
|
518
599
|
finishReason: string;
|
|
519
600
|
rawStopReason: string | undefined;
|
|
520
601
|
conversation: TConversation;
|
|
602
|
+
/**
|
|
603
|
+
* True when the turn ended because its abort signal fired rather than
|
|
604
|
+
* because the model finished.
|
|
605
|
+
*
|
|
606
|
+
* Not derivable from anything else on this result, which is why it is here.
|
|
607
|
+
* A turn cut short mid-stream never receives a terminal event, so
|
|
608
|
+
* `rawStopReason` is undefined and `mapFinishReason` lands on exactly the
|
|
609
|
+
* value a model that answered and stopped produces. Without this flag a
|
|
610
|
+
* caller reading the result cannot tell "the model finished" from "we
|
|
611
|
+
* stopped listening", and every consumer that branches on the outcome —
|
|
612
|
+
* fallback gates, retry policy, a UI that says why a turn ended — reads the
|
|
613
|
+
* interrupted turn as a success.
|
|
614
|
+
*/
|
|
615
|
+
aborted: boolean;
|
|
521
616
|
};
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import type { JsonObject } from "./common.js";
|
|
2
|
+
/** Options forwarded through `applyAllClients` into a configurator's `apply`. */
|
|
3
|
+
export type CliProxyClientApplyOptions = {
|
|
4
|
+
/**
|
|
5
|
+
* Absolute path of the proxy routing config this process loaded.
|
|
6
|
+
* The Grok writer reads `routing.model-mappings` from this file so a
|
|
7
|
+
* `proxy start --config` catalog matches `/v1/models`. Omit to use
|
|
8
|
+
* `~/.neurolink/proxy-config.yaml`.
|
|
9
|
+
*/
|
|
10
|
+
configPath?: string;
|
|
11
|
+
};
|
|
2
12
|
/**
|
|
3
13
|
* One AI coding CLI the proxy can point at itself.
|
|
4
14
|
*
|
|
@@ -22,7 +32,7 @@ export type CliProxyClientConfigurator = {
|
|
|
22
32
|
* suffix its CLI needs. Returns false when nothing was written, so callers
|
|
23
33
|
* never print a success message for work that did not happen.
|
|
24
34
|
*/
|
|
25
|
-
apply: (proxyBaseUrl: string) => Promise<boolean>;
|
|
35
|
+
apply: (proxyBaseUrl: string, options?: CliProxyClientApplyOptions) => Promise<boolean>;
|
|
26
36
|
/**
|
|
27
37
|
* Restore the user's previous configuration. `proxyBaseUrl` is the same bare
|
|
28
38
|
* origin; a configurator that finds a different URL configured must leave it
|
|
@@ -77,6 +87,29 @@ export type CliOpenCodeSnapshot = {
|
|
|
77
87
|
/** What the writer last wrote, so apply() can recognise its own block. */
|
|
78
88
|
written?: unknown;
|
|
79
89
|
};
|
|
90
|
+
/**
|
|
91
|
+
* One Grok Build picker entry the proxy writer emits under `[model.<id>]`.
|
|
92
|
+
*
|
|
93
|
+
* `contextWindow` is Grok's compaction limit and must be <= the upstream
|
|
94
|
+
* model's window. `supportsReasoningEffort` is false when Grok's `xhigh`
|
|
95
|
+
* would become Anthropic adaptive thinking that the model rejects.
|
|
96
|
+
*/
|
|
97
|
+
export type CliGrokProxyModelSpec = {
|
|
98
|
+
id: string;
|
|
99
|
+
name: string;
|
|
100
|
+
apiBackend: "messages" | "chat_completions";
|
|
101
|
+
contextWindow: number;
|
|
102
|
+
maxCompletionTokens: number;
|
|
103
|
+
supportsReasoningEffort: boolean;
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Snapshot of whether `~/.grok/config.toml` existed before the proxy wrote
|
|
107
|
+
* its managed block. Persisted to `~/.neurolink/grok-proxy-snapshot.json`.
|
|
108
|
+
*/
|
|
109
|
+
export type CliGrokSnapshot = {
|
|
110
|
+
originalExisted: boolean;
|
|
111
|
+
writtenBaseUrl: string;
|
|
112
|
+
};
|
|
80
113
|
/**
|
|
81
114
|
* Snapshot of the user's pre-existing Gemini CLI `~/.gemini/.env`.
|
|
82
115
|
*
|
package/dist/types/stream.d.ts
CHANGED
|
@@ -184,6 +184,114 @@ export type ProviderStreamChunk = {
|
|
|
184
184
|
base64: string;
|
|
185
185
|
};
|
|
186
186
|
};
|
|
187
|
+
/**
|
|
188
|
+
* What a `beforeStep` callback is told at a step boundary.
|
|
189
|
+
*
|
|
190
|
+
* The boundary is reached only after that step's tool results have settled and
|
|
191
|
+
* been written into the conversation, and before the step cap is re-checked —
|
|
192
|
+
* so a decision taken here applies to the NEXT step of the SAME turn, never to
|
|
193
|
+
* a step already in flight.
|
|
194
|
+
*/
|
|
195
|
+
export type ExecutionControlStepContext = {
|
|
196
|
+
/** Zero-based index of the step that just settled. */
|
|
197
|
+
stepIndex: number;
|
|
198
|
+
/** Steps that have completed in this turn, including the one that just settled. */
|
|
199
|
+
stepsCompleted: number;
|
|
200
|
+
/** The step cap currently in force — the number a renewal must exceed. */
|
|
201
|
+
maxSteps: number;
|
|
202
|
+
/** Milliseconds since the turn's first request was built. */
|
|
203
|
+
elapsedMs: number;
|
|
204
|
+
/** Names of the tools dispatched on the step that just settled, in order. */
|
|
205
|
+
toolNames: string[];
|
|
206
|
+
/**
|
|
207
|
+
* Fires when the turn is cancelled or the callback outlives its own budget.
|
|
208
|
+
* A callback that does real work (reading a live budget, asking a service)
|
|
209
|
+
* must honour it — the turn does not wait for a callback that ignores it.
|
|
210
|
+
*/
|
|
211
|
+
signal: AbortSignal;
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* What a `beforeStep` callback may change about the rest of the turn.
|
|
215
|
+
*
|
|
216
|
+
* Returning nothing is a decision too: it declines the renewal, so the
|
|
217
|
+
* existing cap stands and a turn that has reached it ends as a step-limit
|
|
218
|
+
* outcome exactly as it would with no callback at all.
|
|
219
|
+
*/
|
|
220
|
+
export type ExecutionControlDecision = {
|
|
221
|
+
/**
|
|
222
|
+
* A new absolute step cap. Must be finite and greater than the cap in force;
|
|
223
|
+
* anything else is ignored, so a callback cannot shorten a turn by returning
|
|
224
|
+
* a smaller number or unbound one by returning Infinity.
|
|
225
|
+
*/
|
|
226
|
+
maxSteps?: number;
|
|
227
|
+
/**
|
|
228
|
+
* A planning nudge appended to the conversation before the next step, in the
|
|
229
|
+
* same loop and the same history — this is how a caller tells the model that
|
|
230
|
+
* its budget changed without restarting the turn.
|
|
231
|
+
*/
|
|
232
|
+
nudge?: string;
|
|
233
|
+
};
|
|
234
|
+
/**
|
|
235
|
+
* Opt-in execution policy for one streamed turn. Supported only on the native
|
|
236
|
+
* Anthropic stream path today; any other provider REJECTS it rather than
|
|
237
|
+
* ignoring it, because silently dropping a policy the caller set is how a turn
|
|
238
|
+
* ends at a limit its owner believed it had removed.
|
|
239
|
+
*
|
|
240
|
+
* Absent, none of the turn-level policy here applies: the turn is bounded by
|
|
241
|
+
* `timeout` / `turnTimeoutMs` / `maxSteps` as it always was.
|
|
242
|
+
*
|
|
243
|
+
* One thing is NOT conditional on this option, and it is worth stating because
|
|
244
|
+
* it changed: the agentic engine bounds every tool call, at
|
|
245
|
+
* `toolTimeoutMs` or the 300s default, whether or not `executionControl` is
|
|
246
|
+
* present. Loops that previously ran tools with no per-tool timer at all — the
|
|
247
|
+
* Bedrock and Google AI Studio paths — therefore acquired one. A caller that
|
|
248
|
+
* relied on unbounded tool execution says so with `toolTimeoutMs: null`, which
|
|
249
|
+
* restores exactly the old behaviour.
|
|
250
|
+
*/
|
|
251
|
+
export type ExecutionControlOptions = {
|
|
252
|
+
/**
|
|
253
|
+
* Hard deadline for a single HTTP request (ms). Required, finite, positive.
|
|
254
|
+
*
|
|
255
|
+
* This is the floor that makes the rest of the contract safe: whatever the
|
|
256
|
+
* turn-level policy is — including no lifetime ceiling at all — a stalled
|
|
257
|
+
* upstream is always caught here, with the timer's own identity on the
|
|
258
|
+
* error. A step boundary cannot reset a deadline already running.
|
|
259
|
+
*/
|
|
260
|
+
requestTimeoutMs: number;
|
|
261
|
+
/**
|
|
262
|
+
* The turn's wall-clock ceiling (ms).
|
|
263
|
+
*
|
|
264
|
+
* - `null` — no lifetime timer is armed at all. This is the case that cannot
|
|
265
|
+
* be expressed any other way: a very large number is still a ceiling, and
|
|
266
|
+
* it fires eventually, in the middle of work, dressed as a cancel. With no
|
|
267
|
+
* lifetime timer the per-tool deadline is the turn's last bound, so
|
|
268
|
+
* `toolTimeoutMs: null` is refused alongside it.
|
|
269
|
+
* - a finite positive number — an explicit cap, reported as `time-limit`.
|
|
270
|
+
* - `0`, negative, or non-finite — rejected.
|
|
271
|
+
* - absent — the legacy handling (`turnTimeoutMs`, else the provider
|
|
272
|
+
* timeout) is inherited untouched.
|
|
273
|
+
*
|
|
274
|
+
* Set to anything other than absent, this OWNS the turn's lifetime timer, so
|
|
275
|
+
* a `turnTimeoutMs` on the same request would be read by nothing. That
|
|
276
|
+
* combination is rejected rather than silently resolved — set one or the
|
|
277
|
+
* other. Note that "absent" means the property is not there AND that it is
|
|
278
|
+
* present holding `undefined`: both say "no opinion", and both inherit.
|
|
279
|
+
*/
|
|
280
|
+
lifetimeTimeoutMs?: number | null;
|
|
281
|
+
/**
|
|
282
|
+
* Runs at each step boundary. May renew the step cap and append a planning
|
|
283
|
+
* nudge. It is itself bounded by `beforeStepTimeoutMs` and cancelled with
|
|
284
|
+
* the turn; a callback that throws, or outlives its budget, is treated as
|
|
285
|
+
* declining to renew.
|
|
286
|
+
*/
|
|
287
|
+
beforeStep?: (context: ExecutionControlStepContext) => ExecutionControlDecision | undefined | Promise<ExecutionControlDecision | undefined>;
|
|
288
|
+
/**
|
|
289
|
+
* Bound on `beforeStep` itself (ms, finite and positive; default 30_000).
|
|
290
|
+
* A boundary callback sits between two model calls, so an unbounded one
|
|
291
|
+
* stalls the turn in a place no other timer is watching.
|
|
292
|
+
*/
|
|
293
|
+
beforeStepTimeoutMs?: number;
|
|
294
|
+
};
|
|
187
295
|
export type StreamOptions = {
|
|
188
296
|
/**
|
|
189
297
|
* Opt this stream call into the knowledge grounding configured on the
|
|
@@ -376,12 +484,39 @@ export type StreamOptions = {
|
|
|
376
484
|
timeout?: number | string;
|
|
377
485
|
/** Wall-clock cap for the whole agentic turn (ms). See GenerateOptions.turnTimeoutMs. */
|
|
378
486
|
turnTimeoutMs?: number;
|
|
487
|
+
/**
|
|
488
|
+
* Opt-in execution policy for this turn (native Anthropic streaming only).
|
|
489
|
+
* See ExecutionControlOptions. Absent means the legacy bounds apply
|
|
490
|
+
* unchanged; present on any other provider is an error, not a no-op.
|
|
491
|
+
*
|
|
492
|
+
* Two consequences of that "error, not a no-op" stance are worth knowing
|
|
493
|
+
* before you set it:
|
|
494
|
+
*
|
|
495
|
+
* - **It is incompatible with provider fallback.** Only the native Anthropic
|
|
496
|
+
* stream path implements the contract, so a turn that falls back to any
|
|
497
|
+
* other provider — internal fallback, or a configured fallback chain —
|
|
498
|
+
* fails there with a ValidationError instead of being served without the
|
|
499
|
+
* policy. That is deliberate: a fallback that silently dropped the policy
|
|
500
|
+
* would produce exactly the invisible ceiling this option exists to
|
|
501
|
+
* remove. Pair it with `disableInternalFallback: true` when you want the
|
|
502
|
+
* turn to stay on Anthropic, and handle the error if you do not.
|
|
503
|
+
* - **It cannot be combined with `turnTimeoutMs`** when it sets
|
|
504
|
+
* `lifetimeTimeoutMs`, because both name the turn's wall-clock ceiling and
|
|
505
|
+
* only one of them can win. Supplying both is rejected up front rather
|
|
506
|
+
* than resolved in silence.
|
|
507
|
+
* - **`lifetimeTimeoutMs: null` cannot be combined with
|
|
508
|
+
* `toolTimeoutMs: null`.** Removing the turn's ceiling leaves the per-tool
|
|
509
|
+
* deadline as the only thing that will ever end a tool which never
|
|
510
|
+
* returns; removing that as well leaves the turn with no bound anywhere.
|
|
511
|
+
* Also rejected up front.
|
|
512
|
+
*/
|
|
513
|
+
executionControl?: ExecutionControlOptions;
|
|
379
514
|
/** Max time with no progress before the turn ends as "stalled" (ms). Native Vertex loops only — see GenerateOptions.stallTimeoutMs. */
|
|
380
515
|
stallTimeoutMs?: number;
|
|
381
516
|
/** Remaining-time threshold that triggers the wrap-up nudge (ms). See GenerateOptions.wrapupTimeLeadMs. */
|
|
382
517
|
wrapupTimeLeadMs?: number;
|
|
383
|
-
/** Per-tool-execution timeout (ms, default 300_000). See GenerateOptions.toolTimeoutMs. */
|
|
384
|
-
toolTimeoutMs?: number;
|
|
518
|
+
/** Per-tool-execution timeout (ms, default 300_000; `null` for no bound). See GenerateOptions.toolTimeoutMs. */
|
|
519
|
+
toolTimeoutMs?: number | null;
|
|
385
520
|
/** AbortSignal for external cancellation of the AI call */
|
|
386
521
|
abortSignal?: AbortSignal;
|
|
387
522
|
/** Bounds for tool execution capture. See GenerateOptions.toolExecutionCapture. */
|
|
@@ -641,9 +776,10 @@ export type StreamResult = {
|
|
|
641
776
|
finishReason?: string;
|
|
642
777
|
/**
|
|
643
778
|
* Why the agentic turn ended (see GenerateStopReason). For background-loop
|
|
644
|
-
* streams (native Vertex paths
|
|
645
|
-
* the stream — this top-level
|
|
646
|
-
*
|
|
779
|
+
* streams (the native Vertex paths and the native Anthropic stream path)
|
|
780
|
+
* prefer `metadata.stopReason` after draining the stream — this top-level
|
|
781
|
+
* field may be a getter that resolves late, and wrapper spreads can
|
|
782
|
+
* snapshot it before the loop finishes.
|
|
647
783
|
*/
|
|
648
784
|
stopReason?: GenerateStopReason;
|
|
649
785
|
/** Verbatim provider finish/stop reason for the turn's terminal model call. */
|
|
@@ -198,3 +198,33 @@ export declare function createValidationSummary(result: EnhancedValidationResult
|
|
|
198
198
|
* Check if validation result has only warnings (no errors)
|
|
199
199
|
*/
|
|
200
200
|
export declare function hasOnlyWarnings(result: EnhancedValidationResult): boolean;
|
|
201
|
+
/**
|
|
202
|
+
* Default bound on a `beforeStep` callback (ms).
|
|
203
|
+
*
|
|
204
|
+
* A boundary callback sits between two model calls, in the one place no other
|
|
205
|
+
* timer in the turn is watching: the request deadline has been disposed and
|
|
206
|
+
* the next one is not armed yet. An unbounded callback therefore stalls the
|
|
207
|
+
* turn silently and indefinitely.
|
|
208
|
+
*/
|
|
209
|
+
export declare const DEFAULT_BEFORE_STEP_TIMEOUT_MS = 30000;
|
|
210
|
+
/**
|
|
211
|
+
* Validate an opt-in `executionControl` object and its provider support.
|
|
212
|
+
*
|
|
213
|
+
* Rejecting rather than ignoring is the whole point. A caller that asked for
|
|
214
|
+
* no lifetime ceiling and got one anyway does not find out at call time — it
|
|
215
|
+
* finds out much later, when a long turn dies at a limit its owner believed it
|
|
216
|
+
* had removed, reported as an ordinary cancel. So an unsupported provider is an
|
|
217
|
+
* error here, and so is every shape that could be read two ways.
|
|
218
|
+
*
|
|
219
|
+
* Throws ValidationError; returns void when the control is absent or valid.
|
|
220
|
+
*
|
|
221
|
+
* @param request the sibling options on the SAME call that this control has to
|
|
222
|
+
* be read together with. `turnTimeoutMs`, because a combination in which one
|
|
223
|
+
* of the two ceilings would be silently discarded is rejected instead; and
|
|
224
|
+
* `toolTimeoutMs`, because removing the turn's ceiling makes the per-tool
|
|
225
|
+
* deadline the last thing watching a tool that never returns.
|
|
226
|
+
*/
|
|
227
|
+
export declare function validateExecutionControl(control: unknown, providerName: string, supported: boolean, request?: {
|
|
228
|
+
turnTimeoutMs?: unknown;
|
|
229
|
+
toolTimeoutMs?: unknown;
|
|
230
|
+
}): void;
|