@juspay/neurolink 12.0.5 → 12.2.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 (48) hide show
  1. package/CHANGELOG.md +3 -3
  2. package/dist/agent/agentToolRegistrar.d.ts +30 -0
  3. package/dist/agent/agentToolRegistrar.js +72 -18
  4. package/dist/agent/backgroundCommands.d.ts +110 -0
  5. package/dist/agent/backgroundCommands.js +914 -0
  6. package/dist/agent/backgroundDelegation.d.ts +87 -0
  7. package/dist/agent/backgroundDelegation.js +753 -0
  8. package/dist/agent/gitTools.d.ts +43 -0
  9. package/dist/agent/gitTools.js +618 -0
  10. package/dist/agent/taskChecklist.d.ts +58 -0
  11. package/dist/agent/taskChecklist.js +322 -0
  12. package/dist/artifacts/artifactBanking.d.ts +57 -0
  13. package/dist/artifacts/artifactBanking.js +123 -0
  14. package/dist/artifacts/artifactStore.d.ts +36 -8
  15. package/dist/artifacts/artifactStore.js +164 -13
  16. package/dist/browser/neurolink.min.js +442 -414
  17. package/dist/cli/commands/setup.js +2 -1
  18. package/dist/constants/enums.d.ts +19 -0
  19. package/dist/constants/enums.js +20 -0
  20. package/dist/factories/providerDescriptors.js +16 -1
  21. package/dist/models/manifestRegistry.js +2 -0
  22. package/dist/models/manifests/cerebras.d.ts +9 -0
  23. package/dist/models/manifests/cerebras.js +19 -0
  24. package/dist/neurolink.d.ts +294 -3
  25. package/dist/neurolink.js +447 -4
  26. package/dist/providers/openaiCompatCatalog.d.ts +1 -1
  27. package/dist/providers/openaiCompatCatalog.js +34 -3
  28. package/dist/types/artifact.d.ts +54 -0
  29. package/dist/types/backgroundCommand.d.ts +174 -0
  30. package/dist/types/backgroundCommand.js +22 -0
  31. package/dist/types/delegation.d.ts +178 -0
  32. package/dist/types/delegation.js +18 -0
  33. package/dist/types/gitTools.d.ts +69 -0
  34. package/dist/types/gitTools.js +22 -0
  35. package/dist/types/index.d.ts +5 -0
  36. package/dist/types/index.js +8 -0
  37. package/dist/types/pathSandbox.d.ts +23 -0
  38. package/dist/types/pathSandbox.js +12 -0
  39. package/dist/types/providers.d.ts +4 -0
  40. package/dist/types/tasks.d.ts +85 -0
  41. package/dist/types/tasks.js +14 -0
  42. package/dist/types/tools.d.ts +11 -0
  43. package/dist/utils/modelChoices.js +17 -1
  44. package/dist/utils/pathSandbox.d.ts +49 -0
  45. package/dist/utils/pathSandbox.js +127 -0
  46. package/dist/utils/providerConfig.d.ts +4 -0
  47. package/dist/utils/providerConfig.js +17 -0
  48. package/package.json +5 -1
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Task-checklist primitive (TodoWrite-style) — types.
3
+ *
4
+ * A long-running agent needs a durable, model-visible list of what this run
5
+ * must finish. The checklist lives on the SESSION, outside the message list,
6
+ * so summarization/compaction can rewrite the conversation without touching
7
+ * it, and every tool call returns the whole list so the model re-anchors for
8
+ * free after a compaction.
9
+ *
10
+ * Naming: the scheduler in `task.ts` already owns `Task`, `TaskStatus`,
11
+ * `TaskDefinition`, `TaskStore`, `TaskRunResult` and `TasksFile`, so every
12
+ * type here carries the `Checklist` prefix (Critical Rule 9).
13
+ */
14
+ /** Lifecycle of one checklist item. `closed` means "not done, and here is why". */
15
+ export type ChecklistItemStatus = "pending" | "in_progress" | "done" | "closed";
16
+ export type ChecklistItem = {
17
+ /** "t1", "t2", … — assigned by the engine, never by the model. */
18
+ id: string;
19
+ title: string;
20
+ status: ChecklistItemStatus;
21
+ /** Result note, or the REASON an item was closed unfinished. */
22
+ note?: string;
23
+ createdAt: number;
24
+ updatedAt: number;
25
+ };
26
+ /** Everything one session's checklist holds. Never stored in messages. */
27
+ export type ChecklistState = {
28
+ sessionId: string;
29
+ items: ChecklistItem[];
30
+ updatedAt: number;
31
+ };
32
+ export type ChecklistCreateInput = {
33
+ titles: string[];
34
+ };
35
+ export type ChecklistUpdateInput = {
36
+ id: string;
37
+ status: ChecklistItemStatus;
38
+ note?: string;
39
+ };
40
+ /**
41
+ * Background delegates outstanding for a session. Populated by the async
42
+ * delegation primitive; zero while that primitive is unused.
43
+ */
44
+ export type ChecklistDelegateCounts = {
45
+ pending: number;
46
+ ready: number;
47
+ };
48
+ /** Supplies {@link ChecklistDelegateCounts} to every checklist tool result. */
49
+ export type ChecklistDelegateCountsSource = (sessionId: string) => ChecklistDelegateCounts;
50
+ /**
51
+ * Background commands outstanding for a session. Populated by the
52
+ * background-command primitive; zero while that primitive is unused.
53
+ */
54
+ export type ChecklistCommandCounts = {
55
+ running: number;
56
+ finished: number;
57
+ };
58
+ /** Supplies {@link ChecklistCommandCounts} to every checklist tool result. */
59
+ export type ChecklistCommandCountsSource = (sessionId: string) => ChecklistCommandCounts;
60
+ /**
61
+ * Every `tasks_*` tool returns this — the model re-anchors on the full list
62
+ * on each call, which is what makes the checklist survive compaction with no
63
+ * re-injection machinery.
64
+ */
65
+ export type ChecklistToolResult = {
66
+ items: ChecklistItem[];
67
+ counts: Record<ChecklistItemStatus, number>;
68
+ /** Background delegates not yet collected (0 when delegation is unused). */
69
+ delegatesPending: number;
70
+ delegatesReady: number;
71
+ /**
72
+ * Background commands still running (0 when the command primitive is
73
+ * unused). Carried here for the same reason the delegate counters are: the
74
+ * model learns "the build finished" from any `tasks_list`, with no polling
75
+ * and no change to the core loop.
76
+ */
77
+ commandsRunning: number;
78
+ /** Background commands that have settled and can be read. */
79
+ commandsFinished: number;
80
+ };
81
+ /** Refusal shape shared with the agent tool registrar: recovery text included. */
82
+ export type ChecklistRefusal = {
83
+ isError: true;
84
+ error: string;
85
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Task-checklist primitive (TodoWrite-style) — types.
3
+ *
4
+ * A long-running agent needs a durable, model-visible list of what this run
5
+ * must finish. The checklist lives on the SESSION, outside the message list,
6
+ * so summarization/compaction can rewrite the conversation without touching
7
+ * it, and every tool call returns the whole list so the model re-anchors for
8
+ * free after a compaction.
9
+ *
10
+ * Naming: the scheduler in `task.ts` already owns `Task`, `TaskStatus`,
11
+ * `TaskDefinition`, `TaskStore`, `TaskRunResult` and `TasksFile`, so every
12
+ * type here carries the `Checklist` prefix (Critical Rule 9).
13
+ */
14
+ export {};
@@ -153,6 +153,17 @@ export type ToolRegistrationOptions = {
153
153
  * When omitted, the SDK's global default (2 retries) is used.
154
154
  * Set to 0 to disable retries for this tool. */
155
155
  maxRetries?: number;
156
+ /**
157
+ * Whether this tool's result may be served from the tool-result cache
158
+ * (default true).
159
+ *
160
+ * Set to `false` for a tool whose result is NOT a function of its arguments
161
+ * — anything reading or mutating live state. The cache is keyed by tool name
162
+ * plus arguments, so a stateful tool called twice with the same arguments
163
+ * replays its first answer for the whole TTL: a checklist that never updates,
164
+ * a queue that hands out the same item twice.
165
+ */
166
+ cacheable?: boolean;
156
167
  };
157
168
  /**
158
169
  * Tool execution result
@@ -2,7 +2,7 @@
2
2
  * Centralized model choices for CLI commands
3
3
  * Derives choices from model enums to ensure consistency
4
4
  */
5
- import { AIProviderName, OpenAIModels, AnthropicModels, GoogleAIModels, BedrockModels, VertexModels, MistralModels, OllamaModels, AzureOpenAIModels, LiteLLMModels, HuggingFaceModels, SageMakerModels, OpenRouterModels, DeepSeekModels, NvidiaNimModels, XaiModels, GroqModels, CohereModels, TogetherAIModels, FireworksModels, PerplexityModels, CloudflareModels, VoyageModels, JinaModels, StabilityModels, IdeogramModels, RecraftModels, ReplicateModels, } from "../constants/enums.js";
5
+ import { AIProviderName, OpenAIModels, AnthropicModels, GoogleAIModels, BedrockModels, VertexModels, MistralModels, OllamaModels, AzureOpenAIModels, LiteLLMModels, HuggingFaceModels, SageMakerModels, OpenRouterModels, DeepSeekModels, NvidiaNimModels, XaiModels, GroqModels, CerebrasModels, CohereModels, TogetherAIModels, FireworksModels, PerplexityModels, CloudflareModels, VoyageModels, JinaModels, StabilityModels, IdeogramModels, RecraftModels, ReplicateModels, } from "../constants/enums.js";
6
6
  /**
7
7
  * Top models per provider with descriptions for CLI prompts
8
8
  * These are curated lists of the most commonly used/recommended models
@@ -302,6 +302,21 @@ const TOP_MODELS_CONFIG = {
302
302
  description: "Mistral 8x7B MoE, 32K context",
303
303
  },
304
304
  ],
305
+ [AIProviderName.CEREBRAS]: [
306
+ {
307
+ model: CerebrasModels.LLAMA_3_3_70B,
308
+ description: "Recommended - Production default; wafer-scale speed",
309
+ },
310
+ {
311
+ model: CerebrasModels.LLAMA_3_1_8B,
312
+ description: "Lowest latency tier",
313
+ },
314
+ { model: CerebrasModels.QWEN_3_32B, description: "Qwen 3 32B" },
315
+ {
316
+ model: CerebrasModels.GPT_OSS_120B,
317
+ description: "OpenAI GPT-OSS 120B (open-weight)",
318
+ },
319
+ ],
305
320
  [AIProviderName.COHERE]: [
306
321
  {
307
322
  model: CohereModels.COMMAND_R_PLUS,
@@ -539,6 +554,7 @@ const MODEL_ENUMS = {
539
554
  [AIProviderName.LLAMACPP]: null,
540
555
  [AIProviderName.XAI]: XaiModels,
541
556
  [AIProviderName.GROQ]: GroqModels,
557
+ [AIProviderName.CEREBRAS]: CerebrasModels,
542
558
  [AIProviderName.COHERE]: CohereModels,
543
559
  [AIProviderName.TOGETHER_AI]: TogetherAIModels,
544
560
  [AIProviderName.FIREWORKS]: FireworksModels,
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Path containment guard for the sandboxed execution paths.
3
+ *
4
+ * `bashTool` does this check inline; `directTools.resolveWithinCwd` does a
5
+ * weaker, non-symlink-aware version against `process.cwd()`. Neither is
6
+ * reusable, and the background-command runner needs the strong form against a
7
+ * caller-declared root, so it lives here once.
8
+ *
9
+ * The load-bearing detail is **realpath**. A string comparison on resolved
10
+ * paths is defeated by a symlink: `<root>/escape → /etc` resolves lexically to
11
+ * `<root>/escape`, which passes, and then the command runs in `/etc`. Both
12
+ * sides are therefore resolved through the filesystem before they are
13
+ * compared, and a target that cannot be resolved is refused rather than
14
+ * assumed innocent.
15
+ *
16
+ * The `+ sep` suffix on the prefix test is the other one: without it
17
+ * `/home/app-evil` passes a containment check against `/home/app`.
18
+ *
19
+ * @module utils/pathSandbox
20
+ */
21
+ import type { PathSandboxResult } from "../types/index.js";
22
+ /**
23
+ * Resolve `target` and confirm it is `root` or lies inside it, with symlinks
24
+ * followed on both sides.
25
+ *
26
+ * Both must exist and be directories — a containment check on a path that is
27
+ * not there yet cannot be honest, because whatever appears later may be a
28
+ * symlink out.
29
+ *
30
+ * @param target Directory to check. Relative paths resolve against `root`.
31
+ * @param root Sandbox root the target must not escape.
32
+ * @returns The resolved REAL path, or the reason it was refused.
33
+ */
34
+ export declare function resolveWithinRoot(target: string, root: string): PathSandboxResult;
35
+ /**
36
+ * The file twin of {@link resolveWithinRoot}: contain a path that may name a
37
+ * file, or may not exist at all (a path argument to `git log`, say).
38
+ *
39
+ * A path that exists is resolved through its symlinks and checked directly. A
40
+ * path that does not exist is checked on its nearest EXISTING ancestor, which
41
+ * is the deepest point a symlink could redirect — everything below that is a
42
+ * name, not a link. Deleted files therefore stay askable about, and
43
+ * `../../etc/passwd` still does not.
44
+ *
45
+ * @param target Path to check. Relative paths resolve against `root`.
46
+ * @param root Sandbox root the target must not escape.
47
+ * @returns The resolved absolute path, or the reason it was refused.
48
+ */
49
+ export declare function resolvePathWithinRoot(target: string, root: string): PathSandboxResult;
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Path containment guard for the sandboxed execution paths.
3
+ *
4
+ * `bashTool` does this check inline; `directTools.resolveWithinCwd` does a
5
+ * weaker, non-symlink-aware version against `process.cwd()`. Neither is
6
+ * reusable, and the background-command runner needs the strong form against a
7
+ * caller-declared root, so it lives here once.
8
+ *
9
+ * The load-bearing detail is **realpath**. A string comparison on resolved
10
+ * paths is defeated by a symlink: `<root>/escape → /etc` resolves lexically to
11
+ * `<root>/escape`, which passes, and then the command runs in `/etc`. Both
12
+ * sides are therefore resolved through the filesystem before they are
13
+ * compared, and a target that cannot be resolved is refused rather than
14
+ * assumed innocent.
15
+ *
16
+ * The `+ sep` suffix on the prefix test is the other one: without it
17
+ * `/home/app-evil` passes a containment check against `/home/app`.
18
+ *
19
+ * @module utils/pathSandbox
20
+ */
21
+ import { realpathSync, statSync } from "node:fs";
22
+ import { basename, isAbsolute, join, resolve, sep } from "node:path";
23
+ /** Real path of an existing directory, or undefined. */
24
+ function realDirectory(candidate) {
25
+ try {
26
+ const real = realpathSync(candidate);
27
+ return statSync(real).isDirectory() ? real : undefined;
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ }
33
+ /** Real path of anything that exists — file, directory or otherwise. */
34
+ function realEntry(candidate) {
35
+ try {
36
+ return realpathSync(candidate);
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ }
42
+ function isInside(candidate, root) {
43
+ return candidate === root || candidate.startsWith(root + sep);
44
+ }
45
+ /**
46
+ * Resolve `target` and confirm it is `root` or lies inside it, with symlinks
47
+ * followed on both sides.
48
+ *
49
+ * Both must exist and be directories — a containment check on a path that is
50
+ * not there yet cannot be honest, because whatever appears later may be a
51
+ * symlink out.
52
+ *
53
+ * @param target Directory to check. Relative paths resolve against `root`.
54
+ * @param root Sandbox root the target must not escape.
55
+ * @returns The resolved REAL path, or the reason it was refused.
56
+ */
57
+ export function resolveWithinRoot(target, root) {
58
+ const realRoot = realDirectory(resolve(root));
59
+ if (!realRoot) {
60
+ return {
61
+ error: `Sandbox root "${root}" is not an existing directory. Point the policy's ` +
62
+ "cwdRoot at a directory that exists before starting commands.",
63
+ };
64
+ }
65
+ const requested = isAbsolute(target) ? target : resolve(realRoot, target);
66
+ const realTarget = realDirectory(requested);
67
+ if (!realTarget) {
68
+ return {
69
+ error: `Working directory "${target}" is not an existing directory. Name a directory ` +
70
+ `that exists inside ${realRoot}.`,
71
+ };
72
+ }
73
+ if (!isInside(realTarget, realRoot)) {
74
+ return {
75
+ error: `Access denied: "${target}" resolves to ${realTarget}, which is outside the ` +
76
+ `permitted root ${realRoot}. Run the command inside that root instead.`,
77
+ };
78
+ }
79
+ return { path: realTarget };
80
+ }
81
+ /**
82
+ * The file twin of {@link resolveWithinRoot}: contain a path that may name a
83
+ * file, or may not exist at all (a path argument to `git log`, say).
84
+ *
85
+ * A path that exists is resolved through its symlinks and checked directly. A
86
+ * path that does not exist is checked on its nearest EXISTING ancestor, which
87
+ * is the deepest point a symlink could redirect — everything below that is a
88
+ * name, not a link. Deleted files therefore stay askable about, and
89
+ * `../../etc/passwd` still does not.
90
+ *
91
+ * @param target Path to check. Relative paths resolve against `root`.
92
+ * @param root Sandbox root the target must not escape.
93
+ * @returns The resolved absolute path, or the reason it was refused.
94
+ */
95
+ export function resolvePathWithinRoot(target, root) {
96
+ const realRoot = realDirectory(resolve(root));
97
+ if (!realRoot) {
98
+ return { error: `Sandbox root "${root}" is not an existing directory.` };
99
+ }
100
+ const denied = {
101
+ error: `Access denied: "${target}" resolves outside the permitted root ${realRoot}. ` +
102
+ "Name a path inside it, relative to the root.",
103
+ };
104
+ const requested = isAbsolute(target)
105
+ ? resolve(target)
106
+ : resolve(realRoot, target);
107
+ const direct = realEntry(requested);
108
+ if (direct) {
109
+ return isInside(direct, realRoot) ? { path: direct } : denied;
110
+ }
111
+ let existing = resolve(requested, "..");
112
+ let suffix = basename(requested);
113
+ for (;;) {
114
+ const real = realDirectory(existing);
115
+ if (real) {
116
+ return isInside(real, realRoot)
117
+ ? { path: resolve(real, suffix) }
118
+ : denied;
119
+ }
120
+ const parent = resolve(existing, "..");
121
+ if (parent === existing) {
122
+ return denied;
123
+ }
124
+ suffix = join(basename(existing), suffix);
125
+ existing = parent;
126
+ }
127
+ }
@@ -135,6 +135,10 @@ export declare function createNvidiaNimConfig(): ProviderConfigOptions;
135
135
  * Creates xAI Grok provider configuration.
136
136
  */
137
137
  export declare function createXaiConfig(): ProviderConfigOptions;
138
+ /**
139
+ * Creates Cerebras provider configuration.
140
+ */
141
+ export declare function createCerebrasConfig(): ProviderConfigOptions;
138
142
  /**
139
143
  * Creates Groq provider configuration.
140
144
  */
@@ -451,6 +451,23 @@ export function createXaiConfig() {
451
451
  ],
452
452
  };
453
453
  }
454
+ /**
455
+ * Creates Cerebras provider configuration.
456
+ */
457
+ export function createCerebrasConfig() {
458
+ return {
459
+ providerName: "Cerebras",
460
+ envVarName: "CEREBRAS_API_KEY",
461
+ setupUrl: "https://cloud.cerebras.ai",
462
+ description: "API key",
463
+ instructions: [
464
+ "1. Visit: https://cloud.cerebras.ai",
465
+ "2. Sign in or create a free Cerebras account",
466
+ "3. Create an API key under API Keys",
467
+ "4. Set CEREBRAS_API_KEY in your .env file",
468
+ ],
469
+ };
470
+ }
454
471
  /**
455
472
  * Creates Groq provider configuration.
456
473
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "12.0.5",
3
+ "version": "12.2.0",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "TypeScript AI SDK with 24+ LLM providers behind one consistent API. MCP-native (connect any MCP server), voice TTS/STT/realtime, RAG, agents, memory, context compaction. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · DeepSeek · NVIDIA NIM and more.",
6
6
  "author": {
@@ -69,6 +69,10 @@
69
69
  "convert:specific": "tsx tools/automation/shellConverter.ts --specific",
70
70
  "// Testing (Continuous Test Suites)": "",
71
71
  "test": "pnpm exec tsx test/continuous-test-suite.ts",
72
+ "test:agent-delegation": "pnpm exec tsx test/continuous-test-suite-agent-delegation.ts",
73
+ "test:agent-tasks": "pnpm exec tsx test/continuous-test-suite-agent-tasks.ts",
74
+ "test:artifact-banking": "pnpm exec tsx test/continuous-test-suite-artifact-banking.ts",
75
+ "test:background-commands": "pnpm exec tsx test/continuous-test-suite-background-commands.ts",
72
76
  "test:client": "pnpm exec tsx test/continuous-test-suite-client.ts",
73
77
  "test:context": "pnpm exec tsx test/continuous-test-suite-context.ts",
74
78
  "test:evaluation": "pnpm exec tsx test/continuous-test-suite-evaluation.ts",