Skip to content

Commit 8dec003

Browse files
feat: add SDK Skills + Memory System (#89)
* feat: catch up with Python * feat: add SDK Skills + Memory System Port SDK Skills and Agent Memory features from Python PR 646. * prettier * fix unit test for Windows * add example * format
1 parent c53d7d7 commit 8dec003

File tree

23 files changed

+3647
-19
lines changed

23 files changed

+3647
-19
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,4 @@ agent-os
5555
.claude
5656

5757
# Sandbox workspace directory - created at runtime
58-
sandbox-workspace/
58+
sandbox-workspace/
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/* eslint-disable no-console */
2+
/**
3+
* Skills + Memory Agent Example
4+
*
5+
* This example demonstrates how to use the Skills and Agent Memory middleware
6+
* to create an agent with:
7+
* - Discoverable skills from SKILL.md files
8+
* - Persistent long-term memory from agent.md files
9+
*
10+
* To run this example:
11+
* npx tsx examples/skills-memory/skills-memory-agent.ts
12+
*
13+
* Prerequisites:
14+
* - Set OPENAI_API_KEY or ANTHROPIC_API_KEY environment variable
15+
* - Optionally create skills in ~/.deepagents/my-agent/skills/
16+
* - Optionally create agent.md in ~/.deepagents/my-agent/
17+
*/
18+
19+
import { ChatOpenAI } from "@langchain/openai";
20+
import { ChatAnthropic } from "@langchain/anthropic";
21+
import { HumanMessage } from "@langchain/core/messages";
22+
import path from "node:path";
23+
24+
import {
25+
createDeepAgent,
26+
createSettings,
27+
createSkillsMiddleware,
28+
createAgentMemoryMiddleware,
29+
listSkills,
30+
} from "../../src/index.js";
31+
32+
// Configuration
33+
const AGENT_NAME = "my-agent";
34+
35+
async function main() {
36+
console.log("🚀 Skills + Memory Agent Example\n");
37+
38+
// Create settings with project detection
39+
const settings = createSettings();
40+
41+
console.log("📁 Environment:");
42+
console.log(` User deepagents dir: ${settings.userDeepagentsDir}`);
43+
console.log(
44+
` Project root: ${settings.projectRoot || "(not in a project)"}`,
45+
);
46+
console.log(` Has project: ${settings.hasProject}\n`);
47+
48+
// Get skills directories
49+
const userSkillsDir = settings.getUserSkillsDir(AGENT_NAME);
50+
const projectSkillsDir = settings.getProjectSkillsDir();
51+
52+
console.log("🛠️ Skills directories:");
53+
console.log(` User skills: ${userSkillsDir}`);
54+
console.log(
55+
` Project skills: ${projectSkillsDir || "(not in a project)"}\n`,
56+
);
57+
58+
// List available skills
59+
const skills = listSkills({
60+
userSkillsDir,
61+
projectSkillsDir,
62+
});
63+
64+
if (skills.length > 0) {
65+
console.log("📚 Available skills:");
66+
for (const skill of skills) {
67+
console.log(
68+
` - ${skill.name} (${skill.source}): ${skill.description.slice(0, 60)}...`,
69+
);
70+
}
71+
console.log();
72+
} else {
73+
console.log("📚 No skills found. You can create skills in:");
74+
console.log(` - ${userSkillsDir}/{skill-name}/SKILL.md`);
75+
if (projectSkillsDir) {
76+
console.log(` - ${projectSkillsDir}/{skill-name}/SKILL.md`);
77+
}
78+
console.log();
79+
}
80+
81+
// Get memory paths
82+
const userMemoryPath = settings.getUserAgentMdPath(AGENT_NAME);
83+
const projectMemoryPath = settings.getProjectAgentMdPath();
84+
85+
console.log("🧠 Memory locations:");
86+
console.log(` User memory: ${userMemoryPath}`);
87+
console.log(
88+
` Project memory: ${projectMemoryPath || "(not in a project)"}\n`,
89+
);
90+
91+
// Create the model
92+
const model = process.env.ANTHROPIC_API_KEY
93+
? new ChatAnthropic({ model: "claude-sonnet-4-20250514" })
94+
: new ChatOpenAI({ model: "gpt-4o-mini" });
95+
96+
console.log(
97+
`🤖 Using model: ${process.env.ANTHROPIC_API_KEY ? "Claude" : "GPT-4o-mini"}\n`,
98+
);
99+
100+
// Create middleware stack
101+
// Note: createDeepAgent already includes FilesystemMiddleware by default
102+
const skillsMiddleware = createSkillsMiddleware({
103+
skillsDir: userSkillsDir,
104+
assistantId: AGENT_NAME,
105+
projectSkillsDir: projectSkillsDir || undefined,
106+
});
107+
108+
const memoryMiddleware = createAgentMemoryMiddleware({
109+
settings,
110+
assistantId: AGENT_NAME,
111+
});
112+
113+
// Create the agent with skills + memory middleware
114+
// (FilesystemMiddleware is already included by createDeepAgent)
115+
const agent = await createDeepAgent({
116+
model,
117+
middleware: [skillsMiddleware, memoryMiddleware],
118+
});
119+
120+
console.log("💬 Agent ready! Asking about available skills...\n");
121+
console.log("─".repeat(60));
122+
123+
// Test the agent
124+
const result = await agent.invoke({
125+
messages: [
126+
new HumanMessage(
127+
"What skills do you have access to? List them with their descriptions. " +
128+
"Also, do you have any long-term memory configured?",
129+
),
130+
],
131+
});
132+
133+
// Get the last AI message
134+
const messages = result.messages;
135+
const lastMessage = messages[messages.length - 1];
136+
137+
console.log("\n🤖 Agent response:\n");
138+
console.log(lastMessage.content);
139+
console.log("\n" + "─".repeat(60));
140+
141+
// Show how to create a skill
142+
console.log("\n💡 Tips:");
143+
console.log("\n To add a skill, run these commands:");
144+
console.log(` mkdir -p "${userSkillsDir}/my-skill"`);
145+
console.log(
146+
` printf '%s\\n' '---' 'name: my-skill' 'description: A custom skill' '---' '' '# My Skill' '' 'Instructions...' > "${userSkillsDir}/my-skill/SKILL.md"`,
147+
);
148+
149+
console.log("\n To add agent memory:");
150+
console.log(` mkdir -p "${path.dirname(userMemoryPath)}"`);
151+
console.log(
152+
` printf '%s\\n' '# Agent Memory' '' 'Remember to be helpful!' > "${userMemoryPath}"`,
153+
);
154+
}
155+
156+
main().catch(console.error);
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
name: arxiv-search
3+
description: Search arXiv preprint repository for papers in physics, mathematics, computer science, quantitative biology, and related fields
4+
---
5+
6+
# arXiv Search Skill
7+
8+
This skill provides access to arXiv, a free distribution service and open-access archive for scholarly articles in physics, mathematics, computer science, quantitative biology, quantitative finance, statistics, electrical engineering, systems science, and economics.
9+
10+
## When to Use This Skill
11+
12+
Use this skill when you need to:
13+
14+
- Find preprints and recent research papers before journal publication
15+
- Search for papers in computational biology, bioinformatics, or systems biology
16+
- Access mathematical or statistical methods papers relevant to biology
17+
- Find machine learning papers applied to biological problems
18+
- Get the latest research that may not yet be in PubMed
19+
20+
## How to Use
21+
22+
The skill provides a TypeScript script that searches arXiv and returns formatted results.
23+
24+
### Basic Usage
25+
26+
**Note:** Always use the absolute path from your skills directory (shown in the system prompt above).
27+
28+
```bash
29+
npx tsx [YOUR_SKILLS_DIR]/arxiv-search/arxiv_search.ts "your search query" [--max-papers N]
30+
```
31+
32+
Replace `[YOUR_SKILLS_DIR]` with the absolute skills directory path from your system prompt (e.g., `~/.deepagents/agent/skills` or the full absolute path).
33+
34+
**Arguments:**
35+
36+
- `query` (required): The search query string (e.g., "neural networks protein structure", "single cell RNA-seq")
37+
- `--max-papers` (optional): Maximum number of papers to retrieve (default: 10)
38+
39+
### Examples
40+
41+
Search for machine learning papers:
42+
43+
```bash
44+
npx tsx ~/.deepagents/agent/skills/arxiv-search/arxiv_search.ts "deep learning drug discovery" --max-papers 5
45+
```
46+
47+
Search for computational biology papers:
48+
49+
```bash
50+
npx tsx ~/.deepagents/agent/skills/arxiv-search/arxiv_search.ts "protein folding prediction"
51+
```
52+
53+
Search for bioinformatics methods:
54+
55+
```bash
56+
npx tsx ~/.deepagents/agent/skills/arxiv-search/arxiv_search.ts "genome assembly algorithms"
57+
```
58+
59+
## Output Format
60+
61+
The script returns formatted results with:
62+
63+
- **Title**: Paper title
64+
- **Summary**: Abstract/summary text
65+
66+
Each paper is separated by blank lines for readability.
67+
68+
## Features
69+
70+
- **Relevance sorting**: Results ordered by relevance to query
71+
- **Fast retrieval**: Direct API access with no authentication required
72+
- **Simple interface**: Clean, easy-to-parse output
73+
- **No API key required**: Free access to arXiv database
74+
75+
## Notes
76+
77+
- arXiv is particularly strong for:
78+
- Computer science (cs.LG, cs.AI, cs.CV)
79+
- Quantitative biology (q-bio)
80+
- Statistics (stat.ML)
81+
- Physics and mathematics
82+
- Papers are preprints and may not be peer-reviewed
83+
- Results include both recent uploads and older papers
84+
- Best for computational/theoretical work in biology
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/usr/bin/env npx tsx
2+
/* eslint-disable no-console */
3+
/**
4+
* arXiv Search.
5+
*
6+
* Searches the arXiv preprint repository for research papers.
7+
* Uses the arXiv API directly without requiring any dependencies.
8+
*/
9+
10+
interface ArxivEntry {
11+
title: string;
12+
summary: string;
13+
}
14+
15+
/**
16+
* Query arXiv for papers based on the provided search query.
17+
*
18+
* @param query - The search query string
19+
* @param maxPapers - Maximum number of papers to retrieve (default: 10)
20+
* @returns Formatted search results or an error message
21+
*/
22+
async function queryArxiv(query: string, maxPapers = 10): Promise<string> {
23+
try {
24+
// Build arXiv API URL
25+
const encodedQuery = encodeURIComponent(query);
26+
const url = `http://export.arxiv.org/api/query?search_query=all:${encodedQuery}&start=0&max_results=${maxPapers}&sortBy=relevance&sortOrder=descending`;
27+
28+
const response = await fetch(url);
29+
if (!response.ok) {
30+
return `Error: Failed to fetch from arXiv API (status ${response.status})`;
31+
}
32+
33+
const xml = await response.text();
34+
35+
// Parse XML response (simple regex-based parsing for portability)
36+
const entries: ArxivEntry[] = [];
37+
const entryRegex = /<entry>([\s\S]*?)<\/entry>/g;
38+
const titleRegex = /<title>([\s\S]*?)<\/title>/;
39+
const summaryRegex = /<summary>([\s\S]*?)<\/summary>/;
40+
41+
let match;
42+
while ((match = entryRegex.exec(xml)) !== null) {
43+
const entryXml = match[1];
44+
45+
const titleMatch = titleRegex.exec(entryXml);
46+
const summaryMatch = summaryRegex.exec(entryXml);
47+
48+
if (titleMatch && summaryMatch) {
49+
entries.push({
50+
title: titleMatch[1].trim().replace(/\s+/g, " "),
51+
summary: summaryMatch[1].trim().replace(/\s+/g, " "),
52+
});
53+
}
54+
}
55+
56+
if (entries.length === 0) {
57+
return "No papers found on arXiv.";
58+
}
59+
60+
return entries
61+
.map((paper) => `Title: ${paper.title}\nSummary: ${paper.summary}`)
62+
.join("\n\n");
63+
} catch (error) {
64+
return `Error querying arXiv: ${error}`;
65+
}
66+
}
67+
68+
function main(): void {
69+
const args = process.argv.slice(2);
70+
71+
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
72+
console.log(`
73+
Usage: npx tsx arxiv_search.ts <query> [--max-papers N]
74+
75+
Arguments:
76+
query Search query string (required)
77+
--max-papers N Maximum number of papers to retrieve (default: 10)
78+
79+
Examples:
80+
npx tsx arxiv_search.ts "deep learning drug discovery"
81+
npx tsx arxiv_search.ts "protein folding" --max-papers 5
82+
`);
83+
process.exit(0);
84+
}
85+
86+
const query = args[0];
87+
let maxPapers = 10;
88+
89+
const maxPapersIndex = args.indexOf("--max-papers");
90+
if (maxPapersIndex !== -1 && args[maxPapersIndex + 1]) {
91+
maxPapers = parseInt(args[maxPapersIndex + 1], 10);
92+
if (isNaN(maxPapers) || maxPapers < 1) {
93+
console.error("Error: --max-papers must be a positive integer");
94+
process.exit(1);
95+
}
96+
}
97+
98+
queryArxiv(query, maxPapers).then((result) => {
99+
console.log(result);
100+
});
101+
}
102+
103+
main();
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
name: langgraph-docs
3+
description: Use this skill for requests related to LangGraph in order to fetch relevant documentation to provide accurate, up-to-date guidance.
4+
---
5+
6+
# langgraph-docs
7+
8+
## Overview
9+
10+
This skill explains how to access LangGraph Python documentation to help answer questions and guide implementation.
11+
12+
## Instructions
13+
14+
### 1. Fetch the Documentation Index
15+
16+
Use the fetch_url tool to read the following URL:
17+
https://docs.langchain.com/llms.txt
18+
19+
This provides a structured list of all available documentation with descriptions.
20+
21+
### 2. Select Relevant Documentation
22+
23+
Based on the question, identify 2-4 most relevant documentation URLs from the index. Prioritize:
24+
25+
- Specific how-to guides for implementation questions
26+
- Core concept pages for understanding questions
27+
- Tutorials for end-to-end examples
28+
- Reference docs for API details
29+
30+
### 3. Fetch Selected Documentation
31+
32+
Use the fetch_url tool to read the selected documentation URLs.
33+
34+
### 4. Provide Accurate Guidance
35+
36+
After reading the documentation, complete the users request.

0 commit comments

Comments
 (0)