@juspay/neurolink 9.85.1 → 9.86.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 +6 -0
- package/dist/browser/neurolink.min.js +370 -362
- package/dist/context/contextCompactor.js +16 -2
- package/dist/context/stages/slidingWindowTruncator.js +76 -30
- package/dist/core/conversationMemoryManager.js +13 -2
- package/dist/core/redisConversationMemoryManager.js +10 -1
- package/dist/lib/context/contextCompactor.js +16 -2
- package/dist/lib/context/stages/slidingWindowTruncator.js +76 -30
- package/dist/lib/core/conversationMemoryManager.js +13 -2
- package/dist/lib/core/redisConversationMemoryManager.js +10 -1
- package/dist/lib/neurolink.d.ts +31 -6
- package/dist/lib/neurolink.js +163 -33
- package/dist/lib/skills/skillMatcher.d.ts +33 -4
- package/dist/lib/skills/skillMatcher.js +81 -6
- package/dist/lib/skills/skillSessionTracker.d.ts +52 -0
- package/dist/lib/skills/skillSessionTracker.js +150 -0
- package/dist/lib/skills/skillStoreRedis.d.ts +8 -0
- package/dist/lib/skills/skillStoreRedis.js +18 -2
- package/dist/lib/skills/skillStoreS3.d.ts +17 -2
- package/dist/lib/skills/skillStoreS3.js +78 -6
- package/dist/lib/skills/skillStores.d.ts +16 -2
- package/dist/lib/skills/skillStores.js +94 -5
- package/dist/lib/skills/skillTools.d.ts +26 -10
- package/dist/lib/skills/skillTools.js +190 -79
- package/dist/lib/skills/skillsManager.d.ts +25 -1
- package/dist/lib/skills/skillsManager.js +46 -4
- package/dist/lib/types/config.d.ts +5 -5
- package/dist/lib/types/conversation.d.ts +27 -0
- package/dist/lib/types/skills.d.ts +145 -14
- package/dist/lib/types/skills.js +3 -3
- package/dist/lib/utils/conversationMemory.d.ts +1 -1
- package/dist/lib/utils/conversationMemory.js +15 -2
- package/dist/neurolink.d.ts +31 -6
- package/dist/neurolink.js +163 -33
- package/dist/skills/skillMatcher.d.ts +33 -4
- package/dist/skills/skillMatcher.js +81 -6
- package/dist/skills/skillSessionTracker.d.ts +52 -0
- package/dist/skills/skillSessionTracker.js +149 -0
- package/dist/skills/skillStoreRedis.d.ts +8 -0
- package/dist/skills/skillStoreRedis.js +18 -2
- package/dist/skills/skillStoreS3.d.ts +17 -2
- package/dist/skills/skillStoreS3.js +78 -6
- package/dist/skills/skillStores.d.ts +16 -2
- package/dist/skills/skillStores.js +94 -5
- package/dist/skills/skillTools.d.ts +26 -10
- package/dist/skills/skillTools.js +190 -79
- package/dist/skills/skillsManager.d.ts +25 -1
- package/dist/skills/skillsManager.js +46 -4
- package/dist/types/config.d.ts +5 -5
- package/dist/types/conversation.d.ts +27 -0
- package/dist/types/skills.d.ts +145 -14
- package/dist/types/skills.js +3 -3
- package/dist/utils/conversationMemory.d.ts +1 -1
- package/dist/utils/conversationMemory.js +15 -2
- package/package.json +1 -1
|
@@ -8,11 +8,13 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { randomUUID } from "node:crypto";
|
|
10
10
|
import { logger } from "../utils/logger.js";
|
|
11
|
-
import { filterSkillIndex, formatSkillsPromptIndex } from "./skillMatcher.js";
|
|
11
|
+
import { filterSkillIndex, formatSkillsPromptIndex, renderSkillListing, sortSkillIndex, } from "./skillMatcher.js";
|
|
12
|
+
import { SkillSessionTracker } from "./skillSessionTracker.js";
|
|
12
13
|
import { createSkillStore } from "./skillStores.js";
|
|
13
14
|
const DEFAULT_MAX_MATCHES = 5;
|
|
14
15
|
const DEFAULT_PROMPT_INDEX_MAX_ITEMS = 50;
|
|
15
16
|
const DEFAULT_INDEX_CACHE_TTL_MS = 30_000;
|
|
17
|
+
const DEFAULT_LISTING_BUDGET_CHARS = 15_000;
|
|
16
18
|
export class SkillsManager {
|
|
17
19
|
config;
|
|
18
20
|
store;
|
|
@@ -20,11 +22,17 @@ export class SkillsManager {
|
|
|
20
22
|
cachedIndexAt = 0;
|
|
21
23
|
/** Serializes writes within this process — see requestMutation(). */
|
|
22
24
|
mutationQueue = Promise.resolve();
|
|
25
|
+
/** Per-session activation state (pinned skills). */
|
|
26
|
+
sessions = new SkillSessionTracker();
|
|
23
27
|
constructor(config) {
|
|
24
28
|
this.config = config;
|
|
25
29
|
this.store = createSkillStore(config.storage);
|
|
26
30
|
}
|
|
27
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Cached index read, sorted by name. TTL 0 disables caching. Sorting
|
|
33
|
+
* here (not per render) keeps every downstream listing byte-stable
|
|
34
|
+
* regardless of store enumeration order.
|
|
35
|
+
*/
|
|
28
36
|
async getIndex(forceRefresh = false) {
|
|
29
37
|
const ttl = this.config.indexCacheTtlMs ?? DEFAULT_INDEX_CACHE_TTL_MS;
|
|
30
38
|
const fresh = this.cachedIndex !== null &&
|
|
@@ -33,7 +41,7 @@ export class SkillsManager {
|
|
|
33
41
|
if (fresh && !forceRefresh && this.cachedIndex) {
|
|
34
42
|
return this.cachedIndex;
|
|
35
43
|
}
|
|
36
|
-
const index = await this.store.index();
|
|
44
|
+
const index = sortSkillIndex(await this.store.index());
|
|
37
45
|
this.cachedIndex = index;
|
|
38
46
|
this.cachedIndexAt = Date.now();
|
|
39
47
|
return index;
|
|
@@ -87,12 +95,46 @@ export class SkillsManager {
|
|
|
87
95
|
* nothing is visible. Never includes instructions.
|
|
88
96
|
*/
|
|
89
97
|
async buildPromptIndex(options) {
|
|
98
|
+
const items = await this.visibleItems(options);
|
|
99
|
+
return formatSkillsPromptIndex(items, this.config.promptIndexMaxItems ?? DEFAULT_PROMPT_INDEX_MAX_ITEMS);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Render the `<available_skills>` block for the use_skill tool
|
|
103
|
+
* description ("tool" discovery mode), or null when nothing is visible.
|
|
104
|
+
* Bounded by listingBudgetChars; entries are never dropped.
|
|
105
|
+
*/
|
|
106
|
+
async buildToolListing(options) {
|
|
107
|
+
const items = await this.visibleItems(options);
|
|
108
|
+
return renderSkillListing(items, this.config.listingBudgetChars ?? DEFAULT_LISTING_BUDGET_CHARS);
|
|
109
|
+
}
|
|
110
|
+
/** Visible (active, scope- and tag-filtered) index entries for one call. */
|
|
111
|
+
async visibleItems(options) {
|
|
90
112
|
let items = await this.list(options?.scopeId);
|
|
91
113
|
if (options?.tags && options.tags.length > 0) {
|
|
92
114
|
const wanted = options.tags.map((t) => t.toLowerCase());
|
|
93
115
|
items = items.filter((item) => (item.tags ?? []).some((t) => wanted.includes(t.toLowerCase())));
|
|
94
116
|
}
|
|
95
|
-
return
|
|
117
|
+
return items;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Read an auxiliary resource file bundled with a skill. Paths are
|
|
121
|
+
* relative to the skill; traversal segments are rejected. Null when the
|
|
122
|
+
* skill, the resource, or store resource support is absent.
|
|
123
|
+
*/
|
|
124
|
+
async getResource(idOrName, resourcePath) {
|
|
125
|
+
const normalized = resourcePath.replace(/\\/g, "/");
|
|
126
|
+
if (normalized.startsWith("/") ||
|
|
127
|
+
normalized.split("/").some((segment) => segment === "..")) {
|
|
128
|
+
throw new Error(`Invalid resource path "${resourcePath}" — must be relative to the skill directory`);
|
|
129
|
+
}
|
|
130
|
+
if (!this.store.getResource) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
const skill = await this.get(idOrName);
|
|
134
|
+
if (!skill) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
return this.store.getResource(skill.id, normalized);
|
|
96
138
|
}
|
|
97
139
|
/**
|
|
98
140
|
* Whether skill create/update/delete is enabled on this instance. Gates the
|
package/dist/types/config.d.ts
CHANGED
|
@@ -117,11 +117,11 @@ export type NeurolinkConstructorConfig = {
|
|
|
117
117
|
classifierRouter?: ClassifierRouterConfig;
|
|
118
118
|
/**
|
|
119
119
|
* Native skills: versioned, discoverable instruction packs (SOPs,
|
|
120
|
-
* playbooks) with progressive disclosure. When enabled,
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* See {@link SkillsConfig}.
|
|
120
|
+
* playbooks) with progressive disclosure. When enabled, each
|
|
121
|
+
* generate()/stream() call gets a skills discovery listing plus
|
|
122
|
+
* use_skill / read_skill_resource tools; activated skill instructions
|
|
123
|
+
* pin to the session so they are loaded once and replayed from history.
|
|
124
|
+
* Opt-in and fails open on read paths. See {@link SkillsConfig}.
|
|
125
125
|
*/
|
|
126
126
|
skills?: SkillsConfig;
|
|
127
127
|
};
|
|
@@ -250,6 +250,20 @@ export type ChatMessageMetadata = {
|
|
|
250
250
|
* payload from the local artifact store.
|
|
251
251
|
*/
|
|
252
252
|
artifactId?: string;
|
|
253
|
+
/**
|
|
254
|
+
* Marks a pinned skill-activation message: the full instructions of a
|
|
255
|
+
* skill loaded via use_skill, persisted into session history so later
|
|
256
|
+
* turns replay it verbatim instead of re-fetching the skill. Pinned
|
|
257
|
+
* skill messages are protected from sliding-window truncation and are
|
|
258
|
+
* re-included after memory summarization.
|
|
259
|
+
*/
|
|
260
|
+
isSkill?: boolean;
|
|
261
|
+
/** Skill id of a pinned skill-activation message. */
|
|
262
|
+
skillId?: string;
|
|
263
|
+
/** Skill name of a pinned skill-activation message. */
|
|
264
|
+
skillName?: string;
|
|
265
|
+
/** Skill version captured at activation (sessions pin the activated version). */
|
|
266
|
+
skillVersion?: number;
|
|
253
267
|
};
|
|
254
268
|
/**
|
|
255
269
|
* Chat message format for conversation history
|
|
@@ -383,6 +397,19 @@ export type StoreConversationTurnOptions = {
|
|
|
383
397
|
};
|
|
384
398
|
/** Gemini 3 thought signature for reasoning continuity across turns */
|
|
385
399
|
thoughtSignature?: string;
|
|
400
|
+
/**
|
|
401
|
+
* Pinned skill-activation messages (skills v2) recorded during this turn.
|
|
402
|
+
* Inserted between the user and assistant messages so replayed history
|
|
403
|
+
* mirrors the actual order: ask → skill loaded → answer. Stored verbatim —
|
|
404
|
+
* skill instructions are never truncated.
|
|
405
|
+
*
|
|
406
|
+
* Invariant for history consumers: a skill-bearing turn is a
|
|
407
|
+
* user → skill(user-role, metadata.isSkill) → assistant triplet, so
|
|
408
|
+
* stored history is NOT strictly pair-wise alternating. Pair-based
|
|
409
|
+
* logic must filter `metadata.isSkill` first (see slidingWindowTruncator
|
|
410
|
+
* for the canonical partition-and-reanchor pattern).
|
|
411
|
+
*/
|
|
412
|
+
skillMessages?: ChatMessage[];
|
|
386
413
|
};
|
|
387
414
|
/**
|
|
388
415
|
* Lightweight session metadata for efficient session listing
|
package/dist/types/skills.d.ts
CHANGED
|
@@ -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 (
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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()
|
|
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
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
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
|
-
|
|
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
|
-
/**
|
|
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 (
|
|
412
|
+
/** Master toggle for this call (listing + per-call tools). Default: true. */
|
|
289
413
|
enabled?: boolean;
|
|
290
|
-
/** Per-call override of SkillsConfig.
|
|
291
|
-
|
|
292
|
-
/** Scope filter for the
|
|
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
|
|
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
|
};
|
package/dist/types/skills.js
CHANGED
|
@@ -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 (
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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 = [
|
|
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.
|
|
3
|
+
"version": "9.86.0",
|
|
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": {
|