XingSeq is an Agent Harness framework — it handles the runtime plumbing so you can focus on what your agent actually does.
You define tools. XingSeq handles the ReAct loop, streams tokens from any LLM provider, dispatches tool calls through a registry, enforces safety confirmation before destructive operations, and persists conversations per workspace. All in plain Node.js, no Python, no heavy abstractions.
TL;DR — Clone,
npm install,make chat-dry. You'll see a full tool-calling loop running locally with zero API keys.
Most agent frameworks give you one of two things: a thin wrapper around chat completions, or a sprawling platform with its own DSL, database, and deployment story.
XingSeq sits in between:
- You own the process. It's a Node.js library, not a platform. No servers to deploy, no SDK lock-in.
- Tools are first-class. Register tool groups with their executors; the framework handles dispatch, argument validation, and four-layer safety guards.
- Workspace isolation. Each workspace gets its own tool scope, conversation history, and storage — no global state leaking between sessions.
- Multi-provider out of the box. DeepSeek, Kimi (Moonshot), Qwen, Doubao — unified streaming, error handling, and session control.
- Runs without API keys. Every app has a
dry-runmode with a mock LLM, so you can validate your tool wiring without burning tokens.
git clone git@github.com:xingseq/xingseq.git
cd xingseq
npm install
# Verify the full tool-calling loop — no API key needed
make chat-dryTo talk to a real LLM:
cp .env.example .env
# Set at least one: DEEPSEEK_API_KEY / KIMI_API_KEY / QWEN_API_KEY / DOUBAO_API_KEY
make chat| Command | What it does |
|---|---|
make chat-dry |
Dry-run: mock LLM + real tool dispatch |
make chat |
Interactive CLI with real LLM |
make web-dev |
HTTP+SSE backend (:3001) + Vite React frontend (:5173) |
make ws |
Workspace-app CLI (file/shell tools scoped to a directory) |
make mail-dry |
Mail gateway in offline mode |
make test |
Smoke test |
Run make to see the full list.
graph TB
subgraph L4[Applications]
chat-app
workspace-app
mail-app
end
subgraph L3[Core Engine]
chat-core
end
subgraph L2[Domain Services]
tool-registry
memory-store
end
subgraph L1[Foundations]
llm-core
config-core
storage-core
shared-utils
end
chat-app --> chat-core
workspace-app --> chat-core
mail-app --> chat-core
chat-core --> tool-registry
chat-core --> llm-core
chat-core --> config-core
tool-registry --> shared-utils
llm-core --> config-core
llm-core --> shared-utils
config-core --> storage-core
storage-core --> shared-utils
Dependency rule: upper layers import lower layers only. Same-layer packages never import each other.
| Layer | Packages | Role |
|---|---|---|
| L1 Foundations | shared-utils · config-core · storage-core · llm-core |
Logger, config, encrypted storage, multi-provider LLM client |
| L2 Domain | tool-registry · memory-store · skill-host · subapp-host |
Tool dispatch, conversation persistence, plugin management |
| L3 Engine | chat-core · agent · flow-engine · agent-runtime |
ReAct loop, workspace session, safety guards |
| L4 Apps | chat-app · workspace-app · mail-app · llm-manager · ... |
End-user applications built on the stack |
A single chat turn:
User message
→ chatLoop.js (depth 0)
→ LLM returns tool_calls
→ registry.dispatch(toolCall)
→ safety confirmation (if destructive)
→ executor runs, returns result
→ tool result appended to messages
→ chatLoop.js (depth 1)
→ LLM returns final text
→ response streamed to user
The loop continues until the LLM stops requesting tools or hits maxDepth (default 5).
- Path sandboxing — all file operations confined to
workspace.filesDir - Command allowlist — only pre-approved shell commands pass
- Argument filtering — dangerous flags (
--force,rm -rf /) are rejected - User confirmation — destructive ops require explicit approval (CLI prompt / Web dialog)
XingSeq was extracted from a 2000+ file Electron desktop assistant. The original monolith worked, but changes in one area broke three others — no clear boundaries, shared mutable state everywhere.
The rewrite strategy was simple:
- Start from the bottom (L1) — pure utilities with zero coupling
- Migrate code only when a real app needs it — no speculative extraction
- Validate each layer in isolation with dry-run mode before stacking the next
The result: 12 packages with enforced single-direction dependencies, running the same features with half the complexity.
This is an active project. APIs may change between minor versions until 1.0.
See CONTRIBUTING.md for setup instructions, commit conventions, and code standards.
