rfc-003: adapter as direct function reference, not string
- RoleSpec.adapter: string → AgentFn (direct import) - Each adapter exports default instance + factory - No adapter map, no registry, no lookup — compile-time safety - TypeScript catches missing adapters at import time Refs #245 小橘 🍊(NEKO Team)
This commit is contained in:
@@ -74,13 +74,16 @@ extract:
|
||||
Roles declare their adapter directly — no indirection through named agents:
|
||||
|
||||
```ts
|
||||
import { cursorAdapter, createCursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
||||
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
||||
|
||||
const workflow: WorkflowSpec<MyMeta> = {
|
||||
name: "develop-workflow",
|
||||
roles: {
|
||||
architect: { adapter: "cursor", prompt: architectPrompt, meta: architectSchema },
|
||||
coder: { adapter: "cursor", prompt: coderPrompt, meta: coderSchema },
|
||||
reviewer: { adapter: "hermes", prompt: reviewPrompt, meta: reviewSchema, timeout: "60s" },
|
||||
deployer: { adapter: "hermes", prompt: deployPrompt, meta: deploySchema },
|
||||
architect: { adapter: cursorAdapter, prompt: architectPrompt, meta: architectSchema },
|
||||
coder: { adapter: createCursorAdapter({ model: "claude-sonnet-4", timeout: 600 }), prompt: coderPrompt, meta: coderSchema },
|
||||
reviewer: { adapter: hermesAdapter, prompt: reviewPrompt, meta: reviewSchema, timeout: "60s" },
|
||||
deployer: { adapter: hermesAdapter, prompt: deployPrompt, meta: deploySchema },
|
||||
},
|
||||
moderator,
|
||||
};
|
||||
@@ -89,21 +92,12 @@ const workflow: WorkflowSpec<MyMeta> = {
|
||||
### Runtime Assembly
|
||||
|
||||
```
|
||||
WorkflowSpec → Role(adapter + prompt) → AdapterFn(prompt, ctx) → string
|
||||
WorkflowSpec → Role(adapter fn + prompt) → adapter(prompt, ctx) → string
|
||||
↓
|
||||
nerve.yaml#extract → ExtractFn(string, schema) → T (typed meta)
|
||||
```
|
||||
|
||||
`buildWorkflowSpec` receives an adapter map and wires each role to its adapter function directly. No registry, no lookup.
|
||||
|
||||
```ts
|
||||
const spec = buildWorkflowSpec(workflow, {
|
||||
adapters: {
|
||||
cursor: createCursorAdapter({ model: "auto", timeout: 300 }),
|
||||
hermes: createHermesAdapter({ model: "auto", timeout: 600 }),
|
||||
},
|
||||
});
|
||||
```
|
||||
Adapter is a direct function reference on each role — no map, no lookup, no registry.
|
||||
|
||||
### Adapter Packages
|
||||
|
||||
@@ -117,30 +111,32 @@ packages/
|
||||
adapter-codex/ # @uncaged/nerve-adapter-codex — codex CLI (future)
|
||||
```
|
||||
|
||||
Each adapter exports a single factory function:
|
||||
Each adapter exports a **default instance** and a **factory** for customization:
|
||||
|
||||
```ts
|
||||
// @uncaged/nerve-adapter-cursor
|
||||
import type { AgentConfig, AgentFn } from "@uncaged/nerve-core";
|
||||
|
||||
// Factory — custom config
|
||||
export function createCursorAdapter(config: AgentConfig): AgentFn;
|
||||
|
||||
// Default — sensible defaults (model: "auto", timeout: 300)
|
||||
export const cursorAdapter: AgentFn;
|
||||
```
|
||||
|
||||
The factory receives adapter config (model, timeout) and returns an `AgentFn` that spawns the CLI tool, passes the prompt, and returns raw output.
|
||||
|
||||
**Wiring** — the daemon's entry point passes installed adapters to `buildWorkflowSpec`:
|
||||
**Wiring** — workflows import adapters directly, no daemon-level registry:
|
||||
|
||||
```ts
|
||||
import { createCursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
||||
import { createHermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
||||
import { cursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
||||
import { hermesAdapter } from "@uncaged/nerve-adapter-hermes";
|
||||
|
||||
const adapters = {
|
||||
cursor: createCursorAdapter({ model: "auto", timeout: 300 }),
|
||||
hermes: createHermesAdapter({ model: "auto", timeout: 600 }),
|
||||
};
|
||||
// Use default instances directly in roles
|
||||
{ adapter: cursorAdapter, prompt: "...", meta: schema }
|
||||
```
|
||||
|
||||
Adapters not installed are simply not passed. `nerve validate` checks that all adapters referenced in workflow roles are available.
|
||||
Adapters not installed simply can't be imported — TypeScript catches missing dependencies at compile time.
|
||||
|
||||
**Workspace `package.json`** only lists the adapters it actually uses:
|
||||
|
||||
@@ -163,7 +159,7 @@ Adapters not installed are simply not passed. `nerve validate` checks that all a
|
||||
type PromptInput = string | ((start: StartStep, messages: WorkflowMessage[]) => Promise<string>);
|
||||
|
||||
type RoleSpec<M> = {
|
||||
adapter: string;
|
||||
adapter: AgentFn;
|
||||
prompt: PromptInput;
|
||||
meta: Schema<M>;
|
||||
timeout: string | null;
|
||||
@@ -176,19 +172,20 @@ Static prompts cover simple cases. Dynamic prompts (functions) are needed when t
|
||||
|
||||
Two-layer: adapter default + role override.
|
||||
|
||||
1. Adapter config provides the default timeout (set at `buildWorkflowSpec` time)
|
||||
1. Adapter instance carries its default timeout (set at creation or from default export)
|
||||
2. Role definition can override for specific scenarios
|
||||
|
||||
```ts
|
||||
// Adapter default: 300s
|
||||
const adapters = {
|
||||
cursor: createCursorAdapter({ model: "auto", timeout: 300 }),
|
||||
};
|
||||
import { cursorAdapter, createCursorAdapter } from "@uncaged/nerve-adapter-cursor";
|
||||
|
||||
// cursorAdapter has built-in default (300s)
|
||||
// Or create with custom timeout:
|
||||
const longRunAdapter = createCursorAdapter({ model: "auto", timeout: 600 });
|
||||
|
||||
// Role override — review is faster
|
||||
reviewer: { adapter: "cursor", ..., timeout: "60s" }
|
||||
reviewer: { adapter: cursorAdapter, ..., timeout: "60s" }
|
||||
// coder uses adapter default (300s)
|
||||
coder: { adapter: "cursor", ... }
|
||||
coder: { adapter: cursorAdapter, ... }
|
||||
```
|
||||
|
||||
### No Runtime Fallback
|
||||
@@ -218,7 +215,7 @@ type WorkflowContext = {
|
||||
### Configuration Validation
|
||||
|
||||
`nerve validate` checks:
|
||||
- All adapter names referenced in WorkflowSpec roles have a registered adapter
|
||||
- All roles have a valid adapter function (not null/undefined)
|
||||
- Adapter CLIs are available (binary exists in PATH)
|
||||
- Extract provider is configured and reachable
|
||||
|
||||
|
||||
Reference in New Issue
Block a user