Merge pull request 'chore: migrate json-cas to ocas' (#586) from chore/migrate-ocas into main
CI / check (push) Failing after 1m10s

This commit was merged in pull request #586.
This commit is contained in:
2026-06-02 03:07:50 +00:00
60 changed files with 147 additions and 143 deletions
+2 -2
View File
@@ -13,7 +13,7 @@ This monorepo implements a stateless workflow engine driven by a single-step CLI
| **Role** | A named actor within a workflow. Each role has a system prompt and a JSON Schema `outputSchema`. |
| **Moderator** | Status-based graph evaluator — determines the next role (or `$END`) with zero LLM cost. |
| **Agent** | An external CLI command (`uwf-hermes`, etc.) spawned by `uwf thread step`. Produces frontmatter markdown output. |
| **CAS** | Content-Addressed Storage via `@uncaged/json-cas` — all workflow definitions, thread nodes, and outputs are immutable CAS nodes. |
| **CAS** | Content-Addressed Storage via `@ocas/core` — all workflow definitions, thread nodes, and outputs are immutable CAS nodes. |
| **Registry** | `~/.uncaged/workflow/registry.yaml` — maps workflow names to current CAS hashes. |
### Monorepo Structure
@@ -35,7 +35,7 @@ workflow/
- Dependency layers: `workflow-protocol``workflow-util``workflow-util-agent``workflow-agent-hermes` / `cli-workflow`
- Packages use `workspace:^` protocol (resolves to `^x.y.z` on publish)
- External CAS: `@uncaged/json-cas` (store API, hashing, schema validation) + `@uncaged/json-cas-fs` (filesystem backend)
- External CAS: `@ocas/core` (store API, hashing, schema validation) + `@ocas/fs` (filesystem backend)
## Language & Paradigm
+1 -1
View File
@@ -67,7 +67,7 @@ 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).
External CAS: [`@ocas/core`](https://www.npmjs.com/package/@ocas/core) (store API, hashing, schema validation) + `@ocas/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.
+15 -15
View File
@@ -8,13 +8,13 @@
A stateless workflow engine driven by a single-step CLI. Workflows are YAML definitions stored as CAS nodes; threads are immutable chains of CAS-linked step nodes. No daemon — each `uwf thread step` invocation runs one moderator→agent→extract cycle and exits.
The implementation lives in **5** active packages under `packages/`, plus two external CAS packages (`@uncaged/json-cas`, `@uncaged/json-cas-fs`). Legacy packages reside in `legacy-packages/` and are not part of the active stack.
The implementation lives in **5** active packages under `packages/`, plus two external CAS packages (`@ocas/core`, `@ocas/fs`). Legacy packages reside in `legacy-packages/` and are not part of the active stack.
## Package map
| Layer | Package | One-line role |
|-------|---------|---------------|
| Contract | `@uncaged/workflow-protocol``workflow-protocol` | Shared TypeScript types (`WorkflowPayload`, `StepNodePayload`, `ModeratorContext`, `WorkflowConfig`, etc.). No runtime deps beyond `@uncaged/json-cas-fs`. |
| Contract | `@uncaged/workflow-protocol``workflow-protocol` | Shared TypeScript types (`WorkflowPayload`, `StepNodePayload`, `ModeratorContext`, `WorkflowConfig`, etc.). No runtime deps beyond `@ocas/fs`. |
| Shared infra | `@uncaged/workflow-util``workflow-util` | Crockford Base32, ULID generation, `createLogger`, frontmatter parsing/validation. |
| Agent framework | `@uncaged/workflow-util-agent``workflow-util-agent` | `createAgent` entrypoint factory, context builder, frontmatter fast-path extractor, LLM extract fallback, output format instruction builder. |
| Agent: Hermes | `@uncaged/workflow-agent-hermes``workflow-agent-hermes` | `uwf-hermes` CLI binary — spawns `hermes chat`, pipes prompt, captures session detail. |
@@ -24,8 +24,8 @@ The implementation lives in **5** active packages under `packages/`, plus two ex
| Package | Role |
|---------|------|
| `@uncaged/json-cas` | Content-addressed store API, XXH64 hashing, JSON Schema registration and validation. |
| `@uncaged/json-cas-fs` | Filesystem backend for `json-cas`. |
| `@ocas/core` | Content-addressed store API, XXH64 hashing, JSON Schema registration and validation. |
| `@ocas/fs` | Filesystem backend for `ocas`. |
| `mustache` | Template renderer for edge prompts (used by `cli-workflow` moderator). |
| `commander` | CLI argument parsing (used by `cli-workflow`). |
| `dotenv` | Loads `.env` files for API keys. |
@@ -36,8 +36,8 @@ The implementation lives in **5** active packages under `packages/`, plus two ex
```mermaid
flowchart BT
subgraph External
jcas["@uncaged/json-cas"]
jcasfs["@uncaged/json-cas-fs"]
jcas["@ocas/core"]
jcasfs["@ocas/fs"]
end
subgraph L0["Layer 0 — contract"]
protocol["@uncaged/workflow-protocol"]
@@ -146,7 +146,7 @@ Key properties:
- **`roles`** — inline role definitions; each `meta` is a JSON Schema (stored as its own CAS node on registration)
- **`graph`** — `Record<Role | "$START", Record<Status, Target>>` — status-based routing; each role maps statuses to targets
- **No agent binding** — agent selection is a deployment concern, configured in `config.yaml`
- **No Zod** — all schemas are JSON Schema, validated through `@uncaged/json-cas`
- **No Zod** — all schemas are JSON Schema, validated through `@ocas/core`
## Three-phase engine loop
@@ -263,7 +263,7 @@ Structured output extraction uses a two-layer strategy (`workflow-util-agent`):
2. Validate required fields (`validateFrontmatter`)
3. Build a candidate object from frontmatter fields (`status`, `next`, `confidence`, `artifacts`, `scope`)
4. `store.put()` the candidate against the role's `meta` schema
5. Validate with `json-cas` schema validation
5. Validate with `ocas` schema validation
6. If valid → return `outputHash` (zero LLM cost)
### Layer 2: LLM extract fallback (`extract.ts`)
@@ -302,7 +302,7 @@ payload:
capabilities: [planning, issue-analysis]
procedure: "Analyze the issue and create a plan."
output: "Output the plan summary."
meta: "5GWKR8TN1V3JA" # cas_ref → JSON Schema node
meta: "5GWKR8TN1V3JA" # ocas_ref → JSON Schema node
conditions:
notApproved:
description: "Reviewer rejected"
@@ -318,7 +318,7 @@ payload:
```yaml
type: <start-node-schema-hash>
payload:
workflow: "4KNM2PXR3B1QW" # cas_ref → Workflow
workflow: "4KNM2PXR3B1QW" # ocas_ref → Workflow
prompt: "Fix the login bug..."
```
@@ -327,11 +327,11 @@ payload:
```yaml
type: <step-node-schema-hash>
payload:
start: "4TNVW8KR2B3MA" # cas_ref → StartNode
prev: "2MXBG6PN4A8JR" # cas_ref → previous StepNode (null for first step)
start: "4TNVW8KR2B3MA" # ocas_ref → StartNode
prev: "2MXBG6PN4A8JR" # ocas_ref → previous StepNode (null for first step)
role: "developer"
output: "9KRVW3TN5F1QA" # cas_ref → structured output (validated against meta schema)
detail: "7BQST3VW9F2MA" # cas_ref → execution detail (raw turns, session data)
output: "9KRVW3TN5F1QA" # ocas_ref → structured output (validated against meta schema)
detail: "7BQST3VW9F2MA" # ocas_ref → execution detail (raw turns, session data)
agent: "uwf-hermes" # agent command used (plain string)
```
@@ -484,7 +484,7 @@ Binary: `uwf`
| **Frontmatter markdown output** | Agents produce structured meta (YAML frontmatter) alongside free-form content (markdown body). Enables zero-cost extraction when frontmatter is well-formed. |
| **Two-layer extract** | Fast path avoids LLM calls when agents follow the format; LLM fallback handles messy output gracefully. |
| **Prompt injection for format** | Output format instruction prepended to system prompt ensures agents produce parseable output without per-agent configuration. |
| **JSON Schema (not Zod)** | Schemas are CAS-native data — storable, hashable, validatable through `json-cas`. No code generation, no runtime library dependency. |
| **JSON Schema (not Zod)** | Schemas are CAS-native data — storable, hashable, validatable through `ocas`. No code generation, no runtime library dependency. |
| **Agent as external command** | Agents are independent CLI binaries (`uwf-hermes`, `uwf-cursor`). Swappable per workflow/role via config. No tight coupling to the engine. |
| **No daemon** | Process starts, does one step, exits. Simpler failure model, no connection management. |
| **Crockford Base32** | Filesystem-safe, case-insensitive, readable, compact. |
+1 -1
View File
@@ -630,7 +630,7 @@ flowchart TB
Spawn -->|"stdout: step hash"| Step
```
**新包**:`packages/workflow-agent-builtin`,bin `uwf-builtin`,仅依赖 `workflow-util-agent`、`workflow-protocol`、`workflow-util`(可选 `@uncaged/json-cas` 写 detail schema)。
**新包**:`packages/workflow-agent-builtin`,bin `uwf-builtin`,仅依赖 `workflow-util-agent`、`workflow-protocol`、`workflow-util`(可选 `@ocas/core` 写 detail schema)。
**分层**:
+25 -25
View File
@@ -22,7 +22,7 @@ uwf workflow show <workflow-id> # 查看 workflow 定义
uwf workflow list # 列出已注册 workflows
```
两组对称,各 3-4 个子命令。CAS 操作交给 `json-cas` CLI,不在 `uwf` 中重复。
两组对称,各 3-4 个子命令。CAS 操作交给 `ocas` CLI,不在 `uwf` 中重复。
### 1.2 `uwf thread start`
@@ -136,14 +136,14 @@ uwf-hermes <thread-id> <role>
沿用 json-cas 的三层:bootstrap meta-schema → JSON Schema nodes → data nodes。
下面所有 CAS 节点都遵循 `{ type: cas_ref, payload: T, timestamp: number }` 的标准格式。
`cas_ref` 类型的字符串字段在 json-cas 中已内置支持,不需要额外的 `$ref` 包装。
下面所有 CAS 节点都遵循 `{ type: ocas_ref, payload: T, timestamp: number }` 的标准格式。
`ocas_ref` 类型的字符串字段在 ocas 中已内置支持,不需要额外的 `$ref` 包装。
### 2.2 数据节点
#### `Workflow`
Roles 和 moderator 内联在 Workflow 中,只有 meta 独立为 CAS 节点(方便 json-cas 校验)。
Roles 和 moderator 内联在 Workflow 中,只有 meta 独立为 CAS 节点(方便 ocas 校验)。
```yaml
type: <workflow-schema-hash>
@@ -157,21 +157,21 @@ payload:
capabilities: [planning, issue-analysis]
procedure: "Analyze the issue and create a plan."
output: "Output the plan summary."
meta: "5GWKR8TN1V3JA" # cas_ref → JSON Schema 节点(json-cas 内置)
meta: "5GWKR8TN1V3JA" # ocas_ref → JSON Schema 节点(ocas 内置)
developer:
description: "Implements code changes"
goal: "You are a developer agent..."
capabilities: [file-edit, shell]
procedure: "Implement the plan."
output: "List all files changed."
meta: "8CNWT4KR6D1HV" # cas_ref → JSON Schema 节点
meta: "8CNWT4KR6D1HV" # ocas_ref → JSON Schema 节点
reviewer:
description: "Reviews code changes"
goal: "You are a code reviewer..."
capabilities: [code-review]
procedure: "Review the implementation."
output: "Approve or reject with comments."
meta: "1VPBG9SM5E7WK" # cas_ref → JSON Schema 节点
meta: "1VPBG9SM5E7WK" # ocas_ref → JSON Schema 节点
conditions:
needsClarification:
description: "Planner requests clarification from user"
@@ -198,7 +198,7 @@ payload:
condition: null
```
- `roles` — 内联定义,每个 role 的 `meta` 是独立的 cas_ref(指向 json-cas 内置 JSON Schema 节点)
- `roles` — 内联定义,每个 role 的 `meta` 是独立的 ocas_ref(指向 ocas 内置 JSON Schema 节点)
- `graph``Record<Role | "$START", Record<Status, Target>>`,每个 Target = `{ role, prompt }`
- Status 来自上一个 role 输出的 `status` 字段,`$START``_` 作为初始 status
- Prompt 模板使用 Mustache 渲染,变量来自 lastOutput
@@ -220,7 +220,7 @@ evaluate(graph, lastRole, lastOutput) → { role, prompt }
```yaml
type: <start-node-schema-hash>
payload:
workflow: "4KNM2PXR3B1QW" # cas_ref → Workflow
workflow: "4KNM2PXR3B1QW" # ocas_ref → Workflow
prompt: "Fix the login bug..."
```
@@ -232,18 +232,18 @@ payload:
```yaml
type: <step-node-schema-hash>
payload:
start: "4TNVW8KR2B3MA" # cas_ref → StartNode(每个 step 都引用)
prev: "2MXBG6PN4A8JR" # cas_ref → 前一个 StepNode,第一步为 null
start: "4TNVW8KR2B3MA" # ocas_ref → StartNode(每个 step 都引用)
prev: "2MXBG6PN4A8JR" # ocas_ref → 前一个 StepNode,第一步为 null
role: "developer"
output: "9KRVW3TN5F1QA" # cas_ref → 结构化输出节点(符合 role 的 meta schema)
detail: "7BQST3VW9F2MA" # cas_ref → 执行详情(content node / 子 workflow terminal StepNode / ...)
output: "9KRVW3TN5F1QA" # ocas_ref → 结构化输出节点(符合 role 的 meta schema)
detail: "7BQST3VW9F2MA" # ocas_ref → 执行详情(content node / 子 workflow terminal StepNode / ...)
agent: "uwf-cursor" # 实际使用的 agent 命令(纯字符串)
```
- `start` — 每个 StepNode 都直接引用 StartNode,方便随机访问
- `prev` — 前一个 StepNode 的 cas_ref,第一步为 `null`(不指向 StartNode)
- `output` — cas_ref,指向符合 role meta schema 的 CAS 节点,可用 json-cas 校验
- `detail` — cas_ref,指向执行详情。可以是原始 agent 输出(content node),也可以是子 workflow thread 的 terminal StepNode(workflowAsAgent 场景)
- `prev` — 前一个 StepNode 的 ocas_ref,第一步为 `null`(不指向 StartNode)
- `output`ocas_ref,指向符合 role meta schema 的 CAS 节点,可用 ocas 校验
- `detail`ocas_ref,指向执行详情。可以是原始 agent 输出(content node),也可以是子 workflow thread 的 terminal StepNode(workflowAsAgent 场景)
- `agent` — 纯字符串,不是 CAS 节点
### 2.3 链式结构
@@ -337,7 +337,7 @@ OPENROUTER_API_KEY=sk-or-...
## 3. 包结构
全新包,不复用现有 packages,避免命名冲突。CAS 直接依赖 `@uncaged/json-cas`
全新包,不复用现有 packages,避免命名冲突。CAS 直接依赖 `@ocas/core`
```
packages/
@@ -349,8 +349,8 @@ packages/
```
**外部依赖:**
- `@uncaged/json-cas` — CAS 存储、hash、schema 校验
- `@uncaged/json-cas-fs` — 文件系统 CAS 后端
- `@ocas/core` — CAS 存储、hash、schema 校验
- `@ocas/fs` — 文件系统 CAS 后端
**现有包全部保留不动**,新旧并存,逐步迁移。
@@ -372,8 +372,8 @@ type ThreadId = string;
/** 一个 step 的核心数据,被 StepNode payload 和 moderator 上下文共享 */
type StepRecord = {
role: string;
output: CasRef; // cas_ref → 结构化输出节点(符合 role meta schema)
detail: CasRef; // cas_ref → 执行详情(content node / 子 workflow terminal StepNode)
output: CasRef; // ocas_ref → 结构化输出节点(符合 role meta schema)
detail: CasRef; // ocas_ref → 执行详情(content node / 子 workflow terminal StepNode)
agent: string; // 实际使用的 agent 命令(纯字符串)
};
```
@@ -387,7 +387,7 @@ type RoleDefinition = {
capabilities: string[];
procedure: string;
output: string;
meta: CasRef; // cas_ref → json-cas 内置 JSON Schema 节点
meta: CasRef; // ocas_ref → ocas 内置 JSON Schema 节点
};
type Target = {
@@ -407,13 +407,13 @@ type WorkflowPayload = {
```typescript
type StartNodePayload = {
workflow: CasRef; // cas_ref → Workflow
workflow: CasRef; // ocas_ref → Workflow
prompt: string;
};
type StepNodePayload = StepRecord & {
start: CasRef; // cas_ref → StartNode(每个 step 都引用)
prev: CasRef | null; // cas_ref → 前一个 StepNode,第一步为 null
start: CasRef; // ocas_ref → StartNode(每个 step 都引用)
prev: CasRef | null; // ocas_ref → 前一个 StepNode,第一步为 null
};
```
+2 -2
View File
@@ -20,7 +20,7 @@ workflow → thread → step → turn
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-util-agent`, `@uncaged/workflow-protocol`, `@uncaged/workflow-util`, `commander`, `dotenv`, `mustache`, `yaml`
**Dependencies:** `@ocas/core`, `@ocas/fs`, `@uncaged/workflow-util-agent`, `@uncaged/workflow-protocol`, `@uncaged/workflow-util`, `commander`, `dotenv`, `mustache`, `yaml`
## Installation
@@ -209,7 +209,7 @@ src/
| `~/.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/json-cas/` | Content-addressed node storage (unified CAS store, shared with `json-cas` CLI) |
| `~/.uncaged/json-cas/` | Content-addressed node storage (unified CAS store, shared with `ocas` CLI) |
### Environment Variables
+2 -2
View File
@@ -11,8 +11,8 @@
"uwf": "./dist/cli.js"
},
"dependencies": {
"@uncaged/json-cas": "^0.5.3",
"@uncaged/json-cas-fs": "^0.5.3",
"@ocas/core": "^0.1.1",
"@ocas/fs": "^0.1.1",
"@uncaged/workflow-protocol": "workspace:^",
"@uncaged/workflow-util": "workspace:^",
"@uncaged/workflow-util-agent": "workspace:^",
@@ -2,8 +2,8 @@ import { execFileSync } from "node:child_process";
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { putSchema } from "@uncaged/json-cas";
import { createFsStore } from "@uncaged/json-cas-fs";
import { putSchema } from "@ocas/core";
import { createFsStore } from "@ocas/fs";
import type { CasRef, StepNodePayload, ThreadId } from "@uncaged/workflow-protocol";
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import { registerUwfSchemas } from "../schemas.js";
@@ -720,7 +720,7 @@ defaultModel: default
describe("no legacy apiKeyEnv references", () => {
test("config.ts has no references to apiKeyEnv", () => {
const configSource = readFileSync(join(__dirname, "..", "commands", "config.ts"), "utf8");
const configSource = readFileSync(join(__dirname, "..", "..", "src", "commands", "config.ts"), "utf8");
expect(configSource).not.toContain("apiKeyEnv");
});
@@ -1,7 +1,7 @@
import { mkdir, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { putSchema } from "@uncaged/json-cas";
import { putSchema } from "@ocas/core";
import type { CasRef, ThreadId } from "@uncaged/workflow-protocol";
import { describe, expect, test } from "vitest";
import { createMarker, deleteMarker } from "../background/index.js";
@@ -1,8 +1,8 @@
import { mkdir, mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { bootstrap, putSchema } from "@uncaged/json-cas";
import { createFsStore } from "@uncaged/json-cas-fs";
import { bootstrap, putSchema } from "@ocas/core";
import { createFsStore } from "@ocas/fs";
import type { CasRef } from "@uncaged/workflow-protocol";
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import { cmdStepRead } from "../commands/step.js";
@@ -40,7 +40,7 @@ const DETAIL_SCHEMA = {
turnCount: { type: "integer" as const },
turns: {
type: "array" as const,
items: { type: "string" as const, format: "cas_ref" },
items: { type: "string" as const, format: "ocas_ref" },
},
},
additionalProperties: false,
@@ -1,8 +1,8 @@
import { mkdir, mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { bootstrap, type Hash, type JSONSchema, putSchema } from "@uncaged/json-cas";
import { createFsStore } from "@uncaged/json-cas-fs";
import { bootstrap, type Hash, type JSONSchema, putSchema } from "@ocas/core";
import { createFsStore } from "@ocas/fs";
import type { CasRef, StepNodePayload } from "@uncaged/workflow-protocol";
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import { cmdStepShow } from "../commands/step.js";
@@ -45,7 +45,7 @@ const DETAIL_SCHEMA: JSONSchema = {
properties: {
turns: {
type: "array",
items: { type: "string", format: "cas_ref" },
items: { type: "string", format: "ocas_ref" },
},
},
additionalProperties: false,
@@ -1,8 +1,8 @@
import { mkdir, mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { bootstrap, putSchema } from "@uncaged/json-cas";
import { createFsStore } from "@uncaged/json-cas-fs";
import { bootstrap, putSchema } from "@ocas/core";
import { createFsStore } from "@ocas/fs";
import type { CasRef, ThreadId } from "@uncaged/workflow-protocol";
import { STEP_NODE_SCHEMA } from "@uncaged/workflow-protocol";
import { afterEach, beforeEach, describe, expect, test } from "vitest";
@@ -43,7 +43,7 @@ const DETAIL_SCHEMA = {
turnCount: { type: "integer" as const },
turns: {
type: "array" as const,
items: { type: "string" as const, format: "cas_ref" },
items: { type: "string" as const, format: "ocas_ref" },
},
},
additionalProperties: false,
@@ -1,8 +1,8 @@
import { mkdir, mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { bootstrap, putSchema } from "@uncaged/json-cas";
import { createFsStore } from "@uncaged/json-cas-fs";
import { bootstrap, putSchema } from "@ocas/core";
import { createFsStore } from "@ocas/fs";
import type { CasRef, ThreadId } from "@uncaged/workflow-protocol";
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import { cmdThreadRead } from "../commands/thread.js";
@@ -41,7 +41,7 @@ const DETAIL_SCHEMA = {
turnCount: { type: "integer" as const },
turns: {
type: "array" as const,
items: { type: "string" as const, format: "cas_ref" },
items: { type: "string" as const, format: "ocas_ref" },
},
},
additionalProperties: false,
@@ -1,8 +1,8 @@
import { mkdir, mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { bootstrap, putSchema } from "@uncaged/json-cas";
import { createFsStore } from "@uncaged/json-cas-fs";
import { bootstrap, putSchema } from "@ocas/core";
import { createFsStore } from "@ocas/fs";
import type { CasRef, ThreadId } from "@uncaged/workflow-protocol";
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import { cmdThreadRead, THREAD_READ_DEFAULT_QUOTA } from "../commands/thread.js";
@@ -42,7 +42,7 @@ const DETAIL_SCHEMA = {
turnCount: { type: "integer" as const },
turns: {
type: "array" as const,
items: { type: "string" as const, format: "cas_ref" },
items: { type: "string" as const, format: "ocas_ref" },
},
},
additionalProperties: false,
@@ -1,8 +1,8 @@
import { mkdir, mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { bootstrap, putSchema } from "@uncaged/json-cas";
import { createFsStore } from "@uncaged/json-cas-fs";
import { bootstrap, putSchema } from "@ocas/core";
import { createFsStore } from "@ocas/fs";
import type { CasRef, ThreadId } from "@uncaged/workflow-protocol";
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import { cmdStepList, cmdStepShow } from "../commands/step.js";
@@ -47,7 +47,7 @@ const DETAIL_SCHEMA = {
turnCount: { type: "integer" as const },
turns: {
type: "array" as const,
items: { type: "string" as const, format: "cas_ref" },
items: { type: "string" as const, format: "ocas_ref" },
},
},
additionalProperties: false,
@@ -1,7 +1,7 @@
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { createFsStore } from "@uncaged/json-cas-fs";
import { createFsStore } from "@ocas/fs";
import type { CasRef, WorkflowPayload } from "@uncaged/workflow-protocol";
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import { stringify } from "yaml";
+9 -5
View File
@@ -1,9 +1,9 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import type { JSONSchema, Store } from "@uncaged/json-cas";
import { bootstrap, getSchema, putSchema, refs, walk } from "@uncaged/json-cas";
import { createFsStore } from "@uncaged/json-cas-fs";
import type { JSONSchema, Store } from "@ocas/core";
import { bootstrap, getSchema, putSchema, refs, walk } from "@ocas/core";
import { createFsStore } from "@ocas/fs";
import { TEXT_SCHEMA } from "../schemas.js";
@@ -85,13 +85,17 @@ export type SchemaListEntry = {
export async function cmdCasSchemaList(storageRoot: string): Promise<SchemaListEntry[]> {
const store = openStore(storageRoot);
const metaHash = await bootstrap(store);
const aliases = await bootstrap(store);
const metaHash = aliases["@ocas/schema"];
if (metaHash === undefined) {
throw new Error("Meta-schema not found in bootstrap result");
}
const entries: SchemaListEntry[] = [];
// Include meta-schema itself
entries.push({ hash: metaHash, title: "(meta-schema)" });
for (const hash of store.listByType(metaHash)) {
for (const { hash } of store.listByType(metaHash)) {
if (hash === metaHash) continue;
const node = store.get(hash);
if (node !== null) {
+5 -5
View File
@@ -1,5 +1,5 @@
import type { Store as CasStore, JSONSchema } from "@uncaged/json-cas";
import { getSchema } from "@uncaged/json-cas";
import type { Store as CasStore, JSONSchema } from "@ocas/core";
import { getSchema } from "@ocas/core";
import type {
CasRef,
StartNodePayload,
@@ -88,7 +88,7 @@ function expandOutput(uwf: UwfStore, outputRef: CasRef): unknown {
}
/**
* Recursively expand all cas_ref fields in a CAS node's payload,
* Recursively expand all ocas_ref fields in a CAS node's payload,
* replacing hash strings with the referenced node's expanded payload.
*/
function expandDeep(store: CasStore, hash: CasRef, visited?: Set<string>): unknown {
@@ -120,7 +120,7 @@ function expandAnyOfField(
): unknown {
if (!Array.isArray(schema.anyOf)) return value;
for (const sub of schema.anyOf as JSONSchema[]) {
if (sub.format === "cas_ref" && typeof value === "string") {
if (sub.format === "ocas_ref" && typeof value === "string") {
return expandDeep(store, value as CasRef, visited);
}
}
@@ -163,7 +163,7 @@ function expandValue(
value: unknown,
visited: Set<string>,
): unknown {
if (schema.format === "cas_ref") return expandCasRefField(store, value, visited);
if (schema.format === "ocas_ref") return expandCasRefField(store, value, visited);
if (Array.isArray(schema.anyOf)) return expandAnyOfField(store, schema, value, visited);
if (schema.type === "array") return expandArrayField(store, schema, value, visited);
return expandObjectField(store, schema, value, visited);
+1 -1
View File
@@ -1,4 +1,4 @@
import type { BootstrapCapableStore } from "@uncaged/json-cas";
import type { BootstrapCapableStore } from "@ocas/core";
import type {
CasRef,
StartEntry,
+1 -1
View File
@@ -1,7 +1,7 @@
import { execFileSync, spawn } from "node:child_process";
import { access, readFile } from "node:fs/promises";
import { dirname, isAbsolute, resolve as resolvePath } from "node:path";
import { validate } from "@uncaged/json-cas";
import { validate } from "@ocas/core";
import type {
AgentAlias,
AgentConfig,
@@ -1,8 +1,8 @@
import { readFile } from "node:fs/promises";
import { dirname, resolve as resolvePath } from "node:path";
import type { JSONSchema } from "@uncaged/json-cas";
import { putSchema, validate } from "@uncaged/json-cas";
import type { JSONSchema } from "@ocas/core";
import { putSchema, validate } from "@ocas/core";
import type { CasRef, RoleDefinition, Target, WorkflowPayload } from "@uncaged/workflow-protocol";
import { parse } from "yaml";
import { createIncludeTag } from "../include.js";
+2 -2
View File
@@ -1,5 +1,5 @@
import type { Hash, Store } from "@uncaged/json-cas";
import { putSchema } from "@uncaged/json-cas";
import type { Hash, Store } from "@ocas/core";
import { putSchema } from "@ocas/core";
import { START_NODE_SCHEMA, STEP_NODE_SCHEMA, WORKFLOW_SCHEMA } from "@uncaged/workflow-protocol";
export const TEXT_SCHEMA = { type: "string" as const };
+2 -2
View File
@@ -3,8 +3,8 @@ import { access, appendFile, mkdir, readdir, readFile, writeFile } from "node:fs
import { homedir } from "node:os";
import { join } from "node:path";
import type { BootstrapCapableStore, Hash } from "@uncaged/json-cas";
import { createFsStore } from "@uncaged/json-cas-fs";
import type { BootstrapCapableStore, Hash } from "@ocas/core";
import { createFsStore } from "@ocas/fs";
import type { CasRef, ThreadId, ThreadListItem, ThreadsIndex } from "@uncaged/workflow-protocol";
import { parse, stringify } from "yaml";
+1 -1
View File
@@ -8,7 +8,7 @@ Layer 3 agent implementation. Runs an OpenAI-compatible chat completion loop wit
Useful when you want a self-contained agent without an external CLI like Hermes or Claude Code.
**Dependencies:** `@uncaged/json-cas`, `@uncaged/workflow-util-agent`, `@uncaged/workflow-util`
**Dependencies:** `@ocas/core`, `@uncaged/workflow-util-agent`, `@uncaged/workflow-util`
## Installation
+1 -1
View File
@@ -23,7 +23,7 @@
"test:ci": "vitest run"
},
"dependencies": {
"@uncaged/json-cas": "^0.5.3",
"@ocas/core": "^0.1.1",
"@uncaged/workflow-util-agent": "workspace:^",
"@uncaged/workflow-util": "workspace:^"
},
+1 -1
View File
@@ -1,4 +1,4 @@
import type { Store } from "@uncaged/json-cas";
import type { Store } from "@ocas/core";
import { createLogger, generateUlid } from "@uncaged/workflow-util";
import {
type AgentContext,
@@ -1,4 +1,4 @@
import { bootstrap, putSchema, type Store } from "@uncaged/json-cas";
import { bootstrap, putSchema, type Store } from "@ocas/core";
import { BUILTIN_DETAIL_SCHEMA, BUILTIN_TURN_SCHEMA } from "./schemas.js";
import { readSessionTurns } from "./session.js";
@@ -1,4 +1,4 @@
import type { JSONSchema } from "@uncaged/json-cas";
import type { JSONSchema } from "@ocas/core";
const BUILTIN_TOOL_CALL_SCHEMA: JSONSchema = {
type: "object",
@@ -38,7 +38,7 @@ export const BUILTIN_DETAIL_SCHEMA: JSONSchema = {
turnCount: { type: "integer" },
turns: {
type: "array",
items: { type: "string", format: "cas_ref" },
items: { type: "string", format: "ocas_ref" },
},
},
additionalProperties: false,
@@ -1,4 +1,4 @@
import type { JSONSchema } from "@uncaged/json-cas";
import type { JSONSchema } from "@ocas/core";
export type ToolContext = {
cwd: string;
@@ -6,7 +6,7 @@
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-util-agent`
**Dependencies:** `@ocas/core`, `@uncaged/workflow-util-agent`
## Installation
@@ -1,5 +1,5 @@
import { describe, expect, test } from "bun:test";
import { createMemoryStore, walk } from "@uncaged/json-cas";
import { createMemoryStore, walk } from "@ocas/core";
import {
parseClaudeCodeJsonOutput,
parseClaudeCodeStreamOutput,
@@ -23,7 +23,7 @@
"test:ci": "vitest run"
},
"dependencies": {
"@uncaged/json-cas": "^0.5.3",
"@ocas/core": "^0.1.1",
"@uncaged/workflow-util-agent": "workspace:^",
"@uncaged/workflow-util": "workspace:^"
},
@@ -1,5 +1,5 @@
import { spawn } from "node:child_process";
import type { Store } from "@uncaged/json-cas";
import type { Store } from "@ocas/core";
import { createLogger } from "@uncaged/workflow-util";
import {
type AgentContext,
@@ -1,4 +1,4 @@
import type { JSONSchema } from "@uncaged/json-cas";
import type { JSONSchema } from "@ocas/core";
export const CLAUDE_CODE_DETAIL_SCHEMA: JSONSchema = {
title: "claude-code-detail",
@@ -34,7 +34,7 @@ export const CLAUDE_CODE_DETAIL_SCHEMA: JSONSchema = {
},
turns: {
type: "array",
items: { type: "string", format: "cas_ref" },
items: { type: "string", format: "ocas_ref" },
},
},
additionalProperties: false,
@@ -1,4 +1,4 @@
import { bootstrap, putSchema, type Store } from "@uncaged/json-cas";
import { bootstrap, putSchema, type Store } from "@ocas/core";
import {
CLAUDE_CODE_DETAIL_SCHEMA,
+1 -1
View File
@@ -8,7 +8,7 @@
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-util-agent`, `@uncaged/workflow-protocol`, `@uncaged/workflow-util`
**Dependencies:** `@ocas/core`, `@uncaged/workflow-util-agent`, `@uncaged/workflow-protocol`, `@uncaged/workflow-util`
## Installation
@@ -3,7 +3,7 @@ import { describe, expect, test } from "bun:test";
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { createMemoryStore, refs, validate, walk } from "@uncaged/json-cas";
import { createMemoryStore, refs, validate, walk } from "@ocas/core";
import {
computeDurationMs,
@@ -82,7 +82,7 @@ describe("computeDurationMs", () => {
});
describe("storeHermesSessionDetail", () => {
test("stores hermes-detail root with cas_ref turns walkable", async () => {
test("stores hermes-detail root with ocas_ref turns walkable", async () => {
const session: HermesSessionJson = {
session_id: "20260518_133159_6a84e8",
model: "claude-opus-4.6",
+1 -1
View File
@@ -23,7 +23,7 @@
"test:ci": "vitest run"
},
"dependencies": {
"@uncaged/json-cas": "^0.5.3",
"@ocas/core": "^0.1.1",
"@uncaged/workflow-util-agent": "workspace:^",
"@uncaged/workflow-protocol": "workspace:^",
"@uncaged/workflow-util": "workspace:^"
+1 -1
View File
@@ -1,4 +1,4 @@
import type { Store } from "@uncaged/json-cas";
import type { Store } from "@ocas/core";
import { createLogger } from "@uncaged/workflow-util";
import {
type AgentContext,
@@ -1,4 +1,4 @@
import type { JSONSchema } from "@uncaged/json-cas";
import type { JSONSchema } from "@ocas/core";
const HERMES_TOOL_CALL_SCHEMA: JSONSchema = {
type: "object",
@@ -39,7 +39,7 @@ export const HERMES_DETAIL_SCHEMA: JSONSchema = {
turnCount: { type: "integer" },
turns: {
type: "array",
items: { type: "string", format: "cas_ref" },
items: { type: "string", format: "ocas_ref" },
},
},
additionalProperties: false,
@@ -3,7 +3,7 @@ import { readFile } from "node:fs/promises";
import { homedir } from "node:os";
import { join } from "node:path";
import { bootstrap, putSchema, type Store } from "@uncaged/json-cas";
import { bootstrap, putSchema, type Store } from "@ocas/core";
import { HERMES_DETAIL_SCHEMA, HERMES_RAW_OUTPUT_SCHEMA, HERMES_TURN_SCHEMA } from "./schemas.js";
import type {
+2 -2
View File
@@ -4,9 +4,9 @@ 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`.
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 `@ocas/core`.
**Dependencies:** `@uncaged/json-cas`, `@uncaged/json-cas-fs`
**Dependencies:** `@ocas/core`, `@ocas/fs`
## Installation
+2 -2
View File
@@ -20,8 +20,8 @@
"test:ci": "vitest run"
},
"dependencies": {
"@uncaged/json-cas": "^0.5.3",
"@uncaged/json-cas-fs": "^0.5.3"
"@ocas/core": "^0.1.1",
"@ocas/fs": "^0.1.1"
},
"devDependencies": {
"typescript": "^5.8.3"
+8 -8
View File
@@ -1,4 +1,4 @@
import type { JSONSchema } from "@uncaged/json-cas";
import type { JSONSchema } from "@ocas/core";
const ROLE_DEFINITION: JSONSchema = {
type: "object",
@@ -9,7 +9,7 @@ const ROLE_DEFINITION: JSONSchema = {
capabilities: { type: "array", items: { type: "string" } },
procedure: { type: "string" },
output: { type: "string" },
frontmatter: { type: "string", format: "cas_ref" },
frontmatter: { type: "string", format: "ocas_ref" },
},
additionalProperties: false,
};
@@ -54,7 +54,7 @@ export const START_NODE_SCHEMA: JSONSchema = {
type: "object",
required: ["workflow", "prompt", "cwd"],
properties: {
workflow: { type: "string", format: "cas_ref" },
workflow: { type: "string", format: "ocas_ref" },
prompt: { type: "string" },
cwd: { type: "string" },
},
@@ -76,20 +76,20 @@ export const STEP_NODE_SCHEMA: JSONSchema = {
"cwd",
],
properties: {
start: { type: "string", format: "cas_ref" },
start: { type: "string", format: "ocas_ref" },
prev: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
role: { type: "string" },
output: { type: "string", format: "cas_ref" },
detail: { type: "string", format: "cas_ref" },
output: { type: "string", format: "ocas_ref" },
detail: { type: "string", format: "ocas_ref" },
agent: { type: "string" },
edgePrompt: { type: "string" },
startedAtMs: { type: "integer" },
completedAtMs: { type: "integer" },
cwd: { type: "string" },
assembledPrompt: {
anyOf: [{ type: "string", format: "cas_ref" }, { type: "null" }],
anyOf: [{ type: "string", format: "ocas_ref" }, { type: "null" }],
},
},
additionalProperties: false,
+1 -1
View File
@@ -8,7 +8,7 @@ Layer 2 agent framework. Provides the standard entrypoint for all agent CLIs: pa
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`
**Dependencies:** `@ocas/core`, `@ocas/fs`, `@uncaged/workflow-protocol`, `@uncaged/workflow-util`, `dotenv`, `yaml`
## Installation
@@ -1,4 +1,4 @@
import { createMemoryStore, putSchema } from "@uncaged/json-cas";
import { createMemoryStore, putSchema } from "@ocas/core";
import { describe, expect, test } from "vitest";
import { tryFrontmatterFastPath } from "../src/frontmatter.js";
@@ -1,4 +1,4 @@
import { createMemoryStore, putSchema } from "@uncaged/json-cas";
import { createMemoryStore, putSchema } from "@ocas/core";
import { describe, expect, test } from "vitest";
import { tryFrontmatterFastPath } from "../src/frontmatter.js";
@@ -1,4 +1,4 @@
import { createMemoryStore, putSchema } from "@uncaged/json-cas";
import { createMemoryStore, putSchema } from "@ocas/core";
import { describe, expect, test } from "vitest";
import { tryFrontmatterFastPath } from "../src/frontmatter.js";
+2 -2
View File
@@ -20,8 +20,8 @@
"test:ci": "vitest run"
},
"dependencies": {
"@uncaged/json-cas": "^0.5.3",
"@uncaged/json-cas-fs": "^0.5.3",
"@ocas/core": "^0.1.1",
"@ocas/fs": "^0.1.1",
"@uncaged/workflow-protocol": "workspace:^",
"@uncaged/workflow-util": "workspace:^",
"dotenv": "^16.6.1",
@@ -1,4 +1,4 @@
import type { JSONSchema } from "@uncaged/json-cas";
import type { JSONSchema } from "@ocas/core";
type SchemaProperty = {
name: string;
+1 -1
View File
@@ -1,4 +1,4 @@
import type { Store } from "@uncaged/json-cas";
import type { Store } from "@ocas/core";
import type {
CasRef,
StartNodePayload,
+1 -1
View File
@@ -1,4 +1,4 @@
import { getSchema, validate } from "@uncaged/json-cas";
import { getSchema, validate } from "@ocas/core";
import type { CasRef, ModelAlias, WorkflowConfig } from "@uncaged/workflow-protocol";
import { createAgentStore, resolveStorageRoot } from "./storage.js";
@@ -1,5 +1,5 @@
import type { Store } from "@uncaged/json-cas";
import { getSchema, validate } from "@uncaged/json-cas";
import type { Store } from "@ocas/core";
import { getSchema, validate } from "@ocas/core";
import type { CasRef } from "@uncaged/workflow-protocol";
import {
type AgentFrontmatter,
+1 -1
View File
@@ -1,4 +1,4 @@
import { getSchema, validate } from "@uncaged/json-cas";
import { getSchema, validate } from "@ocas/core";
import type { CasRef, StepNodePayload, ThreadId } from "@uncaged/workflow-protocol";
import { config as loadDotenv } from "dotenv";
import { buildOutputFormatInstruction } from "./build-output-format-instruction.js";
+2 -2
View File
@@ -1,5 +1,5 @@
import type { Hash, Store } from "@uncaged/json-cas";
import { putSchema } from "@uncaged/json-cas";
import type { Hash, Store } from "@ocas/core";
import { putSchema } from "@ocas/core";
import { START_NODE_SCHEMA, STEP_NODE_SCHEMA, WORKFLOW_SCHEMA } from "@uncaged/workflow-protocol";
export type UwfAgentSchemaHashes = {
+2 -2
View File
@@ -2,8 +2,8 @@ import { readFile } from "node:fs/promises";
import { homedir } from "node:os";
import { join } from "node:path";
import type { Store } from "@uncaged/json-cas";
import { createFsStore } from "@uncaged/json-cas-fs";
import type { Store } from "@ocas/core";
import { createFsStore } from "@ocas/fs";
import type {
AgentAlias,
AgentConfig,
+1 -1
View File
@@ -1,4 +1,4 @@
import type { Store } from "@uncaged/json-cas";
import type { Store } from "@ocas/core";
import type { ModeratorContext, ThreadId, WorkflowPayload } from "@uncaged/workflow-protocol";
export type AgentContext = ModeratorContext & {
@@ -20,7 +20,7 @@ Dependency layers (each only imports from packages above it):
protocol util util-agent agent-hermes / agent-builtin / cli-workflow
\`\`\`
External CAS: \`@uncaged/json-cas\` (store API, hashing, schema validation) + \`@uncaged/json-cas-fs\` (filesystem backend).
External CAS: \`@ocas/core\` (store API, hashing, schema validation) + \`@ocas/fs\` (filesystem backend).
## Coding Conventions
@@ -122,7 +122,7 @@ Shared entry point for all agent CLIs. Handles:
### CAS Integration
All data is CAS-addressed via \`@uncaged/json-cas\`:
All data is CAS-addressed via \`@ocas/core\`:
- \`store.put(schemaHash, data)\` → content hash
- \`store.get(hash)\` → node
- \`validate(store, node)\` → schema check
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bun
// Mock agent for smoke testing
import { bootstrap, type JSONSchema, putSchema } from "@uncaged/json-cas";
import { bootstrap, type JSONSchema, putSchema } from "@ocas/core";
import { createAgent } from "../packages/uwf-agent-kit/src/index.js";
const MOCK_RAW_OUTPUT_SCHEMA: JSONSchema = {