assistme 0.8.5 → 0.8.7
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.
|
@@ -2876,6 +2876,10 @@ var SkillManager = class {
|
|
|
2876
2876
|
skills = /* @__PURE__ */ new Map();
|
|
2877
2877
|
idfCache = /* @__PURE__ */ new Map();
|
|
2878
2878
|
userId = null;
|
|
2879
|
+
/** Tracks the in-flight loadFromDb() promise so callers can await it. */
|
|
2880
|
+
loadPromise = null;
|
|
2881
|
+
/** True once loadFromDb() completes without error (even with 0 skills). */
|
|
2882
|
+
loaded = false;
|
|
2879
2883
|
/** Bounded cache for findRelevant() — keyed by normalized prompt, invalidated on skill changes. */
|
|
2880
2884
|
relevanceCache = new LRUCache(MAX_RELEVANCE_CACHE_ENTRIES);
|
|
2881
2885
|
/** Bounded, TTL-aware cache for marketplace discover results. */
|
|
@@ -2885,6 +2889,42 @@ var SkillManager = class {
|
|
|
2885
2889
|
}
|
|
2886
2890
|
async loadFromDb() {
|
|
2887
2891
|
if (!this.userId) return;
|
|
2892
|
+
if (this.loadPromise) return this.loadPromise;
|
|
2893
|
+
this.loadPromise = this._doLoadFromDb();
|
|
2894
|
+
try {
|
|
2895
|
+
await this.loadPromise;
|
|
2896
|
+
} finally {
|
|
2897
|
+
this.loadPromise = null;
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
/**
|
|
2901
|
+
* Guarantee that loadFromDb() has completed at least once.
|
|
2902
|
+
* Awaits any in-flight load; retries once if the initial load failed.
|
|
2903
|
+
* Safe and cheap to call repeatedly — no-ops after the first success.
|
|
2904
|
+
*/
|
|
2905
|
+
async ensureLoaded() {
|
|
2906
|
+
if (this.loadPromise) await this.loadPromise;
|
|
2907
|
+
if (!this.loaded && this.userId) {
|
|
2908
|
+
await this.loadFromDb();
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
/**
|
|
2912
|
+
* Get a skill by name, with a DB search fallback.
|
|
2913
|
+
* Handles: race conditions, failed initial loads, and skills added externally
|
|
2914
|
+
* after the initial load (e.g. created via UI while agent is running).
|
|
2915
|
+
*/
|
|
2916
|
+
async getWithDbFallback(name) {
|
|
2917
|
+
await this.ensureLoaded();
|
|
2918
|
+
const skill = this.skills.get(name);
|
|
2919
|
+
if (skill) return skill;
|
|
2920
|
+
const dbResults = await searchSkillsInDb(name, 5);
|
|
2921
|
+
const match = dbResults?.find((r) => r.name === name);
|
|
2922
|
+
if (!match) return void 0;
|
|
2923
|
+
this.loaded = false;
|
|
2924
|
+
await this.loadFromDb();
|
|
2925
|
+
return this.skills.get(name);
|
|
2926
|
+
}
|
|
2927
|
+
async _doLoadFromDb() {
|
|
2888
2928
|
try {
|
|
2889
2929
|
const data = await callMcpHandler("skill.load");
|
|
2890
2930
|
this.skills.clear();
|
|
@@ -2894,6 +2934,7 @@ var SkillManager = class {
|
|
|
2894
2934
|
this.skills.set(row.name, this.rowToSkill(row));
|
|
2895
2935
|
}
|
|
2896
2936
|
this.rebuildIdfCache();
|
|
2937
|
+
this.loaded = true;
|
|
2897
2938
|
if (this.skills.size > 0) {
|
|
2898
2939
|
log.info(`Loaded ${this.skills.size} skill(s) from DB`);
|
|
2899
2940
|
}
|
package/dist/index.js
CHANGED
package/dist/workers/entry.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
pollActionResponse,
|
|
20
20
|
resetEventSequence,
|
|
21
21
|
setActionRequest
|
|
22
|
-
} from "../chunk-
|
|
22
|
+
} from "../chunk-QBXD76HA.js";
|
|
23
23
|
import {
|
|
24
24
|
EDSGER_PRODUCT_SLUG,
|
|
25
25
|
JobRunner,
|
|
@@ -1778,7 +1778,7 @@ function createAgentToolsServer(deps) {
|
|
|
1778
1778
|
description: z2.string().optional().describe("Updated description (optional)")
|
|
1779
1779
|
},
|
|
1780
1780
|
async (args) => {
|
|
1781
|
-
const existing = skillManager.
|
|
1781
|
+
const existing = await skillManager.getWithDbFallback(args.name);
|
|
1782
1782
|
if (!existing) {
|
|
1783
1783
|
const available = skillManager.getAll().map((s) => s.name).join(", ");
|
|
1784
1784
|
return {
|
|
@@ -1824,7 +1824,7 @@ function createAgentToolsServer(deps) {
|
|
|
1824
1824
|
arguments: z2.string().optional().describe("Arguments to pass to the skill (replaces $ARGUMENTS placeholders)")
|
|
1825
1825
|
},
|
|
1826
1826
|
async (args) => {
|
|
1827
|
-
let skill = skillManager.
|
|
1827
|
+
let skill = await skillManager.getWithDbFallback(args.name);
|
|
1828
1828
|
if (!skill) {
|
|
1829
1829
|
const confirmResult = await confirmMarketplaceSkill(
|
|
1830
1830
|
skillManager,
|
|
@@ -2138,7 +2138,7 @@ Use \`ask_user\` to request these from the user, or create them yourself (e.g. r
|
|
|
2138
2138
|
author_name: z2.string().optional().describe("Your display name as the author")
|
|
2139
2139
|
},
|
|
2140
2140
|
async (args) => {
|
|
2141
|
-
const skill = skillManager.
|
|
2141
|
+
const skill = await skillManager.getWithDbFallback(args.name);
|
|
2142
2142
|
if (!skill) {
|
|
2143
2143
|
return {
|
|
2144
2144
|
content: [
|
|
@@ -3321,6 +3321,7 @@ var TaskProcessor = class {
|
|
|
3321
3321
|
let tokenUsage;
|
|
3322
3322
|
try {
|
|
3323
3323
|
await emitEvent(task.id, "status_change", { status: "running" });
|
|
3324
|
+
await this.skillManager.ensureLoaded();
|
|
3324
3325
|
const systemPrompt = await buildSystemPrompt(task, {
|
|
3325
3326
|
memoryManager: this.memoryManager,
|
|
3326
3327
|
skillManager: this.skillManager,
|
package/package.json
CHANGED
package/src/agent/processor.ts
CHANGED
|
@@ -181,6 +181,9 @@ export class TaskProcessor {
|
|
|
181
181
|
try {
|
|
182
182
|
await emitEvent(task.id, "status_change", { status: "running" });
|
|
183
183
|
|
|
184
|
+
// Guarantee skills are loaded before building prompt or running tools
|
|
185
|
+
await this.skillManager.ensureLoaded();
|
|
186
|
+
|
|
184
187
|
// Build system prompt with memories + skills + history
|
|
185
188
|
const systemPrompt = await buildSystemPrompt(task, {
|
|
186
189
|
memoryManager: this.memoryManager,
|
package/src/agent/skills.ts
CHANGED
|
@@ -63,6 +63,11 @@ export class SkillManager {
|
|
|
63
63
|
private idfCache: Map<string, number> = new Map();
|
|
64
64
|
private userId: string | null = null;
|
|
65
65
|
|
|
66
|
+
/** Tracks the in-flight loadFromDb() promise so callers can await it. */
|
|
67
|
+
private loadPromise: Promise<void> | null = null;
|
|
68
|
+
/** True once loadFromDb() completes without error (even with 0 skills). */
|
|
69
|
+
private loaded = false;
|
|
70
|
+
|
|
66
71
|
/** Bounded cache for findRelevant() — keyed by normalized prompt, invalidated on skill changes. */
|
|
67
72
|
private relevanceCache = new LRUCache<
|
|
68
73
|
string,
|
|
@@ -79,6 +84,52 @@ export class SkillManager {
|
|
|
79
84
|
async loadFromDb(): Promise<void> {
|
|
80
85
|
if (!this.userId) return;
|
|
81
86
|
|
|
87
|
+
// Deduplicate concurrent loads: reuse existing in-flight promise
|
|
88
|
+
if (this.loadPromise) return this.loadPromise;
|
|
89
|
+
|
|
90
|
+
this.loadPromise = this._doLoadFromDb();
|
|
91
|
+
try {
|
|
92
|
+
await this.loadPromise;
|
|
93
|
+
} finally {
|
|
94
|
+
this.loadPromise = null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Guarantee that loadFromDb() has completed at least once.
|
|
100
|
+
* Awaits any in-flight load; retries once if the initial load failed.
|
|
101
|
+
* Safe and cheap to call repeatedly — no-ops after the first success.
|
|
102
|
+
*/
|
|
103
|
+
async ensureLoaded(): Promise<void> {
|
|
104
|
+
if (this.loadPromise) await this.loadPromise;
|
|
105
|
+
if (!this.loaded && this.userId) {
|
|
106
|
+
await this.loadFromDb();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get a skill by name, with a DB search fallback.
|
|
112
|
+
* Handles: race conditions, failed initial loads, and skills added externally
|
|
113
|
+
* after the initial load (e.g. created via UI while agent is running).
|
|
114
|
+
*/
|
|
115
|
+
async getWithDbFallback(name: string): Promise<Skill | undefined> {
|
|
116
|
+
await this.ensureLoaded();
|
|
117
|
+
|
|
118
|
+
const skill = this.skills.get(name);
|
|
119
|
+
if (skill) return skill;
|
|
120
|
+
|
|
121
|
+
// In-memory miss: check if the skill exists in DB via search
|
|
122
|
+
const dbResults = await searchSkillsInDb(name, 5);
|
|
123
|
+
const match = dbResults?.find((r) => r.name === name);
|
|
124
|
+
if (!match) return undefined;
|
|
125
|
+
|
|
126
|
+
// Found in DB — reload all skills into memory and return
|
|
127
|
+
this.loaded = false; // force reload
|
|
128
|
+
await this.loadFromDb();
|
|
129
|
+
return this.skills.get(name);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private async _doLoadFromDb(): Promise<void> {
|
|
82
133
|
try {
|
|
83
134
|
const data = await callMcpHandler<unknown[]>("skill.load");
|
|
84
135
|
|
|
@@ -90,6 +141,7 @@ export class SkillManager {
|
|
|
90
141
|
}
|
|
91
142
|
|
|
92
143
|
this.rebuildIdfCache();
|
|
144
|
+
this.loaded = true;
|
|
93
145
|
|
|
94
146
|
if (this.skills.size > 0) {
|
|
95
147
|
log.info(`Loaded ${this.skills.size} skill(s) from DB`);
|
|
@@ -173,7 +173,8 @@ export function createAgentToolsServer(deps: AgentToolsDeps): McpSdkServerConfig
|
|
|
173
173
|
description: z.string().optional().describe("Updated description (optional)"),
|
|
174
174
|
},
|
|
175
175
|
async (args) => {
|
|
176
|
-
|
|
176
|
+
// Use DB fallback to handle race condition with async loadFromDb
|
|
177
|
+
const existing = await skillManager.getWithDbFallback(args.name);
|
|
177
178
|
if (!existing) {
|
|
178
179
|
const available = skillManager
|
|
179
180
|
.getAll()
|
|
@@ -228,7 +229,8 @@ export function createAgentToolsServer(deps: AgentToolsDeps): McpSdkServerConfig
|
|
|
228
229
|
.describe("Arguments to pass to the skill (replaces $ARGUMENTS placeholders)"),
|
|
229
230
|
},
|
|
230
231
|
async (args) => {
|
|
231
|
-
|
|
232
|
+
// Use DB fallback to handle race condition with async loadFromDb
|
|
233
|
+
let skill = await skillManager.getWithDbFallback(args.name);
|
|
232
234
|
|
|
233
235
|
// If not in user's collection, try marketplace — requires user confirmation
|
|
234
236
|
if (!skill) {
|
|
@@ -541,7 +543,7 @@ export function createAgentToolsServer(deps: AgentToolsDeps): McpSdkServerConfig
|
|
|
541
543
|
author_name: z.string().optional().describe("Your display name as the author"),
|
|
542
544
|
},
|
|
543
545
|
async (args) => {
|
|
544
|
-
const skill = skillManager.
|
|
546
|
+
const skill = await skillManager.getWithDbFallback(args.name);
|
|
545
547
|
if (!skill) {
|
|
546
548
|
return {
|
|
547
549
|
content: [
|