assistme 0.2.2 → 0.2.4
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/dist/index.js +87 -356
- package/package.json +1 -1
- package/src/agent/mcp-servers.ts +53 -123
- package/src/agent/processor.test.ts +1 -4
- package/src/agent/processor.ts +6 -30
- package/src/agent/skill-extractor.ts +0 -439
- package/src/agent/skills.ts +23 -21
- package/src/agent/memory-extractor.ts +0 -128
- package/src/utils/validation.test.ts +0 -153
- package/src/utils/validation.ts +0 -101
package/src/agent/mcp-servers.ts
CHANGED
|
@@ -10,7 +10,6 @@ import { log } from "../utils/logger.js";
|
|
|
10
10
|
import type { MemoryManager, MemoryCategory } from "./memory.js";
|
|
11
11
|
import type { SkillManager } from "./skills.js";
|
|
12
12
|
import { substituteArguments, preprocessDynamicContext } from "./skills.js";
|
|
13
|
-
import { decomposeJob, generateSkillFromDescription } from "./skill-extractor.js";
|
|
14
13
|
import { getSupabase } from "../db/supabase.js";
|
|
15
14
|
import { JobRunner } from "./job-runner.js";
|
|
16
15
|
import {
|
|
@@ -405,8 +404,9 @@ export function createAgentToolsServer(deps: AgentToolsDeps): McpSdkServerConfig
|
|
|
405
404
|
),
|
|
406
405
|
tool(
|
|
407
406
|
"skill_generate",
|
|
408
|
-
"
|
|
409
|
-
"
|
|
407
|
+
"Prepare context for generating skills from a job description. Returns existing skills and job info " +
|
|
408
|
+
"so you can analyze the job and create skills using skill_create + skill_add. " +
|
|
409
|
+
"After creating all skills, call skill_link_job to link them to the job and mark it as analyzed.",
|
|
410
410
|
{
|
|
411
411
|
job_name: z.string().describe(
|
|
412
412
|
"Short name for this job/role. Example: '电商运营', 'Frontend Dev', 'Data Analyst'"
|
|
@@ -415,137 +415,67 @@ export function createAgentToolsServer(deps: AgentToolsDeps): McpSdkServerConfig
|
|
|
415
415
|
"Description of the user's job, role, and daily tasks. Can be in any language. " +
|
|
416
416
|
"Example: '我是电商运营,每天要看竞品价格、写商品文案、回复客户评论'"
|
|
417
417
|
),
|
|
418
|
-
auto_create: z
|
|
419
|
-
.boolean()
|
|
420
|
-
.optional()
|
|
421
|
-
.describe("If true, automatically create all generated skills. If false (default), return the plan for review first."),
|
|
422
418
|
},
|
|
423
419
|
async (args) => {
|
|
424
|
-
// Step 1: Decompose job into skill specs
|
|
425
420
|
const existingNames = skillManager.getAll().map((s) => s.name);
|
|
426
|
-
const specs = await decomposeJob(args.job_description, existingNames);
|
|
427
|
-
|
|
428
|
-
if (specs.length === 0) {
|
|
429
|
-
return {
|
|
430
|
-
content: [{
|
|
431
|
-
type: "text",
|
|
432
|
-
text: "Could not decompose the job description into automatable skills. Try providing more detail about your daily tasks.",
|
|
433
|
-
}],
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// If not auto-creating, return the decomposition plan
|
|
438
|
-
if (!args.auto_create) {
|
|
439
|
-
let response = `## Job Analysis: ${args.job_name}\n\nI identified **${specs.length} skills** from your job description:\n\n`;
|
|
440
421
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
(byPriority[s.priority] || byPriority.medium).push(s);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
for (const [priority, items] of Object.entries(byPriority)) {
|
|
447
|
-
if (items.length === 0) continue;
|
|
448
|
-
const label = priority === "high" ? "High Priority" : priority === "medium" ? "Medium Priority" : "Low Priority";
|
|
449
|
-
response += `### ${label}\n`;
|
|
450
|
-
for (const s of items) {
|
|
451
|
-
const auto = s.automatable ? "" : " (needs human assistance)";
|
|
452
|
-
response += `- **${s.name}**: ${s.description}${auto}\n`;
|
|
453
|
-
}
|
|
454
|
-
response += "\n";
|
|
455
|
-
}
|
|
422
|
+
let response = `## Job: ${args.job_name}\n`;
|
|
423
|
+
response += `**Description:** ${args.job_description}\n\n`;
|
|
456
424
|
|
|
457
|
-
|
|
458
|
-
response +=
|
|
459
|
-
|
|
460
|
-
return { content: [{ type: "text", text: response }] };
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// Step 2: Auto-create — generate full skills for each spec
|
|
464
|
-
const created: string[] = [];
|
|
465
|
-
const failed: string[] = [];
|
|
466
|
-
|
|
467
|
-
// Generate skills in parallel (batch of 3 to avoid rate limits)
|
|
468
|
-
for (let i = 0; i < specs.length; i += 3) {
|
|
469
|
-
const batch = specs.slice(i, i + 3);
|
|
470
|
-
const results = await Promise.allSettled(
|
|
471
|
-
batch.map((spec) =>
|
|
472
|
-
generateSkillFromDescription(spec.name, spec.description, args.job_description)
|
|
473
|
-
)
|
|
474
|
-
);
|
|
475
|
-
|
|
476
|
-
for (let j = 0; j < results.length; j++) {
|
|
477
|
-
const spec = batch[j];
|
|
478
|
-
const result = results[j];
|
|
479
|
-
|
|
480
|
-
if (result.status === "fulfilled" && result.value) {
|
|
481
|
-
const generated = result.value;
|
|
482
|
-
// Check for duplicates
|
|
483
|
-
const existing = skillManager.findSimilar(generated.name);
|
|
484
|
-
if (existing) {
|
|
485
|
-
failed.push(`${spec.name} (duplicate of "${existing.name}")`);
|
|
486
|
-
continue;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
// Create in skills table (draft)
|
|
490
|
-
const createResult = await skillManager.create(
|
|
491
|
-
generated.name,
|
|
492
|
-
generated.description || spec.description,
|
|
493
|
-
generated.steps,
|
|
494
|
-
{
|
|
495
|
-
source: "job_generated",
|
|
496
|
-
emoji: generated.emoji,
|
|
497
|
-
keywords: generated.keywords,
|
|
498
|
-
}
|
|
499
|
-
);
|
|
500
|
-
|
|
501
|
-
if (createResult) {
|
|
502
|
-
// Add to user's collection (approval)
|
|
503
|
-
const added = await skillManager.addSkill(createResult.id);
|
|
504
|
-
if (added) {
|
|
505
|
-
created.push(generated.name);
|
|
506
|
-
} else {
|
|
507
|
-
failed.push(`${spec.name} (add failed)`);
|
|
508
|
-
}
|
|
509
|
-
} else {
|
|
510
|
-
failed.push(spec.name);
|
|
511
|
-
}
|
|
512
|
-
} else {
|
|
513
|
-
failed.push(spec.name);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
425
|
+
if (existingNames.length > 0) {
|
|
426
|
+
response += `**Existing skills (do NOT duplicate):** ${existingNames.join(", ")}\n\n`;
|
|
516
427
|
}
|
|
517
428
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
429
|
+
response += `**Your task:** Analyze this job description and decompose it into 4-10 automatable skills. `;
|
|
430
|
+
response += `For each skill, call \`skill_create\` with:\n`;
|
|
431
|
+
response += `- name: kebab-case name (e.g. "slack-message-check")\n`;
|
|
432
|
+
response += `- description: one-line description\n`;
|
|
433
|
+
response += `- instructions: detailed step-by-step markdown instructions the agent can follow\n`;
|
|
434
|
+
response += `- emoji: a single emoji representing the skill\n\n`;
|
|
435
|
+
response += `After creating each skill, call \`skill_add\` with the returned skill ID to add it to the user's collection.\n\n`;
|
|
436
|
+
response += `After ALL skills are created and added, call \`skill_link_job\` with job_name="${args.job_name}" and the list of created skill names to link them and mark the job as analyzed.\n\n`;
|
|
437
|
+
response += `**Guidelines for skill instructions:**\n`;
|
|
438
|
+
response += `- Write clear, actionable markdown steps\n`;
|
|
439
|
+
response += `- Reference browser tools (browser_navigate, browser_click, browser_read_page, etc.) for web tasks\n`;
|
|
440
|
+
response += `- Include error handling steps\n`;
|
|
441
|
+
response += `- Use placeholders like {query}, {date} for variable inputs\n`;
|
|
442
|
+
response += `- Each skill should be a single, well-defined workflow (10-25 steps)\n`;
|
|
529
443
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
444
|
+
return { content: [{ type: "text", text: response }] };
|
|
445
|
+
}
|
|
446
|
+
),
|
|
447
|
+
tool(
|
|
448
|
+
"skill_link_job",
|
|
449
|
+
"Link created skills to a job and mark it as analyzed. Call this after creating all skills for a job via skill_create + skill_add.",
|
|
450
|
+
{
|
|
451
|
+
job_name: z.string().describe("Name of the job to link skills to"),
|
|
452
|
+
job_description: z.string().describe("Job description (used if job doesn't exist yet)"),
|
|
453
|
+
skill_names: z.array(z.string()).describe("Names of skills to link to this job"),
|
|
454
|
+
},
|
|
455
|
+
async (args) => {
|
|
456
|
+
if (!userId) {
|
|
457
|
+
return {
|
|
458
|
+
content: [{ type: "text", text: "Not authenticated. Cannot link job." }],
|
|
459
|
+
};
|
|
537
460
|
}
|
|
538
461
|
|
|
539
|
-
|
|
540
|
-
|
|
462
|
+
try {
|
|
463
|
+
await saveJobToDb(userId, args.job_name, args.job_description, args.skill_names);
|
|
464
|
+
log.success(`Job "${args.job_name}": linked ${args.skill_names.length} skills and marked as analyzed`);
|
|
465
|
+
return {
|
|
466
|
+
content: [{
|
|
467
|
+
type: "text",
|
|
468
|
+
text: `Job "${args.job_name}" linked with ${args.skill_names.length} skills and marked as analyzed.`,
|
|
469
|
+
}],
|
|
470
|
+
};
|
|
471
|
+
} catch (err) {
|
|
472
|
+
return {
|
|
473
|
+
content: [{
|
|
474
|
+
type: "text",
|
|
475
|
+
text: `Failed to link job: ${err instanceof Error ? err.message : err}`,
|
|
476
|
+
}],
|
|
477
|
+
};
|
|
541
478
|
}
|
|
542
|
-
|
|
543
|
-
response += "\nThese skills are now available. When you give me a task that matches, I'll automatically use the right skill.";
|
|
544
|
-
response += "\nUse `skill_invoke` to test any skill, or `skill_improve` to refine them after use.";
|
|
545
|
-
|
|
546
|
-
log.success(`Job "${args.job_name}": created ${created.length} skills`);
|
|
547
|
-
|
|
548
|
-
return { content: [{ type: "text", text: response }] };
|
|
549
479
|
}
|
|
550
480
|
),
|
|
551
481
|
tool(
|
|
@@ -92,11 +92,8 @@ vi.mock("./skills.js", () => ({
|
|
|
92
92
|
},
|
|
93
93
|
}));
|
|
94
94
|
|
|
95
|
-
vi.mock("./memory-extractor.js", () => ({
|
|
96
|
-
extractMemoriesWithLLM: vi.fn().mockResolvedValue([]),
|
|
97
|
-
}));
|
|
98
95
|
vi.mock("./skill-extractor.js", () => ({
|
|
99
|
-
// Only ToolCallRecord type is used
|
|
96
|
+
// Only ToolCallRecord type is used
|
|
100
97
|
}));
|
|
101
98
|
|
|
102
99
|
vi.mock("../utils/rate-limiter.js", () => ({
|
package/src/agent/processor.ts
CHANGED
|
@@ -20,7 +20,6 @@ import { log, newCorrelationId, setCorrelationId } from "../utils/logger.js";
|
|
|
20
20
|
import { getBrowser } from "../tools/browser.js";
|
|
21
21
|
import { MemoryManager } from "./memory.js";
|
|
22
22
|
import { SkillManager } from "./skills.js";
|
|
23
|
-
import { extractMemoriesWithLLM } from "./memory-extractor.js";
|
|
24
23
|
import { type ToolCallRecord } from "./skill-extractor.js";
|
|
25
24
|
import { withRetry } from "../utils/retry.js";
|
|
26
25
|
import {
|
|
@@ -56,6 +55,8 @@ Available capabilities:
|
|
|
56
55
|
- You can remember things about the user using memory_store
|
|
57
56
|
- Use this when you learn preferences, important facts, or standing instructions
|
|
58
57
|
- Your stored memories persist across conversations
|
|
58
|
+
- PROACTIVELY use memory_store during tasks when you discover user preferences, habits, or important context
|
|
59
|
+
- Before completing a task, consider if anything learned should be remembered for future conversations
|
|
59
60
|
|
|
60
61
|
4. SKILL PLANNING (pre-task):
|
|
61
62
|
- Before executing a complex task, analyze if it matches an existing skill (use skill_invoke)
|
|
@@ -232,6 +233,7 @@ export class TaskProcessor {
|
|
|
232
233
|
"mcp__assistme-agent__skill_invoke",
|
|
233
234
|
"mcp__assistme-agent__skill_search",
|
|
234
235
|
"mcp__assistme-agent__skill_generate",
|
|
236
|
+
"mcp__assistme-agent__skill_link_job",
|
|
235
237
|
"mcp__assistme-agent__skill_browse",
|
|
236
238
|
"mcp__assistme-agent__skill_add",
|
|
237
239
|
"mcp__assistme-agent__skill_publish",
|
|
@@ -367,35 +369,9 @@ export class TaskProcessor {
|
|
|
367
369
|
}
|
|
368
370
|
this.historyCache.set(task.conversation_id, convHistory);
|
|
369
371
|
|
|
370
|
-
//
|
|
371
|
-
|
|
372
|
-
//
|
|
373
|
-
if (this.memoryManager && finalResponse) {
|
|
374
|
-
const mm = this.memoryManager;
|
|
375
|
-
const taskIdRef = task.id;
|
|
376
|
-
extractMemoriesWithLLM(task.prompt, finalResponse)
|
|
377
|
-
.then(async (memories) => {
|
|
378
|
-
for (const mem of memories) {
|
|
379
|
-
try {
|
|
380
|
-
await mm.remember(mem.content, mem.category, {
|
|
381
|
-
importance: mem.importance,
|
|
382
|
-
tags: mem.tags,
|
|
383
|
-
sourceMessageId: taskIdRef,
|
|
384
|
-
});
|
|
385
|
-
log.info(`Memory extracted: [${mem.category}] ${mem.content.slice(0, 60)}...`);
|
|
386
|
-
} catch {
|
|
387
|
-
// Non-critical — skip individual memory failures
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
if (memories.length > 0) {
|
|
391
|
-
log.success(`${memories.length} memory(s) auto-extracted`);
|
|
392
|
-
}
|
|
393
|
-
})
|
|
394
|
-
.catch(() => {});
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// Note: Skill creation/improvement is now handled pre-task via the
|
|
398
|
-
// skill_create → user approval → skill_add flow, not post-task extraction.
|
|
372
|
+
// Note: Memory extraction and skill creation are handled by the agent itself
|
|
373
|
+
// during task execution via memory_store and skill_create tools.
|
|
374
|
+
// No separate LLM API calls needed — the agent SDK handles everything.
|
|
399
375
|
} catch (err) {
|
|
400
376
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
401
377
|
log.error(`Task failed: ${errorMsg}`);
|