From 97840e25ab021bbbb7fe8b0c0ae2416282996201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E5=A2=A8?= Date: Wed, 29 Apr 2026 09:29:29 +0000 Subject: [PATCH 1/4] chore: add .knowledge/ cards + knowledge.yaml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 7 curated knowledge cards extracted from RFCs and docs: - architecture: core pipeline, extension points, process isolation - sense: compute behavior, Sense→Workflow, config - workflow: engine, threads, WorkflowSpec - adapter: AgentFn protocol, available adapters, extract layer - coding-conventions: functional-first, Result type, naming - monorepo: package structure, dependency rules - knowledge-layer: sync/query CLI, embedding service knowledge.yaml indexes .knowledge/**/*.md only. --- .knowledge/adapter.md | 45 +++++++++++++++++++++++++ .knowledge/architecture.md | 33 ++++++++++++++++++ .knowledge/coding-conventions.md | 57 ++++++++++++++++++++++++++++++++ .knowledge/knowledge-layer.md | 38 +++++++++++++++++++++ .knowledge/monorepo.md | 24 ++++++++++++++ .knowledge/sense.md | 29 ++++++++++++++++ .knowledge/workflow.md | 50 ++++++++++++++++++++++++++++ knowledge.yaml | 4 +++ 8 files changed, 280 insertions(+) create mode 100644 .knowledge/adapter.md create mode 100644 .knowledge/architecture.md create mode 100644 .knowledge/coding-conventions.md create mode 100644 .knowledge/knowledge-layer.md create mode 100644 .knowledge/monorepo.md create mode 100644 .knowledge/sense.md create mode 100644 .knowledge/workflow.md create mode 100644 knowledge.yaml diff --git a/.knowledge/adapter.md b/.knowledge/adapter.md new file mode 100644 index 0000000..975c37e --- /dev/null +++ b/.knowledge/adapter.md @@ -0,0 +1,45 @@ +# Agent Adapters (RFC-003) + +Adapter = capability. Role = scenario. Workflows declare adapters directly via import. + +## AgentFn Protocol + +```ts +type AgentFn = (prompt: string, context: WorkflowContext) => Promise +``` + +- Input: prompt + context (start frame, messages, workdir, AbortSignal) +- Output: raw string — structured extraction is separate +- Adapter handles tool-specific details internally + +## Available Adapters + +| Package | Adapter | Tool | +|---------|---------|------| +| `@uncaged/nerve-adapter-cursor` | `cursorAdapter` / `createCursorAdapter()` | cursor-agent CLI | +| `@uncaged/nerve-adapter-hermes` | `hermesAdapter` / `createHermesAdapter()` | hermes chat CLI | + +Each exports a **default instance** (sensible defaults) and a **factory** for custom config. + +## Usage in Workflows + +```ts +import { cursorAdapter } from "@uncaged/nerve-adapter-cursor"; + +// RoleSpec +{ adapter: cursorAdapter, prompt: "...", meta: schema } +``` + +No registry, no nerve.yaml agent config. TypeScript catches missing adapters at compile time. + +## Extract Layer + +Parses agent raw string → typed meta. Configured in `nerve.yaml`: + +```yaml +extract: + provider: dashscope + model: qwen-plus +``` + +Two-level merge: global → role override. Retry once on parse failure (feeds error back to LLM), then throw `ExtractError`. diff --git a/.knowledge/architecture.md b/.knowledge/architecture.md new file mode 100644 index 0000000..69ccefc --- /dev/null +++ b/.knowledge/architecture.md @@ -0,0 +1,33 @@ +# Nerve Architecture + +Observation engine for autonomous agents — sense the world, react to changes, run workflows. + +## Core Pipeline + +``` +External World → Sense → Signal → Reflex → Workflow → Log +``` + +Causality is **one-directional**. Logs are the end of the chain — they cannot trigger Reflexes (prevents feedback loops). + +## Three Orthogonal Extension Points + +| Extension | Question | Nature | +|-----------|----------|--------| +| **Sense** | What to compute | `compute()` function | +| **Reflex** | When to compute | Declarative YAML (interval / on) | +| **Workflow** | What to do | Roles + Moderator | + +Each is independent. Reflex doesn't know compute internals, Sense doesn't know when it's triggered, Workflow doesn't know why it was started. + +## Two Event Types + +- **Signal** — from Sense compute (non-null return). Pure fact, no intent. Drives the front half (perception). +- **Command Event** — inside Workflow Threads. Has causal chain, must be responded to. Drives the back half (action). + +## Process Isolation + +- One worker per Sense group (long-lived) +- One worker per Workflow type (on-demand) +- Workers never talk to each other +- All user code runs in isolated Workers; kernel never loads user code directly diff --git a/.knowledge/coding-conventions.md b/.knowledge/coding-conventions.md new file mode 100644 index 0000000..b14253b --- /dev/null +++ b/.knowledge/coding-conventions.md @@ -0,0 +1,57 @@ +# Nerve Coding Conventions + +## Functional-First + +- `type` over `interface`, `function` over `class` +- No `this`, no inheritance, composition over inheritance +- Immutability first: `Readonly`, `as const` + +## No Optional Properties + +Never use `?:`. Use `T | null` for nullable fields. Use discriminated unions for mutually exclusive fields. + +```ts +// ✅ Good +type Config = { throttle: string | null } + +// ❌ Bad +type Config = { throttle?: string } +``` + +## Error Handling + +- `Result` for expected failures +- `throw` only for programmer errors (bugs) +- No try-catch for flow control + +## Naming + +| Type | Style | +|------|-------| +| Files | `kebab-case.ts` | +| Types | `PascalCase` | +| Functions/vars | `camelCase` | +| Constants | `UPPER_SNAKE` | + +## Exports + +- Always named exports, never default +- One module = one responsibility + +## Async + +- Always `async/await`, never `.then()` chains + +## No Dynamic Import + +Static `import` only. Exceptions: `sense-runtime.ts`, `workflow-worker.ts` (runtime module paths). + +## Toolchain + +pnpm + TypeScript (strict) + Biome (lint/format) + Vitest (test) + +```bash +pnpm run check # biome check +pnpm test # vitest +pnpm run build # full build +``` diff --git a/.knowledge/knowledge-layer.md b/.knowledge/knowledge-layer.md new file mode 100644 index 0000000..3d580e6 --- /dev/null +++ b/.knowledge/knowledge-layer.md @@ -0,0 +1,38 @@ +# Knowledge Layer (RFC-003 Phase 6) + +Local-first, repo-scoped knowledge base for project context. + +## Files + +- `knowledge.yaml` — repo root, defines include/exclude globs +- `knowledge.db` — SQLite, stores chunks + embeddings +- `.knowledge/` — curated knowledge cards (indexed by sync) + +## Commands + +```bash +nerve knowledge sync # chunk files, compute embeddings, write to knowledge.db +nerve knowledge query "query" # search by cosine similarity (or word overlap fallback) +nerve knowledge query -g "query" # global search across all indexed repos +nerve knowledge query --repo /path "query" # search specific repo +``` + +## Embedding + +- Remote service: `embed.shazhou.workers.dev` (Cloudflare Worker + KV cache) +- Model: Dashscope text-embedding-v3 (1024 dims) +- Cache: content-addressable (sha256 of model+text), never expires +- Fallback: word-overlap scoring when embed service not configured + +## Chunking + +- Markdown: split by headings, large sections split further by paragraphs (max 24) +- TypeScript/JS: split by function declarations, fallback to paragraphs +- Other files: single chunk + +## Env Config + +``` +EMBED_SERVICE_URL=https://embed.shazhou.workers.dev +EMBED_AUTH_TOKEN= +``` diff --git a/.knowledge/monorepo.md b/.knowledge/monorepo.md new file mode 100644 index 0000000..98ae350 --- /dev/null +++ b/.knowledge/monorepo.md @@ -0,0 +1,24 @@ +# Nerve Monorepo Structure + +``` +nerve/ + packages/ + core/ # @uncaged/nerve-core — shared types, config parser, Result, spawn-safe + cli/ # @uncaged/nerve-cli — CLI (init, validate, dev, daemon, knowledge) + daemon/ # @uncaged/nerve-daemon — kernel, workers, signal bus, scheduler + store/ # @uncaged/nerve-store — append-only log, SQLite, CAS blob store + workflow-utils/ # @uncaged/nerve-workflow-utils — role factories, extract, LLM helpers + adapter-cursor/ # @uncaged/nerve-adapter-cursor — cursor-agent CLI adapter + adapter-hermes/ # @uncaged/nerve-adapter-hermes — hermes chat CLI adapter + khala/ # Khala — Sense marketplace (future) + skills/ # nerve-managed skills + docs/ # RFCs, conventions + .knowledge/ # curated knowledge cards (this directory) +``` + +## Dependency Rules + +- `core` is the shared layer — everyone depends on it +- `cli` and `daemon` must NOT depend on each other +- Adapter packages depend only on `core` +- `workflow-utils` depends on `core` and adapter packages diff --git a/.knowledge/sense.md b/.knowledge/sense.md new file mode 100644 index 0000000..52049e0 --- /dev/null +++ b/.knowledge/sense.md @@ -0,0 +1,29 @@ +# Sense + +A `compute()` function that samples or derives external data. The only first-class citizen in nerve. + +## Behavior + +- Returns `T | null` — non-null emits a Signal, null is silent (no storage write, no signal, no downstream trigger) +- Each Sense has its own **independent SQLite database** +- Cross-sense reads are read-only via `peers` parameter +- Schema defined with Drizzle ORM (`schema.ts` is single source of truth) + +## Sense → Workflow + +If `compute()` returns an object with `workflow: "name|maxRounds|prompt"`, the engine starts that workflow and does **not** emit a Signal. `workflow: null` or `""` means emit signal normally. + +See `routeSenseComputeOutput` / `parseSenseWorkflowDirective` in `@uncaged/nerve-core`. + +## Config (nerve.yaml) + +```yaml +senses: + cpu-usage: + group: system # senses in same group share a worker + throttle: 10s # min interval between computes + timeout: 30s # max compute duration + grace_period: 5s # wait before first compute + interval: 30s # periodic trigger (optional) + on: [disk-pressure] # trigger on signals from other senses (optional) +``` diff --git a/.knowledge/workflow.md b/.knowledge/workflow.md new file mode 100644 index 0000000..f44def1 --- /dev/null +++ b/.knowledge/workflow.md @@ -0,0 +1,50 @@ +# Workflow Engine + +Stateful multi-step execution driven by Roles and a Moderator. + +## Core Concepts + +- **Workflow** — definition with concurrency strategy +- **Thread** — one execution instance, unique `runId` +- **Role** — executes actions (has side effects). `(start, messages) → { content, meta }` +- **Moderator** — pure routing function. `(context) → next role | END` + +## Thread Lifecycle + +``` +trigger → queued → started → step_complete ↺ → completed + ↓ + failed / crashed +``` + +## Concurrency Config (nerve.yaml) + +```yaml +workflows: + cleanup: + concurrency: 1 + overflow: drop # discard if already running + code-review: + concurrency: 3 + overflow: queue + max_queue: 20 # queue limit, oldest discarded +``` + +## WorkflowSpec (RFC-003) + +User-facing authoring format that compiles to runtime `WorkflowDefinition`: + +```ts +const workflow: WorkflowSpec = { + name: "develop", + roles: { + coder: { adapter: cursorAdapter, prompt: "...", meta: schema }, + reviewer: { adapter: hermesAdapter, prompt: reviewFn, meta: schema }, + }, + moderator, +}; +``` + +- `adapter: AgentFn` — direct function reference, no registry +- `prompt: string | ((start, messages) => Promise)` — static or dynamic +- `meta: Schema` — extract schema for typed output diff --git a/knowledge.yaml b/knowledge.yaml new file mode 100644 index 0000000..ab2ac2c --- /dev/null +++ b/knowledge.yaml @@ -0,0 +1,4 @@ +include: + - ".knowledge/**/*.md" + +exclude: [] -- 2.43.0 From accc7c59fd7a5eae8699bd2b836110708952272f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E5=A2=A8?= Date: Wed, 29 Apr 2026 09:32:33 +0000 Subject: [PATCH 2/4] chore: add cli.md knowledge card --- .knowledge/cli.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 .knowledge/cli.md diff --git a/.knowledge/cli.md b/.knowledge/cli.md new file mode 100644 index 0000000..1907f5e --- /dev/null +++ b/.knowledge/cli.md @@ -0,0 +1,77 @@ +# Nerve CLI + +`nerve` — CLI entry point for nerve workspace management. + +## Workspace Lifecycle + +```bash +nerve init # scaffold a new workspace (nerve.yaml, senses/, workflows/) +nerve validate # validate nerve.yaml config +nerve dev # run kernel foreground (development, Ctrl+C to stop) +nerve start # start daemon (background) +nerve stop # stop daemon +nerve status # check daemon health (uptime, senses, workflows) +nerve daemon # restart daemon (stop + start) +``` + +## Sense Management + +```bash +nerve create sense # scaffold a new sense (compute.ts + schema.ts) +nerve sense list # list configured senses +nerve sense trigger # manually trigger a sense compute +nerve sense db # inspect sense SQLite database +nerve sense db --sql "SELECT * FROM samples LIMIT 5" +``` + +## Workflow Management + +```bash +nerve create workflow # scaffold a new workflow +nerve workflow trigger --prompt "..." [--max-rounds N] [--dry-run] +nerve workflow list # list configured workflows +nerve thread # list active (queued/started) workflow threads +``` + +## Knowledge + +```bash +nerve knowledge sync # chunk files per knowledge.yaml, compute embeddings → knowledge.db +nerve knowledge query "text" # search indexed knowledge (cosine similarity) +nerve knowledge query -g "text" # global search across all indexed repos +nerve knowledge query --repo /path "text" # search specific repo +``` + +## Logs & Store + +```bash +nerve logs # view daemon logs (last 50 lines) +nerve logs -f # follow logs (tail -f style) +nerve logs -n 200 # last N lines +nerve store archive # archive old log entries to JSONL +``` + +## Remote + +```bash +nerve remote add # add a remote daemon endpoint +nerve status --remote # check remote daemon health +``` + +## Workspace Layout + +``` +my-agent/ + nerve.yaml # senses, workflows, extract config + knowledge.yaml # knowledge index config (optional) + senses/ + cpu-usage/ + compute.ts # sense implementation + schema.ts # Drizzle schema + migrations/ # auto-generated + workflows/ + cleanup/ + src/index.ts # workflow definition + knowledge.db # generated by nerve knowledge sync + .knowledge/ # curated knowledge cards +``` -- 2.43.0 From dfb3c9ec1823f8104c866a32628518bd46f9aef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E5=A2=A8?= Date: Wed, 29 Apr 2026 09:36:28 +0000 Subject: [PATCH 3/4] fix: address review feedback on knowledge cards - knowledge-layer.md: use env var instead of hardcoded URL - monorepo.md: workflow-utils depends on core only (not adapters) - cli.md: fix sense subcommands (schema/query, not db) --- .knowledge/cli.md | 5 +++-- .knowledge/knowledge-layer.md | 2 +- .knowledge/monorepo.md | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.knowledge/cli.md b/.knowledge/cli.md index 1907f5e..a76cbc0 100644 --- a/.knowledge/cli.md +++ b/.knowledge/cli.md @@ -20,8 +20,9 @@ nerve daemon # restart daemon (stop + start) nerve create sense # scaffold a new sense (compute.ts + schema.ts) nerve sense list # list configured senses nerve sense trigger # manually trigger a sense compute -nerve sense db # inspect sense SQLite database -nerve sense db --sql "SELECT * FROM samples LIMIT 5" +nerve sense schema # show sense Drizzle schema +nerve sense query # inspect sense SQLite database +nerve sense query --sql "SELECT * FROM samples LIMIT 5" ``` ## Workflow Management diff --git a/.knowledge/knowledge-layer.md b/.knowledge/knowledge-layer.md index 3d580e6..85dc229 100644 --- a/.knowledge/knowledge-layer.md +++ b/.knowledge/knowledge-layer.md @@ -19,7 +19,7 @@ nerve knowledge query --repo /path "query" # search specific repo ## Embedding -- Remote service: `embed.shazhou.workers.dev` (Cloudflare Worker + KV cache) +- Remote service: configured via `EMBED_SERVICE_URL` env var (self-hosted Cloudflare Worker + KV cache) - Model: Dashscope text-embedding-v3 (1024 dims) - Cache: content-addressable (sha256 of model+text), never expires - Fallback: word-overlap scoring when embed service not configured diff --git a/.knowledge/monorepo.md b/.knowledge/monorepo.md index 98ae350..33e26d2 100644 --- a/.knowledge/monorepo.md +++ b/.knowledge/monorepo.md @@ -21,4 +21,4 @@ nerve/ - `core` is the shared layer — everyone depends on it - `cli` and `daemon` must NOT depend on each other - Adapter packages depend only on `core` -- `workflow-utils` depends on `core` and adapter packages +- `workflow-utils` depends on `core` -- 2.43.0 From 45f5dbe89e9402e120582ce4ce7bee57143d2e71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E5=A2=A8?= Date: Wed, 29 Apr 2026 09:59:04 +0000 Subject: [PATCH 4/4] fix: update workflow.md and adapter.md for createRole (PR #253) - workflow.md: replace WorkflowSpec section with createRole helper - adapter.md: update usage example to createRole --- .knowledge/adapter.md | 8 +++++--- .knowledge/workflow.md | 27 ++++++++++++++++++--------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/.knowledge/adapter.md b/.knowledge/adapter.md index 975c37e..7fe1c2f 100644 --- a/.knowledge/adapter.md +++ b/.knowledge/adapter.md @@ -23,14 +23,16 @@ Each exports a **default instance** (sensible defaults) and a **factory** for cu ## Usage in Workflows +Adapters are passed directly to `createRole`: + ```ts +import { createRole } from "@uncaged/nerve-workflow-utils"; import { cursorAdapter } from "@uncaged/nerve-adapter-cursor"; -// RoleSpec -{ adapter: cursorAdapter, prompt: "...", meta: schema } +const coder = createRole(cursorAdapter, prompt, schema, extractConfig); ``` -No registry, no nerve.yaml agent config. TypeScript catches missing adapters at compile time. +No registry, no config indirection. TypeScript catches missing adapters at compile time. ## Extract Layer diff --git a/.knowledge/workflow.md b/.knowledge/workflow.md index f44def1..0d58aa6 100644 --- a/.knowledge/workflow.md +++ b/.knowledge/workflow.md @@ -30,21 +30,30 @@ workflows: max_queue: 20 # queue limit, oldest discarded ``` -## WorkflowSpec (RFC-003) +## createRole Helper -User-facing authoring format that compiles to runtime `WorkflowDefinition`: +`createRole` builds a `Role` from an adapter, prompt, Zod schema, and extract config: ```ts -const workflow: WorkflowSpec = { +import { createRole } from "@uncaged/nerve-workflow-utils"; +import { cursorAdapter } from "@uncaged/nerve-adapter-cursor"; +import { z } from "zod"; + +const coderSchema = z.object({ plan: z.string(), files: z.array(z.string()) }); + +const coder = createRole(cursorAdapter, coderPrompt, coderSchema, { + provider: { baseUrl: "...", apiKey: "...", model: "qwen-plus" }, +}); + +// Use in WorkflowDefinition +const workflow: WorkflowDefinition = { name: "develop", - roles: { - coder: { adapter: cursorAdapter, prompt: "...", meta: schema }, - reviewer: { adapter: hermesAdapter, prompt: reviewFn, meta: schema }, - }, + roles: { coder, reviewer }, moderator, }; ``` -- `adapter: AgentFn` — direct function reference, no registry +- `adapter: AgentFn` — direct function reference - `prompt: string | ((start, messages) => Promise)` — static or dynamic -- `meta: Schema` — extract schema for typed output +- `meta: z.ZodType` — Zod schema, directly (no wrapper needed) +- `extract: LlmExtractorConfig` — provider for structured extraction -- 2.43.0