From ffc31a8c19bec11da93f7f7a1bb18da61eae2720 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=A9=98?= Date: Sat, 23 May 2026 15:00:05 +0000 Subject: [PATCH] docs: sync all README.md files with current codebase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Root README: add all 9 packages to table, update architecture diagram, refresh CLI reference from uwf --help - New READMEs for 8 packages (cli-workflow, workflow-protocol, workflow-moderator, workflow-agent-kit, workflow-agent-hermes, workflow-agent-builtin, workflow-agent-claude-code, workflow-dashboard) - Updated workflow-util README to match current exports - All API sections verified against src/index.ts exports 小橘 🍊(NEKO TeamοΌ‰ --- README.md | 122 ++++++----- packages/cli-workflow/README.md | 128 ++++++++++++ packages/workflow-agent-builtin/README.md | 141 +++++++++++++ packages/workflow-agent-claude-code/README.md | 91 +++++++++ packages/workflow-agent-hermes/README.md | 90 ++++++++ packages/workflow-agent-kit/README.md | 182 +++++++++++++++++ packages/workflow-dashboard/README.md | 84 ++++++++ packages/workflow-moderator/README.md | 60 ++++++ packages/workflow-protocol/README.md | 193 ++++++++++++++++++ packages/workflow-util/README.md | 145 +++++++++++-- 10 files changed, 1164 insertions(+), 72 deletions(-) create mode 100644 packages/cli-workflow/README.md create mode 100644 packages/workflow-agent-builtin/README.md create mode 100644 packages/workflow-agent-claude-code/README.md create mode 100644 packages/workflow-agent-hermes/README.md create mode 100644 packages/workflow-agent-kit/README.md create mode 100644 packages/workflow-dashboard/README.md create mode 100644 packages/workflow-moderator/README.md create mode 100644 packages/workflow-protocol/README.md diff --git a/README.md b/README.md index 5431160..b00a82a 100644 --- a/README.md +++ b/README.md @@ -2,92 +2,102 @@ A stateless workflow engine driven by a single-step CLI. Workflows are YAML definitions with roles, JSONata routing conditions, and a directed graph. Threads are immutable CAS-linked chains β€” each `uwf thread step` runs one moderatorβ†’agentβ†’extract cycle and exits. -## Package Map +## Overview -| Package | npm | Role | -|---------|-----|------| -| `cli-workflow` | `@uncaged/cli-workflow` | `uwf` CLI binary β€” thread lifecycle, workflow registry, CAS inspection, setup | -| `workflow-protocol` | `@uncaged/workflow-protocol` | Shared TypeScript types (`WorkflowPayload`, `StepNodePayload`, `WorkflowConfig`, etc.) | -| `workflow-moderator` | `@uncaged/workflow-moderator` | JSONata graph evaluator β€” determines next role or `$END` | -| `workflow-agent-kit` | `@uncaged/workflow-agent-kit` | `createAgent` factory, context builder, two-layer extract pipeline | -| `workflow-agent-hermes` | `@uncaged/workflow-agent-hermes` | `uwf-hermes` agent β€” spawns Hermes chat, captures session | -| `workflow-util` | `@uncaged/workflow-util` | Crockford Base32, ULID, logger, frontmatter parsing | +This monorepo implements **uwf**, a workflow engine with no long-running daemon. You register YAML workflow definitions in a content-addressed store (CAS), start a thread with an initial prompt, then invoke `uwf thread step` repeatedly until the moderator routes to `$END`. Each step is a complete process: the moderator evaluates JSONata conditions to pick the next role, an external agent CLI produces frontmatter markdown output, and an extract pipeline validates or structures that output against the role's JSON Schema. -External: [`@uncaged/json-cas`](https://www.npmjs.com/package/@uncaged/json-cas) (CAS store + JSON Schema validation) + `@uncaged/json-cas-fs` (filesystem backend). +Workflow state lives entirely on disk under `~/.uncaged/workflow/`: CAS nodes for definitions and step payloads, `registry.yaml` for workflow nameβ†’hash mappings, and `threads.yaml` for active thread head pointers. Completed threads are archived to `history.jsonl`. Because there is no server process, workflows are easy to debug, fork, and inspect with ordinary CLI tools. + +Agents are pluggable CLI binaries (`uwf-hermes`, `uwf-builtin`, `uwf-claude-code`, or custom commands). The engine spawns the configured agent with `` and ``, sets `UWF_EDGE_PROMPT` from the graph transition, and captures both the agent's markdown output and a detail CAS node for session replay. + +## Architecture + +Dependency layers (lower layers have no dependency on higher layers): + +``` +Layer 0 β€” Contract + workflow-protocol Shared types and JSON Schema definitions + +Layer 1 β€” Shared infra + workflow-util Encoding, IDs, logging, frontmatter, paths + workflow-moderator JSONata graph evaluator + +Layer 2 β€” Agent framework + workflow-agent-kit createAgent factory, context builder, extract pipeline + +Layer 3 β€” Agent implementations + workflow-agent-hermes Hermes ACP agent (uwf-hermes) + workflow-agent-builtin Built-in LLM + tools agent (uwf-builtin) + workflow-agent-claude-code Claude Code agent (uwf-claude-code) + +Layer 4 β€” CLI + cli-workflow uwf binary β€” thread lifecycle, registry, CAS, setup + +App (uses protocol; not in the runtime engine stack) + workflow-dashboard Web UI for visual workflow editing +``` + +External CAS: [`@uncaged/json-cas`](https://www.npmjs.com/package/@uncaged/json-cas) (store API, hashing, schema validation) + `@uncaged/json-cas-fs` (filesystem backend). + +See [docs/architecture.md](docs/architecture.md) for the full design β€” three-phase engine loop, CAS node types, storage layout, agent CLI protocol, and design decisions. + +## Packages + +| Package | npm | Description | Type | README | +|---------|-----|-------------|------|--------| +| `cli-workflow` | `@uncaged/cli-workflow` | `uwf` CLI β€” thread lifecycle, workflow registry, CAS inspection, setup | cli | [README](packages/cli-workflow/README.md) | +| `workflow-protocol` | `@uncaged/workflow-protocol` | Shared TypeScript types and JSON Schema constants | lib | [README](packages/workflow-protocol/README.md) | +| `workflow-moderator` | `@uncaged/workflow-moderator` | JSONata graph evaluator β€” next role or `$END` | lib | [README](packages/workflow-moderator/README.md) | +| `workflow-agent-kit` | `@uncaged/workflow-agent-kit` | `createAgent` factory, context builder, extract pipeline | lib | [README](packages/workflow-agent-kit/README.md) | +| `workflow-util` | `@uncaged/workflow-util` | Crockford Base32, ULID, logger, frontmatter parsing, storage paths | lib | [README](packages/workflow-util/README.md) | +| `workflow-agent-hermes` | `@uncaged/workflow-agent-hermes` | `uwf-hermes` β€” spawns Hermes chat via ACP | agent | [README](packages/workflow-agent-hermes/README.md) | +| `workflow-agent-builtin` | `@uncaged/workflow-agent-builtin` | `uwf-builtin` β€” built-in LLM agent with file/shell tools | agent | [README](packages/workflow-agent-builtin/README.md) | +| `workflow-agent-claude-code` | `@uncaged/workflow-agent-claude-code` | `uwf-claude-code` β€” spawns Claude Code CLI | agent | [README](packages/workflow-agent-claude-code/README.md) | +| `workflow-dashboard` | `@uncaged/workflow-dashboard` | Web graph editor for workflow YAML (private, alpha) | app | [README](packages/workflow-dashboard/README.md) | ## Quick Start ```bash -# 1. Configure provider and model +# 1. Configure provider, model, and default agent uwf setup # 2. Register a workflow from YAML uwf workflow put examples/solve-issue.yaml -# 3. Start a thread +# 3. Start a thread (creates head pointer; does not execute) uwf thread start solve-issue -p "Fix the login redirect bug" # 4. Execute steps (one at a time, until done) uwf thread step ``` -## CLI Commands +Use `-c, --count ` on `thread step` to run multiple steps in one invocation. Override the agent with `--agent `. -### Thread +## CLI Reference -| Command | Description | -|---------|-------------| -| `uwf thread start -p ` | Create a thread (no execution) | -| `uwf thread step [--agent ]` | Execute one moderatorβ†’agentβ†’extract cycle | -| `uwf thread show ` | Show head pointer and done status | -| `uwf thread list [--all]` | List threads (`--all` includes archived) | -| `uwf thread steps ` | List all steps chronologically | -| `uwf thread read [--quota N]` | Render thread as readable markdown | -| `uwf thread fork ` | Fork from a specific step | -| `uwf thread step-details ` | Dump full detail node | -| `uwf thread kill ` | Terminate and archive | +Global options: `-V, --version`, `--format `, `-h, --help`. -### Workflow +| Group | Commands | +|-------|----------| +| **thread** | `start`, `step`, `show`, `list`, `kill`, `steps`, `read`, `fork`, `step-details` | +| **workflow** | `put`, `show`, `list` | +| **cas** | `get`, `put`, `put-text`, `has`, `refs`, `walk`, `reindex`, `schema list`, `schema get` | +| **setup** | Interactive or `--provider`, `--base-url`, `--api-key`, `--model`, `--agent` | +| **skill** | `cli` β€” print markdown reference of all uwf commands | +| **log** | `list`, `show`, `clean` β€” process-level debug logs | -| Command | Description | -|---------|-------------| -| `uwf workflow put ` | Register a workflow from YAML | -| `uwf workflow show ` | Show workflow definition | -| `uwf workflow list` | List registered workflows | +Config is stored in `~/.uncaged/workflow/config.yaml`. API keys go in `~/.uncaged/workflow/.env`. -### CAS - -| Command | Description | -|---------|-------------| -| `uwf cas get ` | Read a CAS node | -| `uwf cas put ` | Store a node | -| `uwf cas has ` | Check existence | -| `uwf cas refs ` | List direct references | -| `uwf cas walk ` | Recursive traversal | -| `uwf cas reindex` | Rebuild type index | -| `uwf cas schema list` | List schemas | -| `uwf cas schema get ` | Show a schema | - -### Setup - -| Command | Description | -|---------|-------------| -| `uwf setup` | Interactive provider/model/agent configuration | -| `uwf setup --provider ... --base-url ... --api-key ... --model ...` | Non-interactive setup | - -Config stored in `~/.uncaged/workflow/config.yaml`. API keys in `~/.uncaged/workflow/.env`. +Detailed command usage, options, and examples: [packages/cli-workflow/README.md](packages/cli-workflow/README.md). ## Development ```bash bun install --no-cache # Install dependencies +bun run build # tsc --build (all packages) bun run check # tsc + biome + lint-log-tags bun run format # Auto-format with Biome bun test # Run all tests ``` Managed with **bun workspace**. See [CLAUDE.md](CLAUDE.md) for coding conventions. - -## Architecture - -See [docs/architecture.md](docs/architecture.md) for the full design β€” three-phase engine loop, CAS node types, storage layout, agent CLI protocol, and design decisions. diff --git a/packages/cli-workflow/README.md b/packages/cli-workflow/README.md new file mode 100644 index 0000000..12affdc --- /dev/null +++ b/packages/cli-workflow/README.md @@ -0,0 +1,128 @@ +# @uncaged/cli-workflow + +`uwf` CLI β€” thread lifecycle, workflow registry, CAS inspection, and setup. + +## Overview + +Layer 4 entry point for the workflow engine. The `uwf` binary orchestrates one step per invocation: load thread head from `threads.yaml`, run the moderator, spawn the configured agent CLI, run extract, append a CAS step node, and update the head pointer (or archive when `$END`). + +This package has no library `src/index.ts` β€” it is consumed as a CLI binary only. + +**Dependencies:** `@uncaged/json-cas`, `@uncaged/json-cas-fs`, `@uncaged/workflow-agent-kit`, `@uncaged/workflow-moderator`, `@uncaged/workflow-protocol`, `@uncaged/workflow-util`, `commander`, `dotenv`, `yaml` + +## Installation + +Included as the `uwf` binary when you install `@uncaged/cli-workflow`: + +```bash +bun add -g @uncaged/cli-workflow +# or from the monorepo: +bun link packages/cli-workflow +``` + +## CLI Usage + +### Global options + +``` +-V, --version Show version +--format Output format (default: json) +-h, --help Show help +``` + +### Thread + +| Command | Description | +|---------|-------------| +| `uwf thread start -p ` | Create a thread without executing | +| `uwf thread step [--agent ] [-c ]` | Execute one or more moderatorβ†’agentβ†’extract cycles | +| `uwf thread show ` | Show thread head pointer | +| `uwf thread list [--all]` | List active threads (`--all` includes archived) | +| `uwf thread steps ` | List all steps chronologically | +| `uwf thread read [--quota N] [--before ] [--start]` | Render thread as readable markdown | +| `uwf thread fork ` | Fork from a specific step | +| `uwf thread step-details ` | Dump full detail node as YAML | +| `uwf thread kill ` | Terminate and archive | + +Examples: + +```bash +uwf thread start solve-issue -p "Fix the login redirect bug" +uwf thread step 01ARZ3NDEKTSV4RRFFQ69G5FAV +uwf thread step 01ARZ3NDEKTSV4RRFFQ69G5FAV -c 3 --agent uwf-builtin +uwf thread read 01ARZ3NDEKTSV4RRFFQ69G5FAV --quota 8000 +``` + +### Workflow + +| Command | Description | +|---------|-------------| +| `uwf workflow put ` | Register a workflow from YAML | +| `uwf workflow show ` | Show workflow definition | +| `uwf workflow list` | List registered workflows | + +### CAS + +| Command | Description | +|---------|-------------| +| `uwf cas get [--timestamp]` | Read a CAS node | +| `uwf cas put ` | Store a node, print hash | +| `uwf cas put-text ` | Store plain text, print hash | +| `uwf cas has ` | Check existence | +| `uwf cas refs ` | List direct references | +| `uwf cas walk ` | Recursive traversal | +| `uwf cas reindex` | Rebuild type index | +| `uwf cas schema list` | List registered schemas | +| `uwf cas schema get ` | Show a schema | + +### Setup + +```bash +uwf setup +uwf setup --provider openai --base-url https://api.openai.com/v1 \ + --api-key sk-... --model gpt-4o --agent hermes +``` + +Config: `~/.uncaged/workflow/config.yaml`. API keys: `~/.uncaged/workflow/.env`. + +### Skill + +| Command | Description | +|---------|-------------| +| `uwf skill cli` | Print markdown reference of all uwf commands (for agent skills) | + +### Log + +| Command | Description | +|---------|-------------| +| `uwf log list` | List log files with sizes | +| `uwf log show [--thread ] [--process ] [--date YYYY-MM-DD]` | Show filtered log entries | +| `uwf log clean [--before YYYY-MM-DD]` | Delete old log files | + +## Internal Structure + +``` +src/ +β”œβ”€β”€ cli.ts Commander entrypoint, command registration +β”œβ”€β”€ format.ts JSON/YAML output formatting +β”œβ”€β”€ store.ts CAS store + registry initialization +β”œβ”€β”€ validate.ts Workflow YAML validation +β”œβ”€β”€ schemas.ts CLI-local schema registration +└── commands/ + β”œβ”€β”€ thread.ts Thread lifecycle and step execution + β”œβ”€β”€ workflow.ts Workflow registry (put/show/list) + β”œβ”€β”€ cas.ts CAS inspection and schema ops + β”œβ”€β”€ setup.ts Interactive/non-interactive setup + β”œβ”€β”€ skill.ts Built-in skill references + └── log.ts Process debug log management +``` + +## Configuration + +| File | Purpose | +|------|---------| +| `~/.uncaged/workflow/config.yaml` | Providers, models, default agent | +| `~/.uncaged/workflow/.env` | API keys (referenced by `apiKeyEnv` in config) | +| `~/.uncaged/workflow/registry.yaml` | Workflow name β†’ CAS hash | +| `~/.uncaged/workflow/threads.yaml` | Active thread head pointers | +| `~/.uncaged/workflow/cas/` | Content-addressed node storage | diff --git a/packages/workflow-agent-builtin/README.md b/packages/workflow-agent-builtin/README.md new file mode 100644 index 0000000..2a31b7f --- /dev/null +++ b/packages/workflow-agent-builtin/README.md @@ -0,0 +1,141 @@ +# @uncaged/workflow-agent-builtin + +`uwf-builtin` agent β€” built-in LLM agent with file read/write and shell tools. + +## Overview + +Layer 3 agent implementation. Runs an OpenAI-compatible chat completion loop with built-in tools (`read_file`, `write_file`, `run_command`). Uses the configured provider/model from `config.yaml`. Produces frontmatter markdown output and stores turn-by-turn session detail in CAS. + +Useful when you want a self-contained agent without an external CLI like Hermes or Claude Code. + +**Dependencies:** `@uncaged/json-cas`, `@uncaged/workflow-agent-kit`, `@uncaged/workflow-util` + +## Installation + +Included as the `uwf-builtin` binary when you install `@uncaged/workflow-agent-builtin`: + +```bash +bun add -g @uncaged/workflow-agent-builtin +``` + +## CLI Usage + +Invoked by `uwf thread step`: + +```bash +uwf-builtin +``` + +Configure as default agent: + +```bash +uwf setup --agent builtin +``` + +Override per step: + +```bash +uwf thread step --agent uwf-builtin +``` + +Environment variables set by the engine: + +| Variable | Purpose | +|----------|---------| +| `UWF_EDGE_PROMPT` | Moderator edge instruction for this step | + +## API + +All exports come from `src/index.ts`. + +### Agent factory + +```typescript +function createBuiltinAgent(): () => Promise +function buildBuiltinMessages(ctx: AgentContext): ChatMessage[] +``` + +### LLM loop + +```typescript +const BUILTIN_MAX_TURNS = 30; +const BUILTIN_CONTINUE_MAX_TURNS = 5; + +function runBuiltinLoop(/* options: RunBuiltinLoopOptions */): Promise +function chatCompletionWithTools( + provider: ResolvedLlmProvider, + messages: ChatMessage[], + tools: OpenAiToolDefinition[], +): Promise +``` + +`RunBuiltinLoopOptions` and `RunBuiltinLoopResult` are internal to `loop.ts` and not re-exported from `index.ts`. + +### Tools + +```typescript +function getBuiltinTools(): readonly BuiltinTool[] +function executeBuiltinTool( + name: string, + args: Record, + ctx: ToolContext, +): Promise +``` + +### Session and detail + +```typescript +function initSessionDir(storageRoot: string): Promise +function appendSessionTurn(storageRoot: string, sessionId: string, turn: BuiltinTurnPayload): Promise +function readSessionTurns(storageRoot: string, sessionId: string): Promise +function removeSession(storageRoot: string, sessionId: string): Promise +function registerBuiltinSchemas(store: Store): Promise +function storeBuiltinDetail(store: Store, payload: BuiltinDetailPayload): Promise +``` + +### Types + +```typescript +type ChatMessage = /* system | user | assistant | tool */; +type LlmAssistantResponse = { content: string | null; toolCalls: LlmToolCall[] | null }; +type LlmToolCall = { id: string; name: string; arguments: string }; +type BuiltinTool = { name: string; description: string; parameters: Record }; +type ToolContext = { cwd: string; storageRoot: string }; +type BuiltinDetailPayload = { /* session turns, model, timestamps */ }; +type BuiltinLoopTurn = { /* single loop iteration record */ }; +type BuiltinToolCallRecord = { /* tool call audit */ }; +type BuiltinToolResultRecord = { /* tool result audit */ }; +type BuiltinTurnPayload = { /* persisted turn */ }; +``` + +## Internal Structure + +``` +src/ +β”œβ”€β”€ index.ts +β”œβ”€β”€ cli.ts Binary entrypoint +β”œβ”€β”€ agent.ts createBuiltinAgent +β”œβ”€β”€ loop.ts Multi-turn LLM + tool loop +β”œβ”€β”€ prompt.ts buildBuiltinMessages +β”œβ”€β”€ session.ts Session directory persistence +β”œβ”€β”€ detail.ts CAS detail node storage +β”œβ”€β”€ schemas.ts Builtin CAS schemas +β”œβ”€β”€ types.ts Detail and turn payload types +β”œβ”€β”€ llm/ +β”‚ β”œβ”€β”€ index.ts +β”‚ β”œβ”€β”€ llm.ts chatCompletionWithTools +β”‚ └── types.ts ChatMessage, LlmToolCall, etc. +└── tools/ + β”œβ”€β”€ index.ts getBuiltinTools, executeBuiltinTool + β”œβ”€β”€ read-file.ts + β”œβ”€β”€ write-file.ts + β”œβ”€β”€ run-command.ts + β”œβ”€β”€ path.ts + └── types.ts +``` + +## Configuration + +Requires a configured OpenAI-compatible provider and model in `~/.uncaged/workflow/config.yaml` (via `uwf setup`). API keys are loaded from `~/.uncaged/workflow/.env`. + +Tools run with the current working directory as `ToolContext.cwd` (typically the directory where `uwf thread step` was invoked). diff --git a/packages/workflow-agent-claude-code/README.md b/packages/workflow-agent-claude-code/README.md new file mode 100644 index 0000000..78f19c7 --- /dev/null +++ b/packages/workflow-agent-claude-code/README.md @@ -0,0 +1,91 @@ +# @uncaged/workflow-agent-claude-code + +`uwf-claude-code` agent β€” spawns the Claude Code CLI and captures session detail. + +## Overview + +Layer 3 agent implementation. Spawns the `claude` CLI with a composed system prompt (role definition, task, prior steps, edge prompt). Parses stream or JSON stdout, caches session IDs for multi-turn continuation, and stores raw output plus structured detail in CAS. + +**Dependencies:** `@uncaged/json-cas`, `@uncaged/workflow-agent-kit` + +## Installation + +Included as the `uwf-claude-code` binary when you install `@uncaged/workflow-agent-claude-code`: + +```bash +bun add -g @uncaged/workflow-agent-claude-code +``` + +Requires the `claude` CLI on `PATH`. + +## CLI Usage + +Invoked by `uwf thread step`: + +```bash +uwf-claude-code +``` + +Configure or override the agent: + +```bash +uwf setup --agent claude-code +uwf thread step --agent uwf-claude-code +``` + +Environment variables set by the engine: + +| Variable | Purpose | +|----------|---------| +| `UWF_EDGE_PROMPT` | Moderator edge instruction for this step | + +## API + +All exports come from `src/index.ts`. + +### Agent factory + +```typescript +function createClaudeCodeAgent(): () => Promise +function buildClaudeCodePrompt(ctx: AgentContext): string +``` + +### Session detail + +```typescript +function parseClaudeCodeStreamOutput(stdout: string): ClaudeCodeParsedResult | null +function parseClaudeCodeJsonOutput(stdout: string): ClaudeCodeParsedResult | null +function storeClaudeCodeDetail( + store: Store, + parsed: ClaudeCodeParsedResult, + sessionId: string, +): Promise +function storeClaudeCodeRawOutput(store: Store, rawOutput: string): Promise +``` + +## Usage (library) + +```typescript +import { createClaudeCodeAgent, buildClaudeCodePrompt } from "@uncaged/workflow-agent-claude-code"; + +const main = createClaudeCodeAgent(); +void main(); +``` + +## Internal Structure + +``` +src/ +β”œβ”€β”€ index.ts +β”œβ”€β”€ cli.ts Binary entrypoint +β”œβ”€β”€ claude-code.ts createClaudeCodeAgent, buildClaudeCodePrompt, spawn logic +β”œβ”€β”€ session-detail.ts Parse stdout, store CAS detail nodes +β”œβ”€β”€ schemas.ts Claude Code detail CAS schemas +└── types.ts ClaudeCodeParsedResult, message shapes +``` + +## Configuration + +Uses session caching from `@uncaged/workflow-agent-kit` (`getCachedSessionId` / `setCachedSessionId`). No separate config file β€” relies on the Claude Code CLI's own authentication. + +Maximum turns per invocation: 90 (constant in `claude-code.ts`). diff --git a/packages/workflow-agent-hermes/README.md b/packages/workflow-agent-hermes/README.md new file mode 100644 index 0000000..e82786e --- /dev/null +++ b/packages/workflow-agent-hermes/README.md @@ -0,0 +1,90 @@ +# @uncaged/workflow-agent-hermes + +`uwf-hermes` agent β€” spawns Hermes chat via ACP and captures session detail. + +## Overview + +Layer 3 agent implementation. Wraps the Hermes CLI using the Agent Client Protocol (ACP). On first visit to a role it sends a composed prompt (role definition, task, history, edge prompt); on continuation it resumes the cached session. Session transcripts and raw output are stored as CAS detail nodes. + +**Dependencies:** `@uncaged/json-cas`, `@uncaged/workflow-agent-kit`, `@uncaged/workflow-protocol`, `@uncaged/workflow-util` + +## Installation + +Included as the `uwf-hermes` binary when you install `@uncaged/workflow-agent-hermes`: + +```bash +bun add -g @uncaged/workflow-agent-hermes +``` + +Requires the `hermes` CLI on `PATH`. + +## CLI Usage + +Invoked by `uwf thread step` (not typically run directly): + +```bash +uwf-hermes +``` + +Environment variables set by the engine: + +| Variable | Purpose | +|----------|---------| +| `UWF_EDGE_PROMPT` | Moderator edge instruction for this step | + +Configure as the default agent via `uwf setup --agent hermes`. + +Override per step: + +```bash +uwf thread step --agent uwf-hermes +``` + +## API + +All exports come from `src/index.ts`. + +### Agent factory + +```typescript +function createHermesAgent(): () => Promise +function buildHermesPrompt(ctx: AgentContext): string +``` + +### ACP client + +```typescript +class HermesAcpClient { + // Spawns hermes, handles JSON-RPC over stdio +} +``` + +## Usage (library) + +```typescript +import { createHermesAgent, buildHermesPrompt } from "@uncaged/workflow-agent-hermes"; + +// CLI entry (src/cli.ts): +const main = createHermesAgent(); +void main(); +``` + +## Internal Structure + +``` +src/ +β”œβ”€β”€ index.ts +β”œβ”€β”€ cli.ts Binary entrypoint +β”œβ”€β”€ hermes.ts createHermesAgent, buildHermesPrompt +β”œβ”€β”€ acp-client.ts HermesAcpClient β€” ACP JSON-RPC over stdio +β”œβ”€β”€ session-cache.ts Session ID cache (re-exports kit helpers + isResumeDisabled) +β”œβ”€β”€ session-detail.ts Parse Hermes session JSON, store CAS detail nodes +β”œβ”€β”€ schemas.ts Hermes detail CAS schemas +└── types.ts HermesSessionJson, HermesSessionMessage +``` + +## Configuration + +Uses workflow config from `~/.uncaged/workflow/config.yaml` (via agent-kit). Hermes session files are stored under the workflow storage root (see `session-detail.ts`). + +Set `UWF_HERMES_NO_RESUME=1` to disable session resume (see `isResumeDisabled` in `session-cache.ts`). diff --git a/packages/workflow-agent-kit/README.md b/packages/workflow-agent-kit/README.md new file mode 100644 index 0000000..fb7ab95 --- /dev/null +++ b/packages/workflow-agent-kit/README.md @@ -0,0 +1,182 @@ +# @uncaged/workflow-agent-kit + +Agent framework β€” `createAgent` factory, context builder, frontmatter fast-path, and LLM extract pipeline. + +## Overview + +Layer 2 agent framework. Provides the standard entrypoint for all agent CLIs: parse ` ` from argv, load thread/workflow context from CAS, invoke the agent's `run`/`continue` functions, validate output via frontmatter fast-path or LLM extract, and write a `StepNodePayload` to CAS. + +Also exports prompt builders, config/storage helpers, and session ID caching for multi-turn agents. + +**Dependencies:** `@uncaged/json-cas`, `@uncaged/json-cas-fs`, `@uncaged/workflow-protocol`, `@uncaged/workflow-util`, `dotenv`, `yaml` + +## Installation + +```bash +bun add @uncaged/workflow-agent-kit +``` + +## API + +All exports come from `src/index.ts`. + +### Agent factory + +```typescript +function createAgent(options: AgentOptions): () => Promise + +type AgentOptions = { + name: string; + run: AgentRunFn; + continue: AgentContinueFn; +}; + +type AgentRunFn = (ctx: AgentContext) => Promise; +type AgentContinueFn = ( + sessionId: string, + message: string, + store: AgentContext["store"], +) => Promise; + +type AgentRunResult = { + output: string; + detailHash: string; + sessionId: string; +}; +``` + +Agent CLIs call `createAgent(...)` and invoke the returned function as `main()`. + +### Context + +```typescript +function buildContext(threadId: ThreadId, role: string): Promise +function buildContextWithMeta( + threadId: ThreadId, + role: string, +): Promise + +type AgentContext = ModeratorContext & { + threadId: ThreadId; + role: string; + store: Store; + workflow: WorkflowPayload; + outputFormatInstruction: string; + edgePrompt: string; + isFirstVisit: boolean; +}; + +type BuildContextMeta = { + storageRoot: string; + store: Store; + schemas: AgentStore["schemas"]; + headHash: CasRef; + chain: ChainState; +}; +``` + +Requires `UWF_EDGE_PROMPT` in the environment (set by `uwf thread step`). + +### Prompt builders + +```typescript +function buildRolePrompt(role: RoleDefinition): string +function buildOutputFormatInstruction(schema: JSONSchema): string +function buildContinuationPrompt( + ctx: AgentContext, + priorOutput: string, + instruction: string, +): string +``` + +### Extract pipeline + +```typescript +function resolveExtractModelAlias(config: WorkflowConfig): ModelAlias +function resolveModel(config: WorkflowConfig, alias: ModelAlias): ResolvedLlmProvider +function extract( + rawOutput: string, + outputSchema: CasRef, + config: WorkflowConfig, +): Promise + +type ResolvedLlmProvider = { baseUrl: string; apiKey: string; model: string }; +type ExtractResult = { value: unknown; hash: CasRef }; +``` + +### Frontmatter fast-path + +```typescript +function tryFrontmatterFastPath( + rawOutput: string, + outputSchema: CasRef, + store: Store, +): Promise + +type FrontmatterFastPathResult = { body: string; outputHash: CasRef }; +``` + +### Session cache + +```typescript +function getCachedSessionId(threadId: ThreadId, role: string): Promise +function setCachedSessionId( + threadId: ThreadId, + role: string, + sessionId: string, +): Promise +``` + +### Config and storage + +```typescript +function getConfigPath(storageRoot: string): string +function getEnvPath(storageRoot: string): string +function resolveStorageRoot(): string +function loadWorkflowConfig(storageRoot: string): Promise +``` + +## Usage + +```typescript +import { createAgent, buildRolePrompt } from "@uncaged/workflow-agent-kit"; +import type { AgentContext, AgentRunResult } from "@uncaged/workflow-agent-kit"; + +async function run(ctx: AgentContext): Promise { + const prompt = buildRolePrompt(ctx.workflow.roles[ctx.role]!); + // ... spawn external process, capture output ... + return { output: markdown, detailHash: "...", sessionId: "..." }; +} + +async function continueSession( + sessionId: string, + message: string, +): Promise { + // ... continue multi-turn session ... + return { output: markdown, detailHash: "...", sessionId }; +} + +export const main = createAgent({ name: "my-agent", run, continue: continueSession }); +``` + +## Internal Structure + +``` +src/ +β”œβ”€β”€ index.ts +β”œβ”€β”€ run.ts createAgent entrypoint +β”œβ”€β”€ context.ts Thread chain walk, AgentContext builder +β”œβ”€β”€ extract.ts LLM structured extract fallback +β”œβ”€β”€ frontmatter.ts Frontmatter fast-path validation +β”œβ”€β”€ build-role-prompt.ts Role definition β†’ prompt text +β”œβ”€β”€ build-output-format-instruction.ts +β”œβ”€β”€ build-continuation-prompt.ts +β”œβ”€β”€ session-cache.ts Per-thread/session ID persistence +β”œβ”€β”€ storage.ts CAS store, config, threads index +β”œβ”€β”€ schemas.ts Agent CAS schema registration +└── types.ts AgentContext, AgentOptions, etc. +``` + +## Configuration + +Reads `config.yaml` and `.env` from the workflow storage root (`~/.uncaged/workflow` by default). See `@uncaged/workflow-protocol` for `WorkflowConfig` shape. Set via `uwf setup`. diff --git a/packages/workflow-dashboard/README.md b/packages/workflow-dashboard/README.md new file mode 100644 index 0000000..f0bc925 --- /dev/null +++ b/packages/workflow-dashboard/README.md @@ -0,0 +1,84 @@ +# @uncaged/workflow-dashboard + +Web graph editor for visualizing and editing workflow YAML definitions. + +## Overview + +A private alpha web app (not part of the runtime engine stack). Provides a React + `@xyflow/react` canvas for editing workflow roles, conditions, and graph transitions. Uses `@uncaged/workflow-protocol` types for validation and YAML round-tripping. + +Planned integration: local `uwf connect` over WebSocket to sync YAML between CLI and the browser editor. The REST API and Elysia backend are currently stubs for development. + +**Dependencies:** `@uncaged/workflow-protocol`, `@xyflow/react`, React 19, react-router v7, Vite 8, Tailwind CSS v4, Elysia + +## Installation + +Monorepo-only ( `"private": true` ). Not published to npm. + +```bash +cd packages/workflow-dashboard +bun install --no-cache +``` + +## CLI Usage + +Start the Vite dev server (port 3000): + +```bash +cd packages/workflow-dashboard +bun run dev +``` + +Build for production: + +```bash +bun run build +``` + +Open `http://localhost:3000` in a browser. + +## Internal Structure + +``` +workflow-dashboard/ +β”œβ”€β”€ server.ts Vite dev server entry (port 3000) +β”œβ”€β”€ vite.config.ts Vite + React + Tailwind + Elysia plugin +β”œβ”€β”€ vite-dev.ts Custom Vite plugin +β”œβ”€β”€ index.html +β”œβ”€β”€ components.json shadcn configuration +β”œβ”€β”€ server/ +β”‚ β”œβ”€β”€ api.ts Elysia REST API (health + workflow CRUD stub) +β”‚ └── workflow.ts Workflow file read/write + format conversion +└── src/ + β”œβ”€β”€ main.tsx React DOM entry + β”œβ”€β”€ app.tsx Root layout + β”œβ”€β”€ router.tsx Hash-mode routes + β”œβ”€β”€ index.css + β”œβ”€β”€ lib/utils.ts Tailwind cn() helper + β”œβ”€β”€ components/ui/ shadcn components (button, card, dialog, input, …) + β”œβ”€β”€ pages/ + β”‚ β”œβ”€β”€ home.tsx Workflow list + β”‚ β”œβ”€β”€ detail.tsx Workflow detail view + β”‚ └── editor.tsx Full editor page + └── editor/ Core graph editor + β”œβ”€β”€ flow.tsx FlowEditor component + β”œβ”€β”€ context.tsx State (useSyncExternalStore + Immer) + β”œβ”€β”€ injection.ts DI container + β”œβ”€β”€ type.ts Internal editor types + β”œβ”€β”€ model/ Node/edge state model + β”œβ”€β”€ nodes/ Start, role, end node components + β”œβ”€β”€ edges/ Conditional edge rendering + β”œβ”€β”€ panel/ Toolbar, add/edit panels + β”œβ”€β”€ trans/ YAML ↔ graph conversion (trans-in, trans-out, validate) + β”œβ”€β”€ layout/ Auto-layout + └── utils/ Event helpers, click-outside hook +``` + +## Configuration + +| Setting | Default | Notes | +|---------|---------|-------| +| Dev server port | `3000` | Set in `server.ts` | +| Workflow storage (dev) | `tmp/workflow/` | YAML files during development | +| Path alias | `@/` β†’ `src/` | Configured in `vite.config.ts` | + +No library API β€” this package is an application, not importable as a module. diff --git a/packages/workflow-moderator/README.md b/packages/workflow-moderator/README.md new file mode 100644 index 0000000..e4978d6 --- /dev/null +++ b/packages/workflow-moderator/README.md @@ -0,0 +1,60 @@ +# @uncaged/workflow-moderator + +JSONata-based graph evaluator β€” determines the next role or `$END` with zero LLM cost. + +## Overview + +The moderator (Layer 1) walks the workflow graph from the current role. For each outgoing transition it evaluates an optional JSONata condition against `ModeratorContext` (start prompt + prior step outputs). The first truthy transition wins; its target role and edge prompt are returned. When no transition matches, the workflow ends (`$END`). + +**Dependencies:** `@uncaged/workflow-protocol`, `jsonata` + +## Installation + +```bash +bun add @uncaged/workflow-moderator +``` + +## API + +### Functions + +```typescript +function evaluate( + workflow: WorkflowPayload, + context: ModeratorContext, +): Promise> +``` + +Returns `{ ok: true, value: { role, prompt } }` where `role` is the next role name or `"$END"`, and `prompt` is the edge instruction for the agent. + +### Types + +```typescript +type EvaluateResult = { + role: string; + prompt: string; +}; +``` + +The `Result` type is local to this package (`{ ok: true; value: T } | { ok: false; error: E }`), not re-exported from `index.ts`. + +## Usage + +```typescript +import { evaluate } from "@uncaged/workflow-moderator"; +import type { ModeratorContext, WorkflowPayload } from "@uncaged/workflow-protocol"; + +const result = await evaluate(workflow, context); +if (result.ok && result.value.role !== "$END") { + console.log(`Next role: ${result.value.role}, prompt: ${result.value.prompt}`); +} +``` + +## Internal Structure + +``` +src/ +β”œβ”€β”€ index.ts Public exports +β”œβ”€β”€ evaluate.ts Graph walk + JSONata condition evaluation +└── types.ts EvaluateResult, Result +``` diff --git a/packages/workflow-protocol/README.md b/packages/workflow-protocol/README.md new file mode 100644 index 0000000..f76b0d1 --- /dev/null +++ b/packages/workflow-protocol/README.md @@ -0,0 +1,193 @@ +# @uncaged/workflow-protocol + +Shared TypeScript types and JSON Schema constants for the workflow engine. + +## Overview + +This is the contract layer (Layer 0). It defines `WorkflowPayload`, thread node payloads, moderator context, CLI output shapes, and configuration types used across every other package. It has no runtime logic beyond exporting schema objects from `@uncaged/json-cas`. + +**Dependencies:** `@uncaged/json-cas`, `@uncaged/json-cas-fs` + +## Installation + +```bash +bun add @uncaged/workflow-protocol +``` + +## API + +All exports come from `src/index.ts`. + +### JSON Schema constants + +```typescript +START_NODE_SCHEMA: JSONSchema +STEP_NODE_SCHEMA: JSONSchema +WORKFLOW_SCHEMA: JSONSchema +``` + +### Core identifiers + +```typescript +type CasRef = string // XXH64 hash, 13-char Crockford Base32 +type ThreadId = string // ULID, 26-char Crockford Base32 +type WorkflowName = string +type RoleName = string +``` + +### Workflow definition + +```typescript +type RoleDefinition = { + description: string; + goal: string; + capabilities: string[]; + procedure: string; + output: string; + frontmatter: CasRef; +}; + +type Transition = { + role: string; + condition: string | null; + prompt: string; +}; + +type ConditionDefinition = { + description: string; + expression: string; +}; + +type WorkflowPayload = { + name: string; + description: string; + roles: Record; + conditions: Record; + graph: Record; +}; +``` + +### Thread nodes + +```typescript +type StepRecord = { + role: string; + output: CasRef; + detail: CasRef; + agent: string; + edgePrompt: string; +}; + +type StartNodePayload = { + workflow: CasRef; + prompt: string; +}; + +type StepNodePayload = StepRecord & { + start: CasRef; + prev: CasRef | null; +}; +``` + +### Moderator context + +```typescript +type StepContext = Omit & { output: unknown }; + +type ModeratorContext = { + start: StartNodePayload; + steps: StepContext[]; +}; +``` + +### Configuration + +```typescript +type ProviderAlias = string; +type ModelAlias = string; +type AgentAlias = string; + +type ProviderConfig = { baseUrl: string; apiKeyEnv: string }; +type ModelConfig = { + provider: ProviderAlias; + name: string; +}; + +type AgentConfig = { + command: string; + args: string[]; +}; + +type WorkflowConfig = { + providers: Record; + models: Record; + agents: Record; + defaultAgent: AgentAlias; + agentOverrides: Record> | null; + defaultModel: ModelAlias; + modelOverrides: Record | null; +}; +``` + +### CLI output types + +```typescript +type StartOutput = { workflow: CasRef; thread: ThreadId }; + +type StepOutput = { + workflow: CasRef; + thread: ThreadId; + head: CasRef; + done: boolean; +}; + +type StepEntry = { + hash: CasRef; + role: string; + output: unknown; + detail: CasRef; + agent: string; + timestamp: number; +}; + +type StartEntry = { + hash: CasRef; + workflow: CasRef; + prompt: string; + timestamp: number; +}; + +type ThreadStepsOutput = { + thread: ThreadId; + workflow: CasRef; + steps: [StartEntry, ...StepEntry[]]; +}; + +type ThreadForkOutput = { + thread: ThreadId; + forkedFrom: { step: CasRef }; +}; + +type ThreadListItem = { + thread: ThreadId; + workflow: CasRef; + head: CasRef; +}; + +type ThreadsIndex = Record; + +type Scenario = string; +``` + +## Internal Structure + +``` +src/ +β”œβ”€β”€ index.ts Public re-exports +β”œβ”€β”€ types.ts All type definitions +└── schemas.ts START_NODE_SCHEMA, STEP_NODE_SCHEMA, WORKFLOW_SCHEMA +``` + +## Configuration + +This package defines `WorkflowConfig` types only. Runtime config loading lives in `@uncaged/workflow-agent-kit` (`loadWorkflowConfig`). diff --git a/packages/workflow-util/README.md b/packages/workflow-util/README.md index d7a0836..1a9e8b8 100644 --- a/packages/workflow-util/README.md +++ b/packages/workflow-util/README.md @@ -1,32 +1,145 @@ # @uncaged/workflow-util -Shared utilities: encoding, IDs, logging, storage paths, and ref-field normalization. +Shared utilities: encoding, IDs, logging, frontmatter parsing, storage paths, and CLI reference generation. -## What This Package Does +## Overview -It provides filesystem-safe Base32 and ULID generation, the structured logger used across packages, helpers for the default workflow data directory and global CAS path, and utilities to merge/normalize `refs` on steps. It re-exports `ok`/`err` from protocol for convenience. +Layer 1 shared infrastructure used across CLI, agent-kit, and agent packages. Provides Crockford Base32 encoding, ULID generation, structured logging with fixed 8-char tags, frontmatter markdown parsing/validation, process-level debug logging, and helpers for the default workflow data directory. -## Key Exports +**Dependencies:** none (standalone) -From `src/index.ts`: +## Installation -- **Base32:** `CROCKFORD_BASE32_ALPHABET`, `decodeCrockfordBase32Bits`, `decodeCrockfordToUint64`, `encodeCrockfordBase32Bits`, `encodeUint64AsCrockford` -- **Logger:** `createLogger` -- **Refs:** `mergeRefsWithContentHash`, `normalizeRefsField` -- **Result:** `ok`, `err` (from `@uncaged/workflow-protocol`) -- **Paths:** `getDefaultWorkflowStorageRoot`, `getGlobalCasDir` -- **ULID:** `generateUlid` -- **Types:** `CreateLoggerOptions`, `LogFn`, `LoggerSink`, `Result` +```bash +bun add @uncaged/workflow-util +``` -## Dependencies +## API -- **Workspace:** `@uncaged/workflow-protocol` β€” `Result` and shared types used by helpers +All exports come from `src/index.ts`. + +### Encoding and IDs + +```typescript +function encodeUint64AsCrockford(value: bigint): string +function generateUlid(nowMs: number): string +``` + +### Logging + +```typescript +function createLogger(options?: { sink: { kind: "stderr" } }): LogFn + +type LogFn = (tag: string, message: string) => void +// CreateLoggerOptions and LoggerSink are internal types +``` + +### Process logger + +```typescript +function createProcessLogger(options: CreateProcessLoggerOptions): ProcessLogger + +type ProcessLogger = { + pid: string; + log: ProcessLogFn; +}; + +type ProcessLoggerContext = { + thread: string | null; + workflow: string | null; +}; + +type CreateProcessLoggerOptions = { + storageRoot: string | null; + context: ProcessLoggerContext; +}; + +type ProcessLogFn = ( + tag: string, + msg: string, + context: Record | null, +) => void; +``` + +### Frontmatter markdown + +```typescript +function parseFrontmatterMarkdown(raw: string): ParsedFrontmatterMarkdown +function validateFrontmatter( + parsed: ParsedFrontmatterMarkdown, + schema: Record, +): FrontmatterValidationError[] + +type ParsedFrontmatterMarkdown = { + frontmatter: Record; + body: string; +}; + +type AgentFrontmatter = { /* standard agent frontmatter fields */ }; +type FrontmatterScope = string; +type FrontmatterStatus = string; +type FrontmatterValidationError = { path: string; message: string }; +``` + +### Result helpers + +```typescript +function ok(value: T): Result +function err(error: E): Result + +type Result = { ok: true; value: T } | { ok: false; error: E } +``` + +### Storage paths + +```typescript +function getDefaultWorkflowStorageRoot(): string +function getGlobalCasDir(storageRoot: string | undefined): string +``` + +### Refs and misc + +```typescript +function normalizeRefsField(value: unknown): string[] +function generateCliReference(): string +function env(name: string, fallback: string): string +``` ## Usage ```typescript -import { createLogger, getDefaultWorkflowStorageRoot, generateUlid } from "@uncaged/workflow-util"; +import { + createLogger, + generateUlid, + getDefaultWorkflowStorageRoot, + parseFrontmatterMarkdown, +} from "@uncaged/workflow-util"; const log = createLogger(); -log("4KNMR2PX", "example"); +log("4KNMR2PX", "Loading workflow..."); + +const root = getDefaultWorkflowStorageRoot(); +const threadId = generateUlid(Date.now()); ``` + +## Internal Structure + +``` +src/ +β”œβ”€β”€ index.ts +β”œβ”€β”€ base32.ts Crockford Base32 encode/decode +β”œβ”€β”€ ulid.ts ULID generation +β”œβ”€β”€ logger.ts Structured logger +β”œβ”€β”€ process-logger/ Process-level debug log files +β”œβ”€β”€ frontmatter-markdown/ Parse and validate agent frontmatter +β”œβ”€β”€ refs-field.ts Normalize refs arrays on CAS nodes +β”œβ”€β”€ result.ts ok / err helpers +β”œβ”€β”€ storage-root.ts Default ~/.uncaged/workflow paths +β”œβ”€β”€ env.ts Environment variable helper +β”œβ”€β”€ cli-reference.ts Markdown CLI reference generator +└── types.ts LogFn, Result, logger options +``` + +## Configuration + +`getDefaultWorkflowStorageRoot()` resolves to `~/.uncaged/workflow` unless overridden by environment (see `storage-root.ts`).