Import the client, server, skills, tests, and build scripts as the starting point for the rebuild, then trim the surfaces this project does not ship: Electron desktop packaging, the marketing website, and the ESP32 firmware tree. CI keeps the web build, e2e, lockfile, release, and Docker workflows. Rename the package and CLI entry points to hermes-ui, drop the prepare hook so npm ci no longer triggers a build, and drop the removed tsconfig and nodemon references. Rewrite AGENTS.md, ARCHITECTURE.md, DEVELOPMENT.md, and the READMEs so the documented package map and hard rules match this repository. reference_code/ is ignored: it is a read-only snapshot, not an input to the build.
3.3 KiB
Ekko Agent
Ekko Agent is a package scaffold for future Hermes Web UI agent integration.
The first implemented layer is model-provider requests. Internally, the agent uses one request shape and provider adapters translate it to external APIs.
Supported request styles:
- OpenAI Chat Completions style
- OpenAI-compatible providers such as DeepSeek, Qwen, Moonshot, Ollama
- OpenAI Responses
- Anthropic Messages
- Gemini Contents
- prompt completion
- custom runtime
Default endpoints:
| Style | Default endpoint |
|---|---|
openai-chat |
https://api.openai.com/v1/chat/completions |
openai-responses |
https://api.openai.com/v1/responses |
anthropic-messages |
https://api.anthropic.com/v1/messages |
gemini-contents |
https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent |
prompt-completion |
https://api.openai.com/v1/completions |
custom-runtime |
http://127.0.0.1:11434/v1/agent |
Use baseUrl and endpointPath to override these defaults.
Message Shape
All adapters receive the same internal message shape:
type AgentMessage = {
role: 'system' | 'user' | 'assistant' | 'tool'
content: string
name?: string
toolCallId?: string
toolCalls?: AgentToolCall[]
}
Use normalizeAgentMessage() or normalizeAgentMessages() at the boundary.
Model responses can be converted back to a single assistant message with
modelResponseToAgentMessage(). Streaming events can be collected into the same
shape with collectModelEvents().
Tools
Built-in tools:
read_filereads a text file.write_filewrites text content and creates parent directories by default.terminal_execruns a command with an argument array andshell: false.
Use workspaceRoot to keep file and terminal working directories inside a
specific workspace.
import { createDefaultToolRegistry } from './src/index'
const tools = createDefaultToolRegistry()
await tools.execute('write_file', {
path: 'notes/todo.txt',
content: 'ship tools',
}, {
workspaceRoot: process.cwd(),
})
const result = await tools.execute('terminal_exec', {
command: 'node',
args: ['-v'],
}, {
workspaceRoot: process.cwd(),
})
Runtime
AgentRuntime ties messages, model requests, tools, skills, system prompt, and
events together. The default maxSteps is 90, matching Hermes' regular agent
turn budget.
import { AgentRuntime, createDefaultToolRegistry } from './src/index'
const runtime = new AgentRuntime({
modelClient: client,
tools: createDefaultToolRegistry(),
skills: [{
id: 'project',
name: 'Project Skill',
instructions: 'Follow the project conventions before editing files.',
}],
})
const result = await runtime.run({
messages: ['Read README.md and summarize it.'],
toolContext: {
workspaceRoot: process.cwd(),
},
onEvent(event) {
console.log(event.type)
},
})
Commands
npm --prefix packages/ekko-agent run check
Example
import { createModelClient } from './src/index'
const client = createModelClient({
id: 'deepseek',
type: 'openai-compatible',
apiKey: process.env.DEEPSEEK_API_KEY,
baseUrl: 'https://api.deepseek.com/v1',
defaultModel: 'deepseek-chat',
})
const response = await client.create({
messages: [{ role: 'user', content: 'Say hello.' }],
})