ningharness

package module
v0.0.0-...-38ea638 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 23, 2026 License: MIT Imports: 6 Imported by: 0

README

ningharness

English | 中文

Pure Go backend framework for hosting agents (SQLite + Gateway/MCP).
No Wails, Node, or git required—only Go 1.25+.

Owns world truth and the tool gateway—not how the model thinks.

Positioning

In scope Out of scope
Store durable state (desk.db, …) Client UI / product shells
Workspace I/O + Gateway MCP core tools Client-specific tools and UX
Skill contract + Lesson / Memory Client Skill catalogs and policies
Job / Task (part of Store)
Lifecycle (default steps + event bus)
Optional defaults: MCP server + Eino Guest You may replace or disable them

Tool truth lives in the host: Guests change the world only through Gateway—no bypass disk writes.
Each Chat / Job run follows lifecycle. Phase boundaries use the Bus (On / Watch) with RunState. MCP tools run only inside run_guest via Gateway.

Standard shape

[Client] caller (app / MCP host / examples embedding this library)
        │  Open · UseProject · RunTurn/Chat · register tools · Bus
        ▼
[ningharness core]
   Workspace · Store · Gateway · Lifecycle · RunState
   swappable slots: Guest · Memory · Skill
  • Client: the caller; UI is not implemented in this library.
  • Store: in-framework durable state (session / history / task / job / resource / lesson, …).
  • Feedforward: a RunState field written at assemble and sent into the model—not the whole RunState.

Glossary

Term Meaning
Client Caller that embeds or invokes this library
Harness Facade: Store + Session + Job
Store In-framework durable state (session / history / task / job / resource / lesson, …)
Workspace Project file world (paths relative to root)
Lifecycle Fixed-phase pipeline for one Task / Chat turn (steps + Bus)
Bus / Event Lifecycle events: Phase × before|after; On may Block, Watch observes only
RunState Per-turn pipeline context; shared by steps and Bus; Guest does not read it directly
Feedforward Extra model context on RunState; attached when assemble persists the user row
Step Domain atom (e.g. begin_task, run_guest); not a tool name
Gateway Tool gateway + MCP core (register / authorize / invoke); sole side-effect entry
Guest Model-loop slot: Run(Input{Message,Feedforward}); default Eino; guest.Chat sugar without feedforward
Memory Memory slot: feedforward at assemble; optional Ingester (FileIngest JSONL); default memory.Lesson; NewLessonWithFileIngest bundles both
Skill Method-pack slot: List / Match / Load; default skill.Disk; SetSkill / WithoutSkill
Lesson Experience row in Store; usable in feedforward on write; ack_lesson for legacy unacked / re-confirm
Task One execution record (Store)
Job Queue unit (schedules when to run a Lifecycle; may span tasks)
Goal Outer-loop Job: re-trigger Lifecycle until GOAL.yaml status is terminal
Trace Append-only JSONL under .ningharness/traces/ per Task; resume = paired tool_call/tool_result + task_end

Packages (by layer)

ningharness/           facade Open/Close/UseProject
  lifecycle/           one turn: steps + Bus + RunState + Runner
  toolgateway/         Gateway + MCP + tool Registry; turn* = RunState projection
  workspace/ protocol/ files / shared DTOs
  store/ session/ history/ resource/   Store
  task/ job/ goal/ trace/
  skill/ lesson/       Skill Slot + Lesson (Store)
  memory/              Memory slot (Assemble + optional Ingest)
  guest/ (+ eino/)     Guest slot (Run)
  defaults/            wiring (Lifecycle Host + Gateway projection + MCP + Eino + Memory + Skill)
  examples/            sample Client

Dependency rule: defaults → lifecycle / toolgateway / guest / memory / skill; lifecycle does not import toolgateway (Host injected).

Turn projection: begin_taskGateway.ProjectTurn; teardown only via OnExitFinishTurn (end_task is a Bus hook).
runLifecycle may wrap ctx with WithRunState for Host steps; Guest stays free of RunState — tool turn identity is the Gateway projection.
assemble_context: match skill.paths → merge caller feedforward + Memory.AssembleRunState.Feedforward → persist user.
run_guest: Guest.Run (guest.Wire merges feedforward into the model turn).
persist_turn: persist assistant; if Memory implements Ingester, call Ingest.
Tool dispatch: RegisterHandler + core ensureCoreHandlers; Client extensions need not edit CallNamedTool.

Quick start

One complete path: edit Eino config → start MCP + Guest → send one message → paste Cursor URL.

Edit examples/chat/main.go:

package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"github.com/wcoreing/ningharness"
	"github.com/wcoreing/ningharness/defaults"
	"github.com/wcoreing/ningharness/guest/eino"
)

var einoCfg = eino.Opts{
	APIKey:  "sk-...",                    // required for Guest
	BaseURL: "https://api.openai.com/v1", // gateway goes here
	Model:   "gpt-4o-mini",
}

func main() {
	root, msg := ".", "List the project files in one short paragraph."
	if len(os.Args) > 1 {
		root = os.Args[1]
	}
	if len(os.Args) > 2 {
		msg = os.Args[2]
	}
	abs, _ := filepath.Abs(root)

	rt, err := defaults.Open(defaults.Opts{
		Opts: ningharness.Opts{DataDir: filepath.Join(abs, ".ningharness-data"), Root: abs},
		Eino: einoCfg,
		// WithoutEino: true  → MCP only (no key)
		// MCPAddr: "off"     → Chat only (no HTTP)
		// MCPAddr: "127.0.0.1:51021" → fixed port
	})
	if err != nil {
		panic(err)
	}
	defer rt.Close()

	fmt.Println("MCP:", rt.MCPURL())
	// paste into ~/.cursor/mcp.json → {"mcpServers":{"ningharness":{"url":"<MCP URL>"}}}

	reply, err := rt.Chat(context.Background(), msg)
	if err != nil {
		panic(err)
	}
	fmt.Println(reply)

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
	<-ch
}

Run:

go run ./examples/chat /path/to/project "List files briefly."

Defaults wire Gateway core tools + MCP HTTP (/mcp) + Eino Guest (ReAct via Gateway.Invoke).
WithoutEino / MCPAddr: "off" / SetGuest turn pieces off or replace them. Empty Eino fields fall back to NINGHARNESS_API_KEY, NINGHARNESS_BASE_URL, NINGHARNESS_MODEL (or OPENAI_*).

Integrate

require github.com/wcoreing/ningharness v0.0.0
go get github.com/wcoreing/ningharness@main

Embed toolgateway.Gateway for product tools, or defaults.Open + SetGuest.

Develop

go test ./...

Requires Go 1.25+ only.

License

MIT


GitHub About

Pure Go agent host: Store, Gateway/MCP core tools, Skill/Memory; optional Eino Guest. Client owns UI.

Documentation

Overview

Package ningharness 是 Agent 宿主门面:Open / UseProject / Close(Store + Session + Job)。

层划分(扁平包,靠依赖方向表达架构):

  • lifecycle:一轮怎么跑(步骤 + Bus + RunState);前馈见 RunState.Feedforward
  • toolgateway / workspace:Gateway 与文件世界(turn* 为 RunState 投影)
  • store / session / history / resource / task / job / goal / trace:Store
  • skill / lesson / memory:Skill Slot + Memory 插槽(默认 Disk / Lesson)
  • guest:Guest 插槽(Run;前馈经 guest.Wire)
  • defaults:可选装配(Lifecycle Host + MCP + Eino + Memory + Skill);examples 为示例客户端

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Harness

type Harness struct {
	DB      *sql.DB
	Session *session.Store
	Job     *job.Manager
	// contains filtered or unexported fields
}

Harness 地基句柄。

func Open

func Open(opts Opts) (*Harness, error)

Open 打开台面库并装配 Session / Job(Job.Executor 须由调用方 SetExecutor)。

func (*Harness) Close

func (h *Harness) Close() error

Close 关闭台面库连接缓存(进程内全局 cache)。

func (*Harness) Root

func (h *Harness) Root() string

Root 当前项目根。

func (*Harness) UseProject

func (h *Harness) UseProject(root string) error

UseProject 登记项目、打开项目迁移,并绑定 Job 到该根。 若 Open 时带了 DataDir,则库落在该目录(OpenProjectAt),避免嵌入方误写入家目录默认库。

type Opts

type Opts struct {
	// DataDir sqlite 下 desk.db 目录;也可作嵌入方工作区根。mysql 忽略库路径。
	DataDir string
	// Root 当前项目根;可空,之后 UseProject。
	Root string
	// Driver sqlite(默认)| mysql。
	Driver string
	// DSN mysql 连接串;与 DB 二选一。
	DSN string
	// DB 宿主注入的连接池(如与产品同实例);不由此 Close。
	DB *sql.DB
	// TablePrefix 表前缀;mysql 默认 nh_,与业务表隔离。
	TablePrefix string
	// ProjectKey 逻辑项目键;嵌入同库时建议设稳定键,避免本机路径当 id。
	ProjectKey string
}

Opts Open 参数(通用宿主配置;不含业务域名)。

Directories

Path Synopsis
Package contextpatch 台面变更补丁:Go 确定性回执(summary + 小 delta + refs)。
Package contextpatch 台面变更补丁:Go 确定性回执(summary + 小 delta + refs)。
Package defaults 可选装配层:粘合 Harness + ToolGateway + MCP + Guest + Lifecycle Host。
Package defaults 可选装配层:粘合 Harness + ToolGateway + MCP + Guest + Lifecycle Host。
examples
chat command
Complete demo: MCP core tools + Eino Guest + one user message.
Complete demo: MCP core tools + Eino Guest + one user message.
Package guest 是 Guest 插槽:怎么想;工具须经 Gateway(本包不定义 Lifecycle)。
Package guest 是 Guest 插槽:怎么想;工具须经 Gateway(本包不定义 Lifecycle)。
eino
Package eino 可选默认 Guest:Eino ReAct + Gateway 工具。
Package eino 可选默认 Guest:Eino ReAct + Gateway 工具。
Package history 模型侧连贯上下文(history_message 表)。
Package history 模型侧连贯上下文(history_message 表)。
Package job Agent 队列:Job = 调度单元;执行台账见 ningharness/task。
Package job Agent 队列:Job = 调度单元;执行台账见 ningharness/task。
Package jsonparse 提供无业务语义的 JSON 解析(不修复、不校验 schema)。
Package jsonparse 提供无业务语义的 JSON 解析(不修复、不校验 schema)。
Package lifecycle 定义 Harness「一轮」怎么跑:固定领域步骤 + 事件打孔 + 管道上下文。
Package lifecycle 定义 Harness「一轮」怎么跑:固定领域步骤 + 事件打孔 + 管道上下文。
Package memory 是 Memory 插槽:在 assemble 时贡献前馈;可选 Ingest 回合后沉淀。
Package memory 是 Memory 插槽:在 assemble 时贡献前馈;可选 Ingest 回合后沉淀。
Package metadir 项目与家目录元数据路径(统一 .ningharness)。
Package metadir 项目与家目录元数据路径(统一 .ningharness)。
Package resource 外置正文索引:入库不截断,进模用 summary,按需召回。
Package resource 外置正文索引:入库不截断,进模用 summary,按需召回。
Package skill 项目级 Skill 磁盘契约与 Slot 插槽:system/skills/<id>/SKILL.md。
Package skill 项目级 Skill 磁盘契约与 Slot 插槽:system/skills/<id>/SKILL.md。
Package store 台面持久层:默认 SQLite(desk.db);可切换 MySQL(表前缀隔离,供嵌入方同实例共库)。
Package store 台面持久层:默认 SQLite(desk.db);可切换 MySQL(表前缀隔离,供嵌入方同实例共库)。
工具回执语义:失败 / 已落盘 / 已入队(未落盘)。
工具回执语义:失败 / 已落盘 / 已入队(未落盘)。
Package toolgateway 是工具网关(Gateway):注册 / 授权 / Invoke / MCP HTTP。
Package toolgateway 是工具网关(Gateway):注册 / 授权 / Invoke / MCP HTTP。
Package workspace includes path sorting helpers (names / rel paths).
Package workspace includes path sorting helpers (names / rel paths).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL