@juspay/neurolink 9.85.1 → 9.86.1

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 (63) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/browser/neurolink.min.js +370 -362
  3. package/dist/context/contextCompactor.js +16 -2
  4. package/dist/context/stages/slidingWindowTruncator.js +76 -30
  5. package/dist/core/conversationMemoryManager.js +13 -2
  6. package/dist/core/redisConversationMemoryManager.js +10 -1
  7. package/dist/lib/context/contextCompactor.js +16 -2
  8. package/dist/lib/context/stages/slidingWindowTruncator.js +76 -30
  9. package/dist/lib/core/conversationMemoryManager.js +13 -2
  10. package/dist/lib/core/redisConversationMemoryManager.js +10 -1
  11. package/dist/lib/neurolink.d.ts +31 -6
  12. package/dist/lib/neurolink.js +163 -33
  13. package/dist/lib/proxy/accountQuota.d.ts +5 -0
  14. package/dist/lib/proxy/accountQuota.js +8 -1
  15. package/dist/lib/server/routes/claudeProxyRoutes.d.ts +22 -6
  16. package/dist/lib/server/routes/claudeProxyRoutes.js +52 -8
  17. package/dist/lib/skills/skillMatcher.d.ts +33 -4
  18. package/dist/lib/skills/skillMatcher.js +81 -6
  19. package/dist/lib/skills/skillSessionTracker.d.ts +52 -0
  20. package/dist/lib/skills/skillSessionTracker.js +150 -0
  21. package/dist/lib/skills/skillStoreRedis.d.ts +8 -0
  22. package/dist/lib/skills/skillStoreRedis.js +18 -2
  23. package/dist/lib/skills/skillStoreS3.d.ts +17 -2
  24. package/dist/lib/skills/skillStoreS3.js +78 -6
  25. package/dist/lib/skills/skillStores.d.ts +16 -2
  26. package/dist/lib/skills/skillStores.js +94 -5
  27. package/dist/lib/skills/skillTools.d.ts +26 -10
  28. package/dist/lib/skills/skillTools.js +190 -79
  29. package/dist/lib/skills/skillsManager.d.ts +25 -1
  30. package/dist/lib/skills/skillsManager.js +46 -4
  31. package/dist/lib/types/config.d.ts +5 -5
  32. package/dist/lib/types/conversation.d.ts +27 -0
  33. package/dist/lib/types/skills.d.ts +145 -14
  34. package/dist/lib/types/skills.js +3 -3
  35. package/dist/lib/utils/conversationMemory.d.ts +1 -1
  36. package/dist/lib/utils/conversationMemory.js +15 -2
  37. package/dist/neurolink.d.ts +31 -6
  38. package/dist/neurolink.js +163 -33
  39. package/dist/proxy/accountQuota.d.ts +5 -0
  40. package/dist/proxy/accountQuota.js +8 -1
  41. package/dist/server/routes/claudeProxyRoutes.d.ts +22 -6
  42. package/dist/server/routes/claudeProxyRoutes.js +52 -8
  43. package/dist/skills/skillMatcher.d.ts +33 -4
  44. package/dist/skills/skillMatcher.js +81 -6
  45. package/dist/skills/skillSessionTracker.d.ts +52 -0
  46. package/dist/skills/skillSessionTracker.js +149 -0
  47. package/dist/skills/skillStoreRedis.d.ts +8 -0
  48. package/dist/skills/skillStoreRedis.js +18 -2
  49. package/dist/skills/skillStoreS3.d.ts +17 -2
  50. package/dist/skills/skillStoreS3.js +78 -6
  51. package/dist/skills/skillStores.d.ts +16 -2
  52. package/dist/skills/skillStores.js +94 -5
  53. package/dist/skills/skillTools.d.ts +26 -10
  54. package/dist/skills/skillTools.js +190 -79
  55. package/dist/skills/skillsManager.d.ts +25 -1
  56. package/dist/skills/skillsManager.js +46 -4
  57. package/dist/types/config.d.ts +5 -5
  58. package/dist/types/conversation.d.ts +27 -0
  59. package/dist/types/skills.d.ts +145 -14
  60. package/dist/types/skills.js +3 -3
  61. package/dist/utils/conversationMemory.d.ts +1 -1
  62. package/dist/utils/conversationMemory.js +15 -2
  63. package/package.json +1 -1
@@ -6,15 +6,28 @@
6
6
  *
7
7
  * Architecture mirrors the Hippocampus memory subsystem: a `skills` config
8
8
  * on the NeuroLink constructor lazily initializes a SkillsManager, which
9
- * auto-registers built-in tools (search_skills / list_skills, plus gated
10
- * mutation tools) and optionally injects a compact skills index into the
11
- * system prompt of each generate()/stream() call.
9
+ * auto-registers built-in tools (list_skills, plus gated mutation tools)
10
+ * and augments each generate()/stream() call with the discovery listing
11
+ * plus per-call use_skill / read_skill_resource tools.
12
12
  *
13
13
  * Naming: every exported type carries a `Skill` prefix to satisfy the
14
14
  * `unique-type-names` ESLint rule.
15
15
  */
16
+ import type { ChatMessage } from "./conversation.js";
16
17
  /** Visibility of a skill: available everywhere, or only in specific scopes. */
17
18
  export type SkillScopeKind = "global" | "scoped";
19
+ /**
20
+ * Reference to an auxiliary file bundled with a skill (progressive
21
+ * disclosure level 3). Resources are read into context on demand via the
22
+ * read_skill_resource tool — a skill's SKILL.md should stay lean and point
23
+ * to resources for rarely-needed detail.
24
+ */
25
+ export type SkillResourceRef = {
26
+ /** Path relative to the skill's directory, e.g. "references/edge-cases.md". */
27
+ path: string;
28
+ /** Size in bytes when known (listing hint only). */
29
+ size?: number;
30
+ };
18
31
  /** Lifecycle status. Deletes are soft — deprecated skills stay in storage. */
19
32
  export type SkillLifecycleStatus = "active" | "deprecated";
20
33
  /**
@@ -47,6 +60,12 @@ export type SkillDefinition = {
47
60
  createdAt?: string;
48
61
  /** ISO timestamp of last update. */
49
62
  updatedAt?: string;
63
+ /**
64
+ * Auxiliary files bundled with the skill, readable on demand through
65
+ * read_skill_resource. Populated by stores that support resources
66
+ * (directory-layout filesystem skills, S3, Redis).
67
+ */
68
+ resources?: SkillResourceRef[];
50
69
  /** Free-form host metadata (audit fields, approval references, …). */
51
70
  metadata?: Record<string, unknown>;
52
71
  };
@@ -69,12 +88,24 @@ export type SkillStore = {
69
88
  index(): Promise<SkillIndexItem[]>;
70
89
  /** Optional: drop any internal caches (called after mutations). */
71
90
  invalidate?(): void;
91
+ /**
92
+ * Optional: fetch an auxiliary resource file bundled with a skill.
93
+ * `resourcePath` is relative to the skill (e.g. "references/forms.md").
94
+ * Null when the skill or resource is absent. Stores without resource
95
+ * support simply omit this method.
96
+ */
97
+ getResource?(id: string, resourcePath: string): Promise<string | null>;
72
98
  };
73
99
  /** In-process store, optionally seeded. Good for tests and embedded use. */
74
100
  export type SkillMemoryStorageConfig = {
75
101
  type: "memory";
76
102
  /** Initial skills to seed the store with. */
77
103
  skills?: SkillDefinition[];
104
+ /**
105
+ * Resource file contents keyed by skill id → relative path.
106
+ * E.g. `{ "my-skill": { "references/forms.md": "..." } }`.
107
+ */
108
+ resources?: Record<string, Record<string, string>>;
78
109
  };
79
110
  /**
80
111
  * Directory-backed store. Reads three layouts:
@@ -154,6 +185,15 @@ export type SkillS3IndexDocument = {
154
185
  lastUpdated: string;
155
186
  skills: SkillIndexItem[];
156
187
  };
188
+ /** Result of a conditional (ETag) object read. */
189
+ export type SkillS3ConditionalGetResult = {
190
+ /** Object body; null when the key is absent. */
191
+ body: string | null;
192
+ /** ETag of the returned body, for the next conditional read. */
193
+ etag?: string;
194
+ /** True when the object is unchanged since the supplied ETag (no body). */
195
+ notModified?: boolean;
196
+ };
157
197
  /**
158
198
  * Minimal object-storage operations the S3 skill store runs on. The
159
199
  * default implementation is created lazily from @aws-sdk/client-s3;
@@ -166,9 +206,15 @@ export type SkillS3ObjectOps = {
166
206
  deleteObject(key: string): Promise<void>;
167
207
  /** List all object keys under a prefix (paginated internally). */
168
208
  listKeys(prefix: string): Promise<string[]>;
209
+ /**
210
+ * Optional: ETag-conditional read (If-None-Match). Used for index.json
211
+ * refreshes so an unchanged index costs a 304 instead of a full download.
212
+ * Ops without it fall back to plain getObject.
213
+ */
214
+ getObjectConditional?(key: string, etag?: string): Promise<SkillS3ConditionalGetResult>;
169
215
  };
170
216
  export type SkillsStorageConfig = SkillMemoryStorageConfig | SkillFilesystemStorageConfig | SkillS3StorageConfig | SkillRedisStorageConfig | SkillCustomStorageConfig;
171
- /** Query accepted by SkillsManager.search() and the search_skills tool. */
217
+ /** Query accepted by SkillsManager.search() (programmatic + CLI search). */
172
218
  export type SkillSearchQuery = {
173
219
  /** Keyword matched (case-insensitive substring) against name, displayName, and description. */
174
220
  query?: string;
@@ -239,14 +285,38 @@ export type SkillsConfig = {
239
285
  /** Persistence backend. Default: `{ type: "memory" }`. */
240
286
  storage?: SkillsStorageConfig;
241
287
  /**
242
- * Inject a compact skills index (names + descriptions, never instructions)
243
- * into the system prompt of each generate()/stream() call. Default: true.
244
- * Set false for curator-style pure tool-driven disclosure.
288
+ * Where the skills listing (names + descriptions, never instructions)
289
+ * surfaces for model-driven discovery:
290
+ * - "tool" (default): an `<available_skills>` block embedded in the
291
+ * use_skill tool description — the Claude Code pattern. Keeps the
292
+ * host's system prompt untouched and the listing cache-stable.
293
+ * - "system-prompt": a "## Available Skills" index appended to the
294
+ * system prompt instead.
295
+ * - "none": no listing anywhere; discovery only via list_skills.
296
+ */
297
+ discovery?: SkillDiscoveryMode;
298
+ /**
299
+ * Character budget for the "tool" discovery listing. When the full
300
+ * listing exceeds it, every description is shortened uniformly (first
301
+ * sentence, then a hard cap) so the render stays a pure function of the
302
+ * index — byte-stable across calls; names are never dropped.
303
+ * Default: 15000.
245
304
  */
246
- promptIndex?: boolean;
305
+ listingBudgetChars?: number;
306
+ /**
307
+ * Pin activated skill instructions into session history so later turns
308
+ * replay them verbatim (byte-stable, provider-cacheable) instead of
309
+ * re-fetching the skill. Requires conversation memory + a sessionId on
310
+ * the call. Default: true.
311
+ */
312
+ sessionPersistence?: boolean;
247
313
  /** Maximum skills hydrated (with instructions) per search. Default: 5. */
248
314
  maxMatches?: number;
249
- /** Maximum entries rendered in the prompt index before truncation. Default: 50. */
315
+ /**
316
+ * Maximum entries rendered by the "system-prompt" discovery mode before
317
+ * truncation. Default: 50. The "tool" mode is bounded by
318
+ * listingBudgetChars instead and never drops entries.
319
+ */
250
320
  promptIndexMaxItems?: number;
251
321
  /** Index cache TTL in milliseconds. Default: 30000. 0 disables caching. */
252
322
  indexCacheTtlMs?: number;
@@ -265,6 +335,30 @@ export type SkillsConfig = {
265
335
  */
266
336
  onMutationRequest?: (action: SkillMutationAction) => Promise<SkillMutationDecision>;
267
337
  };
338
+ /** Where the skills discovery listing surfaces. See SkillsConfig.discovery. */
339
+ export type SkillDiscoveryMode = "tool" | "system-prompt" | "none";
340
+ /**
341
+ * One activated skill in a session: which skill, at which version, when.
342
+ * Sessions pin the version active at activation time — a mid-session skill
343
+ * update never mutates instructions the model has already loaded.
344
+ */
345
+ export type SkillActivationRecord = {
346
+ skillId: string;
347
+ name: string;
348
+ version: number;
349
+ /** ISO timestamp of activation. */
350
+ activatedAt: string;
351
+ };
352
+ /**
353
+ * Structural view of the per-session activation tracker consumed by the
354
+ * skill tools factory.
355
+ */
356
+ export type SkillSessionStateLike = {
357
+ isActive: (sessionId: string, skillId: string, name: string) => boolean;
358
+ getActivation: (sessionId: string, skillId: string, name?: string) => SkillActivationRecord | undefined;
359
+ recordActivation: (sessionId: string, skill: SkillDefinition) => ChatMessage;
360
+ hydrate: (sessionId: string, storedMessages: ChatMessage[]) => void;
361
+ };
268
362
  /**
269
363
  * Structural view of SkillsManager consumed by the skill tools factory —
270
364
  * keeps skillTools.ts decoupled from the concrete manager class.
@@ -272,8 +366,38 @@ export type SkillsConfig = {
272
366
  export type SkillsManagerLike = {
273
367
  search: (query: SkillSearchQuery) => Promise<SkillDefinition[]>;
274
368
  list: (scopeId?: string) => Promise<SkillIndexItem[]>;
369
+ get: (idOrName: string) => Promise<SkillDefinition | null>;
370
+ getResource: (idOrName: string, resourcePath: string) => Promise<string | null>;
371
+ sessions: SkillSessionStateLike;
275
372
  requestMutation: (action: SkillMutationAction) => Promise<SkillMutationResult>;
276
373
  };
374
+ /**
375
+ * Per-call context bound into the use_skill / read_skill_resource tools at
376
+ * injection time (prepareGenerate/prepareStream). The sessionId is captured
377
+ * by closure so activation state is tracked without relying on runtime tool
378
+ * context plumbing.
379
+ */
380
+ export type SkillCallToolsContext = {
381
+ /** Session the call belongs to; absent → activation state is per-turn only. */
382
+ sessionId?: string;
383
+ /** Scope filter applied when resolving skills for this call. */
384
+ scopeId?: string;
385
+ /**
386
+ * Pin activated instructions into session history after the turn.
387
+ * Mirrors SkillsConfig.sessionPersistence resolved for this call.
388
+ */
389
+ sessionPersistence: boolean;
390
+ /** Discovery mode resolved for this call — shapes the use_skill description. */
391
+ discovery: SkillDiscoveryMode;
392
+ /** Rendered `<available_skills>` block for "tool" discovery; null when empty. */
393
+ listing?: string | null;
394
+ /**
395
+ * Stored session history loader used to hydrate activation state before
396
+ * every dedup check (restart/multi-instance/failed-persistence safety).
397
+ * Invoked once per use_skill / read_skill_resource attempt.
398
+ */
399
+ getStoredMessages?: (sessionId: string) => Promise<ChatMessage[]>;
400
+ };
277
401
  /** Options for the createSkillTools factory. */
278
402
  export type SkillToolsOptions = {
279
403
  /** Include skill_create / skill_update / skill_delete. Default: false. */
@@ -285,12 +409,19 @@ export type SkillToolsOptions = {
285
409
  * config (same precedence convention as per-call credentials).
286
410
  */
287
411
  export type SkillsCallOptions = {
288
- /** Master toggle for this call (prompt index only tools stay registered). Default: true. */
412
+ /** Master toggle for this call (listing + per-call tools). Default: true. */
289
413
  enabled?: boolean;
290
- /** Per-call override of SkillsConfig.promptIndex. */
291
- promptIndex?: boolean;
292
- /** Scope filter for the prompt index on this call. Overrides defaultScopeId. */
414
+ /** Per-call override of SkillsConfig.discovery. */
415
+ discovery?: SkillDiscoveryMode;
416
+ /** Scope filter for the listing and skill resolution on this call. Overrides defaultScopeId. */
293
417
  scopeId?: string;
294
- /** Restrict the prompt index to skills carrying at least one of these tags. */
418
+ /** Restrict the listing to skills carrying at least one of these tags. */
295
419
  tags?: string[];
420
+ /**
421
+ * Skill names to activate at the start of this call: their full
422
+ * instructions are injected up front (and pinned to the session when
423
+ * sessionPersistence is on), without waiting for the model to invoke
424
+ * use_skill. Already-active skills are skipped.
425
+ */
426
+ preload?: string[];
296
427
  };
@@ -6,9 +6,9 @@
6
6
  *
7
7
  * Architecture mirrors the Hippocampus memory subsystem: a `skills` config
8
8
  * on the NeuroLink constructor lazily initializes a SkillsManager, which
9
- * auto-registers built-in tools (search_skills / list_skills, plus gated
10
- * mutation tools) and optionally injects a compact skills index into the
11
- * system prompt of each generate()/stream() call.
9
+ * auto-registers built-in tools (list_skills, plus gated mutation tools)
10
+ * and augments each generate()/stream() call with the discovery listing
11
+ * plus per-call use_skill / read_skill_resource tools.
12
12
  *
13
13
  * Naming: every exported type carries a `Skill` prefix to satisfy the
14
14
  * `unique-type-names` ESLint rule.
@@ -28,7 +28,7 @@ export declare function getConversationMessages(conversationMemory: Conversation
28
28
  * Store conversation turn for future context
29
29
  * Saves user messages and AI responses for conversation memory
30
30
  */
31
- export declare function storeConversationTurn(conversationMemory: ConversationMemoryManager | RedisConversationMemoryManager | null | undefined, originalOptions: TextGenerationOptions, result: TextGenerationResult, startTimeStamp?: Date | undefined, requestId?: string): Promise<void>;
31
+ export declare function storeConversationTurn(conversationMemory: ConversationMemoryManager | RedisConversationMemoryManager | null | undefined, originalOptions: TextGenerationOptions, result: TextGenerationResult, startTimeStamp?: Date | undefined, requestId?: string, skillMessages?: ChatMessage[]): Promise<void>;
32
32
  /**
33
33
  * Build context messages from pointer onwards (token-based memory)
34
34
  * Returns summary message (if exists) + all messages after the summarized pointer
@@ -227,7 +227,7 @@ export async function getConversationMessages(conversationMemory, options) {
227
227
  * Store conversation turn for future context
228
228
  * Saves user messages and AI responses for conversation memory
229
229
  */
230
- export async function storeConversationTurn(conversationMemory, originalOptions, result, startTimeStamp, requestId) {
230
+ export async function storeConversationTurn(conversationMemory, originalOptions, result, startTimeStamp, requestId, skillMessages) {
231
231
  logger.debug("[conversationMemoryUtils] storeConversationTurn called", {
232
232
  requestId,
233
233
  hasMemory: !!conversationMemory,
@@ -369,6 +369,9 @@ export async function storeConversationTurn(conversationMemory, originalOptions,
369
369
  enableSummarization: originalOptions.enableSummarization,
370
370
  requestId,
371
371
  events: toolActivityEvents,
372
+ ...(skillMessages && skillMessages.length > 0
373
+ ? { skillMessages }
374
+ : {}),
372
375
  tokenUsage: result.usage
373
376
  ? {
374
377
  inputTokens: result.usage.input,
@@ -450,6 +453,12 @@ export function buildContextFromPointer(session, requestId) {
450
453
  return session.messages;
451
454
  }
452
455
  const messagesAfterPointer = session.messages.slice(pointerIndex + 1);
456
+ // Pinned skill instructions must survive summarization: any skill message
457
+ // that fell behind the pointer is re-included verbatim after the summary,
458
+ // so an activated skill keeps its full instructions for the whole session.
459
+ const pinnedSkillMessages = session.messages
460
+ .slice(0, pointerIndex + 1)
461
+ .filter((msg) => msg.metadata?.isSkill);
453
462
  // Construct context: summary message + recent messages
454
463
  const summaryMessage = {
455
464
  id: `summary-${session.summarizedUpToMessageId}`,
@@ -468,7 +477,11 @@ export function buildContextFromPointer(session, requestId) {
468
477
  totalMessages: session.messages.length,
469
478
  summaryLength: session.summarizedMessage.length,
470
479
  });
471
- const contextMessages = [summaryMessage, ...messagesAfterPointer];
480
+ const contextMessages = [
481
+ summaryMessage,
482
+ ...pinnedSkillMessages,
483
+ ...messagesAfterPointer,
484
+ ];
472
485
  // Log context built for LLM with structural metadata
473
486
  const totalChars = contextMessages.reduce((sum, msg) => sum + msg.content.length, 0);
474
487
  logger.info("[ConversationMemory] Context built for LLM", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "9.85.1",
3
+ "version": "9.86.1",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applications with 21+ providers: OpenAI, Anthropic, Google AI Studio, Google Vertex, AWS Bedrock, Azure OpenAI, Mistral, LiteLLM, SageMaker, Hugging Face, Ollama, OpenAI-compatible, OpenRouter, DeepSeek, NVIDIA NIM, LM Studio, llama.cpp, plus voice (OpenAI TTS, ElevenLabs, Deepgram, Azure Speech).",
6
6
  "author": {