Files
hermes-ui/packages/ekko-agent/README.md
T
toor 1e9ea89d86
Build / build (push) Canceled after 0s
NPM Lockfile Check / npm ci --ignore-scripts (push) Canceled after 0s
Playwright / e2e (push) Canceled after 0s
chore: bootstrap hermes-ui skeleton from 0.6.29 snapshot
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.
2026-10-16 10:34:35 +08:00

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_file reads a text file.
  • write_file writes text content and creates parent directories by default.
  • terminal_exec runs a command with an argument array and shell: 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.' }],
})