@cjhyy/code-shell-core
UI-agnostic agent orchestration framework. The headless core of code-shell: turn an LLM + a tool registry + a transcript into a multi-turn agent loop, with permissions, hooks, MCP support, and pluggable approval backends.
Compatible with Anthropic and OpenAI-protocol providers (Claude, DeepSeek, GPT, Gemini, Qwen, …). No terminal UI dependencies — embed it in a CLI, a web service, a Slack bot, a desktop app. This is a Node.js runtime package, not a browser SDK.
Install
npm install @cjhyy/code-shell-core
bun add @cjhyy/code-shell-core
Requires Node ≥ 20.10 and an ESM-capable runtime.
Quickstart
import { Engine, HeadlessApprovalBackend } from "@cjhyy/code-shell-core";
const engine = new Engine({
llm: {
provider: "anthropic",
model: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY!,
},
cwd: process.cwd(),
approvalBackend: new HeadlessApprovalBackend("approve-all"),
headless: true,
});
const result = await engine.run("list files in this directory and summarise their purpose");
console.log(result.text);
console.log("turns:", result.turnCount, "reason:", result.reason);
Streaming events:
await engine.run("...", {
onStream(event) {
if (event.type === "text_delta") process.stdout.write(event.text);
if (event.type === "tool_use_start") console.log("→", event.toolCall);
},
});
Restrict the tool set:
new Engine({
llm: {
},
enabledBuiltinTools: ["Read", "Glob", "Grep", "WebFetch"],
});
What's included
- Engine — turn loop, transcript management, session persistence, resume.
- Tool system — registry, executor, permission classifier, schema validation. Built-in tools for filesystem, shell, web fetch, sub-agents, plan mode, tasks.
- LLM clients — Anthropic, OpenAI (and any OpenAI-protocol endpoint). Provider catalog covers DeepSeek, Qwen, OpenRouter, Gemini.
- MCP — connect to Model Context Protocol servers (stdio / SSE / streamable-http / in-process).
- Hooks —
pre_tool_use, post_tool_use, on_user_prompt_submit, on_session_start, on_session_end, more.
- Protocol —
AgentServer / AgentClient over JSON-RPC for running the engine out-of-process.
- Run manager — queue, schedule, and persist multi-engine workflows.
Designing for production
- Approval backend.
HeadlessApprovalBackend("approve-all") is fine only for local/trusted prompts. For untrusted input, explicitly set permissionMode, restrict tools with enabledBuiltinTools / disabledBuiltinTools, or implement ApprovalBackend to gate destructive tools (Write/Edit/Bash) through your own auth.
- Permission rules. Pass
permissionRules to the engine for fine-grained allow/deny per tool + args pattern. Bash commands additionally get a built-in safety classifier (dangerous commands like rm -rf always require explicit approval).
- Hooks are the right place for audit logging, prompt rewriting, and policy enforcement.
- Memory. The engine ships with an extract-memories + auto-dream pipeline that persists to
~/.code-shell/memory/. Disable by passing a no-op MemoryOrchestrator, or scope to a project by setting CODE_SHELL_HOME.
Recommended public API (B3 / standard §S7)
The stable, recommended construction path for embedders is the
createServer / createClient factory pair over a protocol transport.
Direct new Engine(...) still works for advanced cases — but the
protocol-mediated path is what we test, document, and avoid breaking.
import { createServer, createClient, createInProcessTransport } from "@cjhyy/code-shell-core";
const [serverT, clientT] = createInProcessTransport();
const handle = createServer({
transport: serverT,
cwd: process.cwd(),
llm: { provider: "anthropic", model: "claude-sonnet-4-6", apiKey: KEY },
permissionMode: "default",
});
const client = createClient({ transport: clientT });
client.onStreamEvent(({ sessionId, event }) => {
if (event.type === "text_delta") process.stdout.write(event.text);
});
const result = await client.run({ sessionId: "main", task: "Summarize README.md" });
console.log(result.text);
handle.close();
client.close();
For an out-of-process worker, swap createInProcessTransport() for a
StdioTransport pair. The factory accepts any Transport, so a future
IpcAdapter will compose without changing the call shape.
Browser UI hosts that only need the trusted Panel App lifecycle coordinator should
use the audited browser-safe subpath:
import { PanelAppLifecycleRuntime } from "@cjhyy/code-shell-core/browser/panel-app-runtime";
This entry contains no Engine, filesystem, process or Electron dependency.
The non-browser alias is @cjhyy/code-shell-core/panel-app-runtime.
StreamEvent includes background_agent_completed (B2.2) so embedders
that don't poll notificationQueue still observe sub-agent finish/fail
results. client.onBackgroundAgentCompleted((sessionId, event) => …) is
the typed entry point; events also flow through the catch-all
onStreamEvent.
Stable surface
These exports are covered by the stability promise:
| Factories | createServer, createClient, createInProcessTransport, StdioTransport |
| Server/client | AgentServer, AgentClient (direct use is supported but createServer/Client preferred) |
| Engine | Engine, EngineConfig, EngineRuntime, ChatSessionManager |
| Protocol types | Methods, ErrorCodes, RpcMessage, RunResult, StreamEvent, StreamCallback, BackgroundAgentCompletedEvent |
| LLM | LLMConfig, LLMResponse, ModelPool, createLLMClient, registerProvider |
| Tool authoring | ToolDefinition, ToolCall, ToolResult, RegisteredTool, BUILTIN_TOOLS, HookContext, HookResult |
| Permissions | PermissionClassifier, HeadlessApprovalBackend, AutoApprovalBackend, ApprovalBackend, PermissionMode, PermissionRule |
| Errors | FrameworkError + subclasses (LLMError, ToolError, PermissionDeniedError, …) |
Treat as internal
Anything not in the table above — including root-entry exports for
product/runtime helpers and direct imports from ./engine/...,
./tool-system/..., ./session/..., ./hooks/... subpaths — is
experimental/internal unless explicitly documented otherwise, and may
change between minor versions without notice. Plan to migrate via the
protocol surface instead of deep-importing.
In 0.8 the plugin installer / marketplace / onboarding / self-updater exports
(installPluginFromPath, installPlugin, addMarketplace, resolveApiKey,
checkForUpdate, …) moved off this root entry to
@cjhyy/code-shell-core/internal — they are host-assembly surfaces for the
in-repo TUI/desktop apps, not part of the embeddable agent SDK.
Stability
Pre-1.0 — APIs may change between minor versions. The "stable surface"
table above is the contract; everything else is mobile.
Breaking in 0.8: host-assembly exports (installer/marketplace/onboarding/
updater) left the root entry; import them from
@cjhyy/code-shell-core/internal if you must (no stability promise there).
See CHANGELOG.md in the monorepo.
License
MIT © maki maki. See LICENSE.