RFC: Migrate workflow CAS to json-cas with full-closure schema design #294

Closed
opened 2026-05-17 15:57:16 +00:00 by xiaoju · 2 comments
Owner

Summary

Replace @uncaged/workflow-cas with @uncaged/json-cas and redesign the CAS schema to achieve full-closure: workflow definitions, agent instances, thread execution, and ReAct-level traces all live in CAS as self-describing typed nodes.

Key Design Decisions

  1. Workflow = pure JSON in CAS — no more ESM bundles. Moderator conditions use JSONata expressions.
  2. Agent instance = CAS node — npm package + config. Reusable across threads.
  3. Binding at thread level — workflow has no agent opinion; thread-start maps role → agent instance.
  4. React layer — full ReAct loop trace (session → turns → tool calls) stored in CAS.
  5. No GC — immutable history, what happened happened.
  6. No backward compatibility — clean break from YAML-based CAS.

Schema Design (11 types, 4 layers)

Layer 1: Definition (immutable templates)

agent

Agent instance — a specific package + config combination. Reusable across threads.

{
  "package": "@uncaged/workflow-agent-cursor",
  "version": "^1.0.0",
  "config": {
    "model": "claude-4.6-opus-high-thinking",
    "mode": "write"
  }
}
  • package: npm package name. Convention: @uncaged/workflow-agent-* or any scope.
  • version: semver range, resolved at runtime.
  • config: agent-specific parameters. Schema defined by the package's configSchema export.

npm package contract:

// Every agent package exports:
export const packageDescriptor: PackageDescriptor = {
  name: "cursor",
  capabilities: ["code-edit", "file-read", "terminal"],
  configSchema: { /* JSON Schema for config */ },
};
export const createAgent: (config: Config) => AgentFn;

role-schema

JSON Schema for a role's extracted meta output. Stored separately for reuse across roles/workflows.

{
  "name": "coder-meta",
  "type": "object",
  "properties": {
    "completedPhase": { "type": "string", "format": "cas_ref" },
    "status": { "type": "string", "enum": ["completed", "failed"] }
  },
  "required": ["completedPhase", "status"]
}

role

Role definition — pure data, no agent binding.

{
  "name": "coder",
  "description": "Implement code changes according to the plan",
  "systemPrompt": "You are a senior developer...",
  "extractPrompt": "Extract the following from the agent output...",
  "schema": "<role-schema-hash>"
}

Fields:

  • schema: cas_refrole-schema node

workflow

Workflow definition — roles + JSONata moderator. No agent opinions.

{
  "name": "develop",
  "description": "Plan, implement, review, test, commit",
  "roles": {
    "planner": "<role-hash>",
    "coder": "<role-hash>",
    "reviewer": "<role-hash>",
    "tester": "<role-hash>",
    "committer": "<role-hash>"
  },
  "moderator": [
    { "from": "__start__", "to": "planner", "when": null },
    { "from": "planner",   "to": "__end__", "when": "$steps[role='planner'].meta.status = 'aborted'" },
    { "from": "planner",   "to": "coder",   "when": null },
    { "from": "coder",     "to": "reviewer","when": "$count($filter(steps[role='planner'].meta.phases, function($p) { $p.hash in steps[role='coder'].meta.completedPhase })) >= $count(steps[role='planner'].meta.phases)" },
    { "from": "coder",     "to": "coder",   "when": null },
    { "from": "reviewer",  "to": "tester",  "when": "$steps[-1].meta.status = 'approved'" },
    { "from": "reviewer",  "to": "coder",   "when": null },
    { "from": "tester",    "to": "committer","when": "$steps[-1].meta.status = 'passed'" },
    { "from": "tester",    "to": "coder",   "when": null },
    { "from": "committer", "to": "__end__", "when": null }
  ]
}

Fields:

  • roles: Record<string, cas_ref> → each value is a role node hash
  • moderator: ordered transition rules. First matching when (JSONata, evaluated against thread context) wins. null = FALLBACK (always matches).
  • __start__ and __end__ are reserved names.

Layer 2: Execution (per-thread)

thread-start

Created when a thread begins. Binds roles to agent instances.

{
  "workflow": "<workflow-hash>",
  "input": "Fix issue #42 — login redirect loop",
  "depth": 0,
  "parentThread": null,
  "agents": {
    "planner": "<agent-instance-hash>",
    "coder": "<agent-instance-hash>",
    "reviewer": "<agent-instance-hash>",
    "tester": "<agent-instance-hash>",
    "committer": "<agent-instance-hash>"
  }
}

Fields:

  • workflow: cas_refworkflow node
  • parentThread: cas_ref | null → parent thread-start for sub-workflows
  • agents: Record<string, cas_ref> → maps role name to agent instance node

thread-step

One completed role execution.

{
  "role": "coder",
  "meta": { "completedPhase": "ABC123", "status": "completed" },
  "content": "<content-hash>",
  "react": "<react-session-hash>",
  "start": "<thread-start-hash>",
  "previous": "<previous-step-hash>"
}

Fields:

  • content: cas_refcontent node (final agent output)
  • react: cas_refreact-session node (execution trace)
  • start: cas_refthread-start node
  • previous: cas_ref | null → previous thread-step (linked list)

thread-end

Thread completion record.

{
  "returnCode": 0,
  "summary": "Successfully fixed the login redirect loop",
  "start": "<thread-start-hash>",
  "lastStep": "<last-step-hash>"
}

Fields:

  • start: cas_refthread-start
  • lastStep: cas_ref → final thread-step

content

Generic text/data carrier. Used for agent output, prompts, tool args/results.

{
  "text": "Here is the implementation..."
}

Layer 3: React (ReAct execution trace)

react-session

A complete ReAct execution for one role step.

{
  "agent": "<agent-instance-hash>",
  "role": "coder",
  "turns": ["<turn-hash-1>", "<turn-hash-2>"],
  "totalTokens": 12580,
  "durationMs": 45000
}

Fields:

  • agent: cas_refagent instance
  • turns: cas_ref[] → ordered react-turn nodes

react-turn

One LLM call within a ReAct session.

{
  "input": "<content-hash>",
  "output": "<content-hash>",
  "toolCalls": ["<tool-call-hash-1>", "<tool-call-hash-2>"],
  "tokens": { "input": 3200, "output": 850 },
  "latencyMs": 2300
}

Fields:

  • input: cas_ref → prompt content
  • output: cas_ref → response content
  • toolCalls: cas_ref[]react-tool-call nodes

react-tool-call

One tool invocation.

{
  "name": "read_file",
  "arguments": "<content-hash>",
  "result": "<content-hash>",
  "durationMs": 120
}

Fields:

  • arguments: cas_refcontent node
  • result: cas_refcontent node

Traversal Example

From any thread-step, you can reconstruct everything:

thread-step
  → start → thread-start
               → workflow → roles → role → role-schema
               → agents → agent (package + config)
  → content (final output)
  → react → react-session
              → turns → react-turn
                          → input/output (prompts/responses)
                          → toolCalls → react-tool-call
                                         → arguments/result
  → previous → thread-step (chain back to start)

Migration Path

  1. Add @uncaged/json-cas + @uncaged/json-cas-fs as dependencies to workflow monorepo
  2. Define all 11 schemas as JSON Schema files, register them via putSchema
  3. Replace workflow-cas usage in workflow-execute with json-cas Store
  4. Convert workflow-template-* moderator tables to JSONata-based workflow JSON
  5. Update engine to evaluate JSONata for moderator decisions
  6. Add react-layer instrumentation to the reactor
  7. Remove old packages: workflow-cas, bundle build pipeline
  8. Update CLI commands for the new model

npm Package Convention for Agents

@uncaged/workflow-agent-cursor    — Cursor Agent
@uncaged/workflow-agent-hermes    — Hermes Agent
@uncaged/workflow-agent-llm       — Direct LLM Agent
@uncaged/workflow-agent-react     — ReAct Agent

// Third-party agents:
@myorg/workflow-agent-custom      — any scope works

Each package exports packageDescriptor and createAgent.

小橘 🍊(NEKO Team)

## Summary Replace `@uncaged/workflow-cas` with `@uncaged/json-cas` and redesign the CAS schema to achieve full-closure: workflow definitions, agent instances, thread execution, and ReAct-level traces all live in CAS as self-describing typed nodes. ## Key Design Decisions 1. **Workflow = pure JSON in CAS** — no more ESM bundles. Moderator conditions use JSONata expressions. 2. **Agent instance = CAS node** — npm package + config. Reusable across threads. 3. **Binding at thread level** — workflow has no agent opinion; thread-start maps role → agent instance. 4. **React layer** — full ReAct loop trace (session → turns → tool calls) stored in CAS. 5. **No GC** — immutable history, what happened happened. 6. **No backward compatibility** — clean break from YAML-based CAS. ## Schema Design (11 types, 4 layers) ### Layer 1: Definition (immutable templates) #### `agent` Agent instance — a specific package + config combination. Reusable across threads. ```json { "package": "@uncaged/workflow-agent-cursor", "version": "^1.0.0", "config": { "model": "claude-4.6-opus-high-thinking", "mode": "write" } } ``` - `package`: npm package name. Convention: `@uncaged/workflow-agent-*` or any scope. - `version`: semver range, resolved at runtime. - `config`: agent-specific parameters. Schema defined by the package's `configSchema` export. npm package contract: ```typescript // Every agent package exports: export const packageDescriptor: PackageDescriptor = { name: "cursor", capabilities: ["code-edit", "file-read", "terminal"], configSchema: { /* JSON Schema for config */ }, }; export const createAgent: (config: Config) => AgentFn; ``` #### `role-schema` JSON Schema for a role's extracted meta output. Stored separately for reuse across roles/workflows. ```json { "name": "coder-meta", "type": "object", "properties": { "completedPhase": { "type": "string", "format": "cas_ref" }, "status": { "type": "string", "enum": ["completed", "failed"] } }, "required": ["completedPhase", "status"] } ``` #### `role` Role definition — pure data, no agent binding. ```json { "name": "coder", "description": "Implement code changes according to the plan", "systemPrompt": "You are a senior developer...", "extractPrompt": "Extract the following from the agent output...", "schema": "<role-schema-hash>" } ``` Fields: - `schema`: `cas_ref` → `role-schema` node #### `workflow` Workflow definition — roles + JSONata moderator. No agent opinions. ```json { "name": "develop", "description": "Plan, implement, review, test, commit", "roles": { "planner": "<role-hash>", "coder": "<role-hash>", "reviewer": "<role-hash>", "tester": "<role-hash>", "committer": "<role-hash>" }, "moderator": [ { "from": "__start__", "to": "planner", "when": null }, { "from": "planner", "to": "__end__", "when": "$steps[role='planner'].meta.status = 'aborted'" }, { "from": "planner", "to": "coder", "when": null }, { "from": "coder", "to": "reviewer","when": "$count($filter(steps[role='planner'].meta.phases, function($p) { $p.hash in steps[role='coder'].meta.completedPhase })) >= $count(steps[role='planner'].meta.phases)" }, { "from": "coder", "to": "coder", "when": null }, { "from": "reviewer", "to": "tester", "when": "$steps[-1].meta.status = 'approved'" }, { "from": "reviewer", "to": "coder", "when": null }, { "from": "tester", "to": "committer","when": "$steps[-1].meta.status = 'passed'" }, { "from": "tester", "to": "coder", "when": null }, { "from": "committer", "to": "__end__", "when": null } ] } ``` Fields: - `roles`: `Record<string, cas_ref>` → each value is a `role` node hash - `moderator`: ordered transition rules. First matching `when` (JSONata, evaluated against thread context) wins. `null` = FALLBACK (always matches). - `__start__` and `__end__` are reserved names. ### Layer 2: Execution (per-thread) #### `thread-start` Created when a thread begins. Binds roles to agent instances. ```json { "workflow": "<workflow-hash>", "input": "Fix issue #42 — login redirect loop", "depth": 0, "parentThread": null, "agents": { "planner": "<agent-instance-hash>", "coder": "<agent-instance-hash>", "reviewer": "<agent-instance-hash>", "tester": "<agent-instance-hash>", "committer": "<agent-instance-hash>" } } ``` Fields: - `workflow`: `cas_ref` → `workflow` node - `parentThread`: `cas_ref | null` → parent `thread-start` for sub-workflows - `agents`: `Record<string, cas_ref>` → maps role name to `agent` instance node #### `thread-step` One completed role execution. ```json { "role": "coder", "meta": { "completedPhase": "ABC123", "status": "completed" }, "content": "<content-hash>", "react": "<react-session-hash>", "start": "<thread-start-hash>", "previous": "<previous-step-hash>" } ``` Fields: - `content`: `cas_ref` → `content` node (final agent output) - `react`: `cas_ref` → `react-session` node (execution trace) - `start`: `cas_ref` → `thread-start` node - `previous`: `cas_ref | null` → previous `thread-step` (linked list) #### `thread-end` Thread completion record. ```json { "returnCode": 0, "summary": "Successfully fixed the login redirect loop", "start": "<thread-start-hash>", "lastStep": "<last-step-hash>" } ``` Fields: - `start`: `cas_ref` → `thread-start` - `lastStep`: `cas_ref` → final `thread-step` #### `content` Generic text/data carrier. Used for agent output, prompts, tool args/results. ```json { "text": "Here is the implementation..." } ``` ### Layer 3: React (ReAct execution trace) #### `react-session` A complete ReAct execution for one role step. ```json { "agent": "<agent-instance-hash>", "role": "coder", "turns": ["<turn-hash-1>", "<turn-hash-2>"], "totalTokens": 12580, "durationMs": 45000 } ``` Fields: - `agent`: `cas_ref` → `agent` instance - `turns`: `cas_ref[]` → ordered `react-turn` nodes #### `react-turn` One LLM call within a ReAct session. ```json { "input": "<content-hash>", "output": "<content-hash>", "toolCalls": ["<tool-call-hash-1>", "<tool-call-hash-2>"], "tokens": { "input": 3200, "output": 850 }, "latencyMs": 2300 } ``` Fields: - `input`: `cas_ref` → prompt `content` - `output`: `cas_ref` → response `content` - `toolCalls`: `cas_ref[]` → `react-tool-call` nodes #### `react-tool-call` One tool invocation. ```json { "name": "read_file", "arguments": "<content-hash>", "result": "<content-hash>", "durationMs": 120 } ``` Fields: - `arguments`: `cas_ref` → `content` node - `result`: `cas_ref` → `content` node ## Traversal Example From any `thread-step`, you can reconstruct everything: ``` thread-step → start → thread-start → workflow → roles → role → role-schema → agents → agent (package + config) → content (final output) → react → react-session → turns → react-turn → input/output (prompts/responses) → toolCalls → react-tool-call → arguments/result → previous → thread-step (chain back to start) ``` ## Migration Path 1. Add `@uncaged/json-cas` + `@uncaged/json-cas-fs` as dependencies to workflow monorepo 2. Define all 11 schemas as JSON Schema files, register them via `putSchema` 3. Replace `workflow-cas` usage in `workflow-execute` with `json-cas` Store 4. Convert `workflow-template-*` moderator tables to JSONata-based workflow JSON 5. Update engine to evaluate JSONata for moderator decisions 6. Add react-layer instrumentation to the reactor 7. Remove old packages: `workflow-cas`, bundle build pipeline 8. Update CLI commands for the new model ## npm Package Convention for Agents ``` @uncaged/workflow-agent-cursor — Cursor Agent @uncaged/workflow-agent-hermes — Hermes Agent @uncaged/workflow-agent-llm — Direct LLM Agent @uncaged/workflow-agent-react — ReAct Agent // Third-party agents: @myorg/workflow-agent-custom — any scope works ``` Each package exports `packageDescriptor` and `createAgent`. 小橘 🍊(NEKO Team)
Author
Owner

Phase 拆分

基于依赖分析,从底向上迁移。每个 phase 可独立验证。


Phase 1: json-cas schema 注册

目标:在 json-cas 中注册全部 11 个 schema,验证 schema 体系自洽。

工作内容

  • json-cas repo 新建 packages/json-cas-workflow/@uncaged/json-cas-workflow
  • 编写 11 个 JSON Schema 文件(agent, role-schema, role, workflow, thread-start, thread-step, thread-end, content, react-session, react-turn, react-tool-call)
  • 导出 registerWorkflowSchemas(store) → 注册所有 schema,返回 type hash map
  • 导出每个 schema 的类型定义(TypeScript type)

验证步骤

  • bun test — 11 个 schema 全部能 putSchema,返回 hash
  • 用每个 schema 的 type hash put 一个示例节点,validate 通过
  • 故意传错误 payload,validate 失败
  • cas_ref 字段能被 refs() 正确提取
  • walk() 能沿 cas_ref 遍历完整链(thread-step → content → react-session → turns → tool-calls)
  • Schema 间的 cas_ref 引用构成合法 DAG(无循环依赖)

Phase 2: JSONata moderator engine

目标:实现基于 JSONata 的 moderator 求值器,替代 JS 函数。

工作内容

  • workflow repo 新建或修改 workflow-protocol 中的 moderator 逻辑
  • 实现 evaluateModerator(rules, context) → role name | END
  • rules 格式:[{ from, to, when: JSONata | null }]
  • context:{ steps, start, threadId, ... }(与现有 ModeratorContext 兼容)

验证步骤

  • solve-issue 的线性流程用 JSONata 表达,evaluateModerator 正确路由 __start__ → preparer → developer → submitter → __end__
  • develop 的条件分支用 JSONata 表达:
    • plannerAborted: $steps[role='planner'].meta.status = 'aborted' → END
    • allPhasesComplete: phases 比对逻辑 → reviewer
    • reviewApproved: $steps[-1].meta.status = 'approved' → tester
    • testsPassed: $steps[-1].meta.status = 'passed' → committer
  • FALLBACK(when: null)行为正确
  • 无匹配规则 → END
  • 不合法的 JSONata 表达式 → 明确错误

Phase 3: Workflow 定义 JSON 化

目标:将现有的 TypeScript workflow template 转为纯 JSON workflow 定义,存入 CAS。

工作内容

  • workflow-template-solve-issueworkflow-template-develop 转为 JSON
  • 每个 role 的 systemPrompt、extractPrompt、schema 提取为独立 CAS 节点
  • Workflow JSON 引用 role hash,moderator 用 JSONata
  • 实现 loadWorkflow(store, workflowHash) → 从 CAS 加载完整 workflow 定义
  • 实现 registerWorkflow(store, workflowJson) → 递归存入所有子节点(role-schema, role, workflow),返回 workflow hash

验证步骤

  • solve-issue workflow JSON 能 registerWorkflow,返回 hash
  • develop workflow JSON 能 registerWorkflow,返回 hash
  • loadWorkflow 从 hash 还原完整 workflow(含所有 role 定义和 schema)
  • 两次注册同一 workflow → 幂等,相同 hash
  • walk(workflowHash) 遍历所有 role 和 role-schema 节点

Phase 4: Engine 迁移(CAS 替换)

目标:将 engine 从 workflow-cas 切换到 json-cas,thread 执行使用新 schema。

工作内容

  • workflow-execute 中将 CasStore 类型替换为 json-casStore
  • Engine 启动时:从 CAS 加载 workflow 定义(而非从 bundle import)
  • Engine 写入 thread-start(含 workflow hash + agents binding)
  • 每个 step 写入 thread-step(含 content hash + react session hash)
  • 结束时写入 thread-end
  • 删除所有 YAML merkle/nodes 相关代码
  • 删除 putStartNode, putStateNode, putContentNodeWithRefs 等旧函数

验证步骤

  • 用 memory store 跑 solve-issue workflow 端到端
  • thread-start 节点包含 workflow ref 和 agents binding
  • 每个 thread-step 有 content ref 和 react ref
  • thread-end 记录 returnCode 和 summary
  • walk(threadStartHash) 能遍历整个 thread 的所有节点
  • get(stepHash) → get(reactHash) → get(turnHashes) 链路完整
  • fs store 持久化后重新加载可正常读取

Phase 5: React 层 instrumentation

目标:Reactor 执行过程记录为 react-session/turn/tool-call 节点。

工作内容

  • 修改 workflow-reactor 中的 createThreadReactor
  • 每轮 LLM 调用存为 react-turn(prompt/response 存为 content 节点)
  • 每次 tool call 存为 react-tool-call(args/result 存为 content 节点)
  • 最终组装 react-session 节点
  • thread-step 的 react 字段引用 session hash

验证步骤

  • 跑一个含 tool call 的 workflow step
  • react-session 节点存在,包含正确的 agent ref 和 turn 列表
  • 每个 react-turn 有 input/output content ref
  • 每个 react-tool-call 有 arguments/result content ref
  • tokens 和 duration 数值合理
  • walk(reactSessionHash) 遍历所有 turns 和 tool-calls
  • 从 thread-step 出发,能完整重建整个 ReAct 执行过程

Phase 6: Agent 实例模型 + npm 约定

目标:定义 agent instance CAS 节点,重构 agent 包导出约定。

工作内容

  • 定义 PackageDescriptor type(name, capabilities, configSchema)
  • 各 agent 包(cursor, hermes, llm, react)添加 packageDescriptor 导出
  • 各 agent 包添加 createAgent(config) 标准工厂导出
  • Agent 实例作为 CAS 节点存储(package + version + config)
  • thread-start 的 agents 字段引用 agent 实例 hash

验证步骤

  • 每个 agent 包导出 packageDescriptorcreateAgent
  • packageDescriptor.configSchema 是合法 JSON Schema
  • agent 实例 CAS 节点能 put + validate
  • 同一 package + config → 幂等,相同 hash
  • 不同 config → 不同 hash
  • thread-start 的 agents 引用 agent 实例,refs() 正确提取

Phase 7: CLI 适配 + 清理

目标:更新 CLI 命令,删除旧代码。

工作内容

  • cli-workflow 中替换所有 workflow-cas 用法
  • CAS 子命令改用 json-cas-fs 的 store
  • Thread 子命令适配新的 thread-start/step/end 结构
  • Bundle 相关命令移除(不再有 .esm.js)
  • Workflow 子命令改为从 CAS 读取 JSON workflow
  • 删除 workflow-cas
  • 删除 bundle 构建管线(workflow-register 中的 bundle 相关)
  • 更新 workflow.yaml registry 指向 CAS hash(而非 bundle hash)

验证步骤

  • uncaged-workflow cas list/get/put 使用 json-cas store
  • uncaged-workflow thread show 正确展示新格式的 thread
  • uncaged-workflow workflow list/add 操作 CAS 中的 workflow JSON
  • 无任何代码 import @uncaged/workflow-cas
  • bun test 全部通过
  • biome check 无 error

Phase 依赖图

Phase 1 (json-cas schemas)
  ↓
Phase 2 (JSONata moderator) ──── 独立,可与 Phase 1 并行
  ↓
Phase 3 (Workflow JSON 化) ←── 依赖 Phase 1 + 2
  ↓
Phase 4 (Engine 迁移) ←── 依赖 Phase 3
  ↓
Phase 5 (React 层) ←── 依赖 Phase 4
  ↓
Phase 6 (Agent 模型) ←── 可与 Phase 5 并行,依赖 Phase 4
  ↓
Phase 7 (CLI + 清理) ←── 依赖 Phase 4-6 全部完成

完成标准

  • Phase 1: Schema 注册
  • Phase 2: JSONata moderator
  • Phase 3: Workflow JSON 化
  • Phase 4: Engine 迁移
  • Phase 5: React 层
  • Phase 6: Agent 实例模型
  • Phase 7: CLI + 清理

小橘 🍊(NEKO Team)

## Phase 拆分 基于依赖分析,从底向上迁移。每个 phase 可独立验证。 --- ### Phase 1: json-cas schema 注册 **目标**:在 json-cas 中注册全部 11 个 schema,验证 schema 体系自洽。 **工作内容**: - 在 `json-cas` repo 新建 `packages/json-cas-workflow/` → `@uncaged/json-cas-workflow` - 编写 11 个 JSON Schema 文件(agent, role-schema, role, workflow, thread-start, thread-step, thread-end, content, react-session, react-turn, react-tool-call) - 导出 `registerWorkflowSchemas(store)` → 注册所有 schema,返回 type hash map - 导出每个 schema 的类型定义(TypeScript type) **验证步骤**: - [ ] `bun test` — 11 个 schema 全部能 `putSchema`,返回 hash - [ ] 用每个 schema 的 type hash `put` 一个示例节点,`validate` 通过 - [ ] 故意传错误 payload,`validate` 失败 - [ ] `cas_ref` 字段能被 `refs()` 正确提取 - [ ] `walk()` 能沿 cas_ref 遍历完整链(thread-step → content → react-session → turns → tool-calls) - [ ] Schema 间的 cas_ref 引用构成合法 DAG(无循环依赖) --- ### Phase 2: JSONata moderator engine **目标**:实现基于 JSONata 的 moderator 求值器,替代 JS 函数。 **工作内容**: - 在 `workflow` repo 新建或修改 `workflow-protocol` 中的 moderator 逻辑 - 实现 `evaluateModerator(rules, context)` → role name | END - rules 格式:`[{ from, to, when: JSONata | null }]` - context:`{ steps, start, threadId, ... }`(与现有 `ModeratorContext` 兼容) **验证步骤**: - [ ] solve-issue 的线性流程用 JSONata 表达,`evaluateModerator` 正确路由 `__start__ → preparer → developer → submitter → __end__` - [ ] develop 的条件分支用 JSONata 表达: - `plannerAborted`: `$steps[role='planner'].meta.status = 'aborted'` → END - `allPhasesComplete`: phases 比对逻辑 → reviewer - `reviewApproved`: `$steps[-1].meta.status = 'approved'` → tester - `testsPassed`: `$steps[-1].meta.status = 'passed'` → committer - [ ] FALLBACK(`when: null`)行为正确 - [ ] 无匹配规则 → END - [ ] 不合法的 JSONata 表达式 → 明确错误 --- ### Phase 3: Workflow 定义 JSON 化 **目标**:将现有的 TypeScript workflow template 转为纯 JSON workflow 定义,存入 CAS。 **工作内容**: - 将 `workflow-template-solve-issue` 和 `workflow-template-develop` 转为 JSON - 每个 role 的 systemPrompt、extractPrompt、schema 提取为独立 CAS 节点 - Workflow JSON 引用 role hash,moderator 用 JSONata - 实现 `loadWorkflow(store, workflowHash)` → 从 CAS 加载完整 workflow 定义 - 实现 `registerWorkflow(store, workflowJson)` → 递归存入所有子节点(role-schema, role, workflow),返回 workflow hash **验证步骤**: - [ ] solve-issue workflow JSON 能 `registerWorkflow`,返回 hash - [ ] develop workflow JSON 能 `registerWorkflow`,返回 hash - [ ] `loadWorkflow` 从 hash 还原完整 workflow(含所有 role 定义和 schema) - [ ] 两次注册同一 workflow → 幂等,相同 hash - [ ] `walk(workflowHash)` 遍历所有 role 和 role-schema 节点 --- ### Phase 4: Engine 迁移(CAS 替换) **目标**:将 engine 从 `workflow-cas` 切换到 `json-cas`,thread 执行使用新 schema。 **工作内容**: - `workflow-execute` 中将 `CasStore` 类型替换为 `json-cas` 的 `Store` - Engine 启动时:从 CAS 加载 workflow 定义(而非从 bundle import) - Engine 写入 `thread-start`(含 workflow hash + agents binding) - 每个 step 写入 `thread-step`(含 content hash + react session hash) - 结束时写入 `thread-end` - 删除所有 YAML merkle/nodes 相关代码 - 删除 `putStartNode`, `putStateNode`, `putContentNodeWithRefs` 等旧函数 **验证步骤**: - [ ] 用 memory store 跑 solve-issue workflow 端到端 - [ ] thread-start 节点包含 workflow ref 和 agents binding - [ ] 每个 thread-step 有 content ref 和 react ref - [ ] thread-end 记录 returnCode 和 summary - [ ] `walk(threadStartHash)` 能遍历整个 thread 的所有节点 - [ ] `get(stepHash) → get(reactHash) → get(turnHashes)` 链路完整 - [ ] fs store 持久化后重新加载可正常读取 --- ### Phase 5: React 层 instrumentation **目标**:Reactor 执行过程记录为 react-session/turn/tool-call 节点。 **工作内容**: - 修改 `workflow-reactor` 中的 `createThreadReactor` - 每轮 LLM 调用存为 `react-turn`(prompt/response 存为 content 节点) - 每次 tool call 存为 `react-tool-call`(args/result 存为 content 节点) - 最终组装 `react-session` 节点 - thread-step 的 `react` 字段引用 session hash **验证步骤**: - [ ] 跑一个含 tool call 的 workflow step - [ ] react-session 节点存在,包含正确的 agent ref 和 turn 列表 - [ ] 每个 react-turn 有 input/output content ref - [ ] 每个 react-tool-call 有 arguments/result content ref - [ ] tokens 和 duration 数值合理 - [ ] `walk(reactSessionHash)` 遍历所有 turns 和 tool-calls - [ ] 从 thread-step 出发,能完整重建整个 ReAct 执行过程 --- ### Phase 6: Agent 实例模型 + npm 约定 **目标**:定义 agent instance CAS 节点,重构 agent 包导出约定。 **工作内容**: - 定义 `PackageDescriptor` type(name, capabilities, configSchema) - 各 agent 包(cursor, hermes, llm, react)添加 `packageDescriptor` 导出 - 各 agent 包添加 `createAgent(config)` 标准工厂导出 - Agent 实例作为 CAS 节点存储(package + version + config) - thread-start 的 agents 字段引用 agent 实例 hash **验证步骤**: - [ ] 每个 agent 包导出 `packageDescriptor` 和 `createAgent` - [ ] `packageDescriptor.configSchema` 是合法 JSON Schema - [ ] agent 实例 CAS 节点能 put + validate - [ ] 同一 package + config → 幂等,相同 hash - [ ] 不同 config → 不同 hash - [ ] thread-start 的 agents 引用 agent 实例,`refs()` 正确提取 --- ### Phase 7: CLI 适配 + 清理 **目标**:更新 CLI 命令,删除旧代码。 **工作内容**: - `cli-workflow` 中替换所有 `workflow-cas` 用法 - CAS 子命令改用 `json-cas-fs` 的 store - Thread 子命令适配新的 thread-start/step/end 结构 - Bundle 相关命令移除(不再有 .esm.js) - Workflow 子命令改为从 CAS 读取 JSON workflow - 删除 `workflow-cas` 包 - 删除 bundle 构建管线(`workflow-register` 中的 bundle 相关) - 更新 `workflow.yaml` registry 指向 CAS hash(而非 bundle hash) **验证步骤**: - [ ] `uncaged-workflow cas list/get/put` 使用 json-cas store - [ ] `uncaged-workflow thread show` 正确展示新格式的 thread - [ ] `uncaged-workflow workflow list/add` 操作 CAS 中的 workflow JSON - [ ] 无任何代码 import `@uncaged/workflow-cas` - [ ] `bun test` 全部通过 - [ ] biome check 无 error --- ## Phase 依赖图 ``` Phase 1 (json-cas schemas) ↓ Phase 2 (JSONata moderator) ──── 独立,可与 Phase 1 并行 ↓ Phase 3 (Workflow JSON 化) ←── 依赖 Phase 1 + 2 ↓ Phase 4 (Engine 迁移) ←── 依赖 Phase 3 ↓ Phase 5 (React 层) ←── 依赖 Phase 4 ↓ Phase 6 (Agent 模型) ←── 可与 Phase 5 并行,依赖 Phase 4 ↓ Phase 7 (CLI + 清理) ←── 依赖 Phase 4-6 全部完成 ``` ## 完成标准 - [ ] Phase 1: Schema 注册 - [ ] Phase 2: JSONata moderator - [ ] Phase 3: Workflow JSON 化 - [ ] Phase 4: Engine 迁移 - [ ] Phase 5: React 层 - [ ] Phase 6: Agent 实例模型 - [ ] Phase 7: CLI + 清理 小橘 🍊(NEKO Team)
Author
Owner

Closing: Migration to json-cas completed

— 小橘 🍊(NEKO Team)

Closing: Migration to json-cas completed — 小橘 🍊(NEKO Team)
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: uncaged/workflow#294