From 18dff2fbf54355a2d5d915877afe41d03e155d64 Mon Sep 17 00:00:00 2001 From: Yilin Jing Date: Wed, 18 Mar 2026 15:24:21 +0800 Subject: [PATCH 1/7] chore: add autoresearch harness --- autoresearch.md | 35 +++++++++++++++++++++++++++++++++++ autoresearch.sh | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 autoresearch.md create mode 100755 autoresearch.sh diff --git a/autoresearch.md b/autoresearch.md new file mode 100644 index 0000000..5f8944f --- /dev/null +++ b/autoresearch.md @@ -0,0 +1,35 @@ +# Autoresearch: Distributed World Authoring + +## Objective +Make it dramatically easier to author and run distributed agent worlds by: +- Extending the DAP protocol / `@resciencelab/agent-world-sdk` manifests to cover programmatic & hosted worlds +- Updating the built-in `world/` template and the standalone `pokemon-world` example to follow the new manifest contract +- Ensuring the OpenClaw plugin, SDK, and example worlds stay fully buildable and testable while these capabilities evolve + +## Metrics +- **Primary**: `total_ms` (milliseconds, lower is better) — wall-clock time to build DAP, run the test suite, and syntax-check the Pokemon world +- **Secondary**: `dap_build_ms`, `dap_tests_ms`, `pokemon_check_ms` + +## How to Run +`./autoresearch.sh` — prints `METRIC name=value` lines for each step and exits non-zero on failure. + +## Files in Scope +- `packages/agent-world-sdk/**` — SDK types, peer protocol helpers, manifest + world server logic +- `world/**` — default world template, docs, helper scripts +- `docs/**` (especially `docs/WORLD_MANIFEST.md`) — published protocol documentation +- `src/**` (where SDK integration into the DAP plugin lives) when needed for end-to-end behavior +- `/Users/yilin/Developer/ReScienceLab/pokemon-world/**` — standalone example world that must reflect the new authoring flow + +## Off Limits +- `dist/**`, `node_modules/**`, build artifacts (regenerate instead of editing) +- Secrets / identity material under `~/.openclaw/**` +- Bootstrap node deployment scripts (only touch when explicitly needed) + +## Constraints +- Always run `npm run build && node --test test/*.test.mjs` successfully before keeping an experiment +- Maintain backwards compatibility for existing worlds (new manifest fields must be optional) +- No new runtime dependencies without clear justification +- `pokemon-world` must remain launchable via `node server.mjs` + +## What's Been Tried +- Baseline scaffolding (2026-03-18): set up autoresearch harness, no optimizations applied yet. diff --git a/autoresearch.sh b/autoresearch.sh new file mode 100755 index 0000000..97959ce --- /dev/null +++ b/autoresearch.sh @@ -0,0 +1,47 @@ +#!/bin/bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "$0")" && pwd)" +POKEMON_WORLD_DIR="/Users/yilin/Developer/ReScienceLab/pokemon-world" + +if [ ! -d "$POKEMON_WORLD_DIR" ]; then + echo "[autoresearch] Missing pokemon-world repo at $POKEMON_WORLD_DIR" >&2 + exit 1 +fi + +now_ms() { + python - <<'PY' +import time +print(int(time.time() * 1000)) +PY +} + +run_step() { + local var_name="$1" + shift + local start_ms end_ms + start_ms=$(now_ms) + "$@" + end_ms=$(now_ms) + local duration=$((end_ms - start_ms)) + printf -v "$var_name" '%s' "$duration" +} + +start_total_ms=$(now_ms) + +echo "[autoresearch] npm run build" +run_step dap_build_ms npm run build + +echo "[autoresearch] node --test test/*.test.mjs" +run_step dap_tests_ms node --test test/*.test.mjs + +echo "[autoresearch] pokemon-world: node --check server.mjs" +run_step pokemon_check_ms bash -c "cd '$POKEMON_WORLD_DIR' && node --check server.mjs" + +end_total_ms=$(now_ms) +total_ms=$((end_total_ms - start_total_ms)) + +echo "METRIC dap_build_ms=$dap_build_ms" +echo "METRIC dap_tests_ms=$dap_tests_ms" +echo "METRIC pokemon_check_ms=$pokemon_check_ms" +echo "METRIC total_ms=$total_ms" From 7ab85ddd3f634565514d108629bbb6d7c3f47f0a Mon Sep 17 00:00:00 2001 From: Yilin Jing Date: Wed, 18 Mar 2026 15:24:49 +0800 Subject: [PATCH 2/7] baseline build+tests timings\n\nResult: {"status":"keep","total_ms":2141,"dap_build_ms":1297,"dap_tests_ms":736,"pokemon_check_ms":47} --- .agents/skills/using-git-worktrees/SKILL.md | 218 ++++++++++++++++++++ .claude/skills/using-git-worktrees | 1 + .factory/skills/using-git-worktrees | 1 + autoresearch.jsonl | 1 + 4 files changed, 221 insertions(+) create mode 100644 .agents/skills/using-git-worktrees/SKILL.md create mode 120000 .claude/skills/using-git-worktrees create mode 120000 .factory/skills/using-git-worktrees create mode 100644 autoresearch.jsonl diff --git a/.agents/skills/using-git-worktrees/SKILL.md b/.agents/skills/using-git-worktrees/SKILL.md new file mode 100644 index 0000000..e153843 --- /dev/null +++ b/.agents/skills/using-git-worktrees/SKILL.md @@ -0,0 +1,218 @@ +--- +name: using-git-worktrees +description: Use when starting feature work that needs isolation from current workspace or before executing implementation plans - creates isolated git worktrees with smart directory selection and safety verification +--- + +# Using Git Worktrees + +## Overview + +Git worktrees create isolated workspaces sharing the same repository, allowing work on multiple branches simultaneously without switching. + +**Core principle:** Systematic directory selection + safety verification = reliable isolation. + +**Announce at start:** "I'm using the using-git-worktrees skill to set up an isolated workspace." + +## Directory Selection Process + +Follow this priority order: + +### 1. Check Existing Directories + +```bash +# Check in priority order +ls -d .worktrees 2>/dev/null # Preferred (hidden) +ls -d worktrees 2>/dev/null # Alternative +``` + +**If found:** Use that directory. If both exist, `.worktrees` wins. + +### 2. Check CLAUDE.md + +```bash +grep -i "worktree.*director" CLAUDE.md 2>/dev/null +``` + +**If preference specified:** Use it without asking. + +### 3. Ask User + +If no directory exists and no CLAUDE.md preference: + +``` +No worktree directory found. Where should I create worktrees? + +1. .worktrees/ (project-local, hidden) +2. ~/.config/superpowers/worktrees// (global location) + +Which would you prefer? +``` + +## Safety Verification + +### For Project-Local Directories (.worktrees or worktrees) + +**MUST verify directory is ignored before creating worktree:** + +```bash +# Check if directory is ignored (respects local, global, and system gitignore) +git check-ignore -q .worktrees 2>/dev/null || git check-ignore -q worktrees 2>/dev/null +``` + +**If NOT ignored:** + +Per Jesse's rule "Fix broken things immediately": +1. Add appropriate line to .gitignore +2. Commit the change +3. Proceed with worktree creation + +**Why critical:** Prevents accidentally committing worktree contents to repository. + +### For Global Directory (~/.config/superpowers/worktrees) + +No .gitignore verification needed - outside project entirely. + +## Creation Steps + +### 1. Detect Project Name + +```bash +project=$(basename "$(git rev-parse --show-toplevel)") +``` + +### 2. Create Worktree + +```bash +# Determine full path +case $LOCATION in + .worktrees|worktrees) + path="$LOCATION/$BRANCH_NAME" + ;; + ~/.config/superpowers/worktrees/*) + path="~/.config/superpowers/worktrees/$project/$BRANCH_NAME" + ;; +esac + +# Create worktree with new branch +git worktree add "$path" -b "$BRANCH_NAME" +cd "$path" +``` + +### 3. Run Project Setup + +Auto-detect and run appropriate setup: + +```bash +# Node.js +if [ -f package.json ]; then npm install; fi + +# Rust +if [ -f Cargo.toml ]; then cargo build; fi + +# Python +if [ -f requirements.txt ]; then pip install -r requirements.txt; fi +if [ -f pyproject.toml ]; then poetry install; fi + +# Go +if [ -f go.mod ]; then go mod download; fi +``` + +### 4. Verify Clean Baseline + +Run tests to ensure worktree starts clean: + +```bash +# Examples - use project-appropriate command +npm test +cargo test +pytest +go test ./... +``` + +**If tests fail:** Report failures, ask whether to proceed or investigate. + +**If tests pass:** Report ready. + +### 5. Report Location + +``` +Worktree ready at +Tests passing ( tests, 0 failures) +Ready to implement +``` + +## Quick Reference + +| Situation | Action | +|-----------|--------| +| `.worktrees/` exists | Use it (verify ignored) | +| `worktrees/` exists | Use it (verify ignored) | +| Both exist | Use `.worktrees/` | +| Neither exists | Check CLAUDE.md → Ask user | +| Directory not ignored | Add to .gitignore + commit | +| Tests fail during baseline | Report failures + ask | +| No package.json/Cargo.toml | Skip dependency install | + +## Common Mistakes + +### Skipping ignore verification + +- **Problem:** Worktree contents get tracked, pollute git status +- **Fix:** Always use `git check-ignore` before creating project-local worktree + +### Assuming directory location + +- **Problem:** Creates inconsistency, violates project conventions +- **Fix:** Follow priority: existing > CLAUDE.md > ask + +### Proceeding with failing tests + +- **Problem:** Can't distinguish new bugs from pre-existing issues +- **Fix:** Report failures, get explicit permission to proceed + +### Hardcoding setup commands + +- **Problem:** Breaks on projects using different tools +- **Fix:** Auto-detect from project files (package.json, etc.) + +## Example Workflow + +``` +You: I'm using the using-git-worktrees skill to set up an isolated workspace. + +[Check .worktrees/ - exists] +[Verify ignored - git check-ignore confirms .worktrees/ is ignored] +[Create worktree: git worktree add .worktrees/auth -b feature/auth] +[Run npm install] +[Run npm test - 47 passing] + +Worktree ready at /Users/jesse/myproject/.worktrees/auth +Tests passing (47 tests, 0 failures) +Ready to implement auth feature +``` + +## Red Flags + +**Never:** +- Create worktree without verifying it's ignored (project-local) +- Skip baseline test verification +- Proceed with failing tests without asking +- Assume directory location when ambiguous +- Skip CLAUDE.md check + +**Always:** +- Follow directory priority: existing > CLAUDE.md > ask +- Verify directory is ignored for project-local +- Auto-detect and run project setup +- Verify clean test baseline + +## Integration + +**Called by:** +- **brainstorming** (Phase 4) - REQUIRED when design is approved and implementation follows +- **subagent-driven-development** - REQUIRED before executing any tasks +- **executing-plans** - REQUIRED before executing any tasks +- Any skill needing isolated workspace + +**Pairs with:** +- **finishing-a-development-branch** - REQUIRED for cleanup after work complete diff --git a/.claude/skills/using-git-worktrees b/.claude/skills/using-git-worktrees new file mode 120000 index 0000000..d49204a --- /dev/null +++ b/.claude/skills/using-git-worktrees @@ -0,0 +1 @@ +../../.agents/skills/using-git-worktrees \ No newline at end of file diff --git a/.factory/skills/using-git-worktrees b/.factory/skills/using-git-worktrees new file mode 120000 index 0000000..d49204a --- /dev/null +++ b/.factory/skills/using-git-worktrees @@ -0,0 +1 @@ +../../.agents/skills/using-git-worktrees \ No newline at end of file diff --git a/autoresearch.jsonl b/autoresearch.jsonl new file mode 100644 index 0000000..9f07fc5 --- /dev/null +++ b/autoresearch.jsonl @@ -0,0 +1 @@ +{"type":"config","name":"Distributed World Authoring","metricName":"total_ms","metricUnit":"ms","bestDirection":"lower"} From 6c301398cde2ac37cafbc968df91571d1133a474 Mon Sep 17 00:00:00 2001 From: Yilin Jing Date: Wed, 18 Mar 2026 15:31:51 +0800 Subject: [PATCH 3/7] normalize hosted manifest handling in SDK\n\nResult: {"status":"keep","total_ms":2053,"dap_build_ms":1118,"dap_tests_ms":817,"pokemon_check_ms":62} --- autoresearch.jsonl | 1 + packages/agent-world-sdk/src/index.ts | 2 + packages/agent-world-sdk/src/types.ts | 11 ++- packages/agent-world-sdk/src/world-server.ts | 77 +++++++++++++++++--- 4 files changed, 80 insertions(+), 11 deletions(-) diff --git a/autoresearch.jsonl b/autoresearch.jsonl index 9f07fc5..83b37d5 100644 --- a/autoresearch.jsonl +++ b/autoresearch.jsonl @@ -1 +1,2 @@ {"type":"config","name":"Distributed World Authoring","metricName":"total_ms","metricUnit":"ms","bestDirection":"lower"} +{"run":1,"commit":"7ab85dd","metric":2141,"metrics":{"dap_build_ms":1297,"dap_tests_ms":736,"pokemon_check_ms":47},"status":"keep","description":"baseline build+tests timings","timestamp":1773818689202,"segment":0} diff --git a/packages/agent-world-sdk/src/index.ts b/packages/agent-world-sdk/src/index.ts index 61a9a36..25951d3 100644 --- a/packages/agent-world-sdk/src/index.ts +++ b/packages/agent-world-sdk/src/index.ts @@ -14,6 +14,8 @@ export type { Identity, BootstrapNode, ActionParamSchema, + StructuredActionSchema, + LegacyActionSchema, ActionSchema, WorldRule, HostInfo, diff --git a/packages/agent-world-sdk/src/types.ts b/packages/agent-world-sdk/src/types.ts index 8363e79..a2ceba2 100644 --- a/packages/agent-world-sdk/src/types.ts +++ b/packages/agent-world-sdk/src/types.ts @@ -35,15 +35,22 @@ export interface ActionParamSchema { desc?: string min?: number max?: number - enum?: (string | number)[] + enum?: Array } -export interface ActionSchema { +export interface StructuredActionSchema { desc: string params?: Record phase?: string[] } +export interface LegacyActionSchema { + desc: string + params?: Record +} + +export type ActionSchema = StructuredActionSchema | LegacyActionSchema + export interface WorldRule { id?: string text: string diff --git a/packages/agent-world-sdk/src/world-server.ts b/packages/agent-world-sdk/src/world-server.ts index 3424ac7..55215db 100644 --- a/packages/agent-world-sdk/src/world-server.ts +++ b/packages/agent-world-sdk/src/world-server.ts @@ -4,7 +4,7 @@ import { PeerDb } from "./peer-db.js" import { registerPeerRoutes } from "./peer-protocol.js" import { startDiscovery } from "./bootstrap.js" import { canonicalize, signPayload, signHttpRequest } from "./crypto.js" -import type { WorldConfig, WorldHooks, WorldServer, WorldManifest } from "./types.js" +import type { WorldConfig, WorldHooks, WorldServer, WorldManifest, WorldRule, ActionSchema, ActionParamSchema, StructuredActionSchema } from "./types.js" const DEFAULT_BOOTSTRAP_URL = "https://resciencelab.github.io/DAP/bootstrap.json" @@ -48,18 +48,36 @@ export async function createWorldServer( cardDescription, } = config - function enrichManifest(manifest: WorldManifest): WorldManifest { - if (worldType !== "hosted" || !hostAgentId) return manifest - return { + function buildManifest(manifest?: WorldManifest): WorldManifest { + const base: WorldManifest = { + name: manifest?.name ?? worldName, ...manifest, - type: "hosted", - host: { + } + + const normalized: WorldManifest = { + ...base, + type: base.type ?? worldType ?? "programmatic", + } + + const rules = ensureRules(base.rules) + if (rules) normalized.rules = rules + + const actions = ensureActions(base.actions) + if (actions) normalized.actions = actions + + if (!normalized.theme) normalized.theme = worldTheme + + if (worldType === "hosted" && hostAgentId) { + normalized.type = "hosted" + normalized.host = { agentId: hostAgentId, cardUrl: hostCardUrl, endpoints: hostEndpoints, - ...manifest.host, - }, + ...normalized.host, + } } + + return normalized } const resolvedPublicPort = publicPort ?? port @@ -104,7 +122,7 @@ export async function createWorldServer( } agentLastSeen.set(agentId, Date.now()) const result = await hooks.onJoin(agentId, data) - sendReply({ ok: true, worldId, manifest: enrichManifest(result.manifest), state: result.state }) + sendReply({ ok: true, worldId, manifest: buildManifest(result.manifest), state: result.state }) console.log(`[world] ${agentId.slice(0, 8)} joined — ${agentLastSeen.size} agents`) return } @@ -234,3 +252,44 @@ export async function createWorldServer( }, } } + + +function ensureRules(rules?: Array): WorldRule[] | undefined { + if (!rules?.length) return undefined + return rules.map((rule, index) => { + if (typeof rule === "string") { + return { id: `rule-${index + 1}`, text: rule, enforced: false } + } + return { ...rule, id: rule.id ?? `rule-${index + 1}` } + }) +} + +function ensureActions(actions?: Record): Record | undefined { + if (!actions) return undefined + const normalized: Record = {} + for (const [name, schema] of Object.entries(actions)) { + if (!schema) continue + const params = normalizeActionParams(schema.params as Record | undefined) + const phase = "phase" in schema ? schema.phase : undefined + normalized[name] = { + desc: schema.desc, + ...(params ? { params } : {}), + ...(phase ? { phase } : {}), + } + } + return Object.keys(normalized).length ? normalized : undefined +} + +function normalizeActionParams(params?: Record): Record | undefined { + if (!params) return undefined + const normalized: Record = {} + for (const [name, schema] of Object.entries(params)) { + if (!schema) continue + if (typeof schema === "string") { + normalized[name] = { type: "string", desc: schema } + continue + } + normalized[name] = { ...schema, type: schema.type ?? "string" } + } + return Object.keys(normalized).length ? normalized : undefined +} From 920a78a1205b28b081a39a4a73e01beec13e6da5 Mon Sep 17 00:00:00 2001 From: Yilin Jing Date: Wed, 18 Mar 2026 15:37:38 +0800 Subject: [PATCH 4/7] manifest docs/template overhaul + incremental builds\n\nResult: {"status":"keep","total_ms":1588,"dap_build_ms":940,"dap_tests_ms":570,"pokemon_check_ms":37} --- .gitignore | 1 + autoresearch.jsonl | 2 + docs/WORLD_MANIFEST.md | 126 ++++++++++++++++++++++++++++++++++++----- tsconfig.json | 4 +- world/server.mjs | 17 ++++-- 5 files changed, 130 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index 53eb731..73f6b20 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules/ dist/ *.js.map +*.tsbuildinfo .env *.db *.db-journal diff --git a/autoresearch.jsonl b/autoresearch.jsonl index 83b37d5..14728f7 100644 --- a/autoresearch.jsonl +++ b/autoresearch.jsonl @@ -1,2 +1,4 @@ {"type":"config","name":"Distributed World Authoring","metricName":"total_ms","metricUnit":"ms","bestDirection":"lower"} {"run":1,"commit":"7ab85dd","metric":2141,"metrics":{"dap_build_ms":1297,"dap_tests_ms":736,"pokemon_check_ms":47},"status":"keep","description":"baseline build+tests timings","timestamp":1773818689202,"segment":0} +{"run":2,"commit":"6c30139","metric":2053,"metrics":{"dap_build_ms":1118,"dap_tests_ms":817,"pokemon_check_ms":62},"status":"keep","description":"normalize hosted manifest handling in SDK","timestamp":1773819111835,"segment":0} +{"run":3,"commit":"6c30139","metric":2449,"metrics":{"dap_build_ms":1381,"dap_tests_ms":933,"pokemon_check_ms":43},"status":"discard","description":"doc + template manifest normalization (no perf gain)","timestamp":1773819295412,"segment":0} diff --git a/docs/WORLD_MANIFEST.md b/docs/WORLD_MANIFEST.md index 43d4b66..4869ba0 100644 --- a/docs/WORLD_MANIFEST.md +++ b/docs/WORLD_MANIFEST.md @@ -14,36 +14,121 @@ World Agents are discovered automatically via the DAP bootstrap network: No registration or central database required. If your World Agent is on the network, it will be discovered. +## Programmatic vs Hosted Worlds + +| Type | Description | Typical examples | +| --- | --- | --- | +| **Programmatic** | World Server =裁判+规则引擎。Agent 发送 `world.action`,服务器按规则执行并返回结果。胜负/状态由程序裁定。 | Pokemon Battle Arena、Chess、拍卖行 | +| **Hosted** | 有一个 Host Agent,World Server 只做“场所公告 + 撮合”。访客通过 manifest 获得 host 的 agentId / card / endpoints,之后点对点沟通。 | 咖啡厅、咨询室、工作室 | + +世界作者通过 manifest 的 `type`、`host`、`lifecycle` 声明自身模式;SDK 会在 `world.join` 响应时把结构化信息返回给 agent,帮助其自动判断如何互动。 + ## WORLD.md -Each World project should include a `WORLD.md` file in its root directory. This file describes the world metadata in YAML frontmatter: +每个 World 仓库应包含 `WORLD.md`,其 YAML frontmatter 描述世界元数据。示例: ```yaml --- -name: my-world -description: "A brief description of what this world does" +name: pokemon-arena version: "1.0.0" -author: your-name -theme: battle | exploration | social | sandbox | custom +author: resciencelab +theme: battle frontend_path: / manifest: - objective: "What agents should try to achieve" + type: programmatic + objective: "Win turn-based Pokemon battles" rules: - - "Rule 1" - - "Rule 2" + - id: rule-1 + text: "Each trainer submits one action per turn" + enforced: true + - text: "Idle players are auto-moved after 10s" + enforced: false + lifecycle: + matchmaking: arena + evictionPolicy: loser-leaves + turnTimeoutMs: 10000 + turnTimeoutAction: default-move actions: - action_name: - params: { key: "description of param" } - desc: "What this action does" + move: + desc: "Use a move" + params: + slot: + type: number + required: true + desc: "Move slot (1-4)" + min: 1 + max: 4 + switch: + desc: "Switch Pokemon" + params: + slot: + type: number + required: true + desc: "Bench slot" state_fields: - - "field — description" + - "active — active Pokemon summary" + - "teams — remaining roster" --- -# My World +# Pokemon Arena Human-readable documentation about the world. ``` +Hosted 世界可在 manifest 中添加: + +```yaml +manifest: + type: hosted + host: + agentId: aw:sha256:... + name: "Max" + description: "咖啡厅老板,喜欢聊科技" + cardUrl: https://max.world/.well-known/agent.json + endpoints: + - transport: tcp + address: cafe.example.com + port: 8099 +``` + +## Manifest Reference + +### `type` +`"programmatic"`(默认)或 `"hosted"`。Hosted 模式下 SDK 会自动把 host 信息注入 manifest,并由访客直接联系 host agent。 + +### `rules` +数组,可为字符串或对象。对象格式:`{ id?: string, text: string, enforced: boolean }`。SDK 会为字符串自动生成 ID,并默认 `enforced: false`。 + +### `actions` +`Record`。现代 schema: + +```yaml +actions: + move: + desc: "Use a move" + phase: ["battle"] + params: + direction: + type: string + enum: [up, down, left, right] + required: true + desc: "Move direction" +``` + +参数描述支持 `type` (`string`/`number`/`boolean`)、`required`、`desc`、`min`/`max`、`enum`。老格式 `{ params: { key: "description" } }` 仍兼容,SDK 会自动转换。 + +### `host` +Hosted 世界声明 host agent 的身份:`agentId`、`cardUrl`、`endpoints`、`name`、`description`。客户端应验证 host Agent Card 的 JWS 签名。 + +### `lifecycle` +结构化匹配/淘汰规则: +- `matchmaking`: `"arena"` (擂台制) 或 `"free"` +- `evictionPolicy`: `"idle" | "loser-leaves" | "manual"` +- `idleTimeoutMs`, `turnTimeoutMs`, `turnTimeoutAction` (`"default-move" | "forfeit"`) + +### `state_fields` +描述 `state` 对象字段,帮助 Agent 理解世界状态。 + ## DAP Peer Protocol Every World Agent must implement these HTTP endpoints: @@ -108,11 +193,22 @@ Agent requests to join the world. Response includes the **manifest** so the agen "worldId": "my-world", "manifest": { "name": "My World", + "type": "programmatic", "description": "...", "objective": "...", - "rules": ["..."], + "rules": [{ "id": "rule-1", "text": "...", "enforced": true }], "actions": { - "move": { "params": { "direction": "up|down|left|right" }, "desc": "Move in a direction" } + "move": { + "params": { + "direction": { "type": "string", "enum": ["up", "down"], "required": true } + }, + "desc": "Move in a direction" + } + }, + "lifecycle": { "turnTimeoutMs": 10000 }, + "host": { + "agentId": "aw:sha256:...", + "cardUrl": "https://host.world/.well-known/agent.json" }, "state_fields": ["x — current x position", "y — current y position"] }, diff --git a/tsconfig.json b/tsconfig.json index 1951778..68d4855 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,9 @@ "resolveJsonModule": true, "declaration": true, "declarationMap": true, - "sourceMap": true + "sourceMap": true, + "incremental": true, + "tsBuildInfoFile": "./tsconfig.tsbuildinfo" }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] diff --git a/world/server.mjs b/world/server.mjs index 6dc4ce1..bf83956 100644 --- a/world/server.mjs +++ b/world/server.mjs @@ -103,18 +103,27 @@ const server = await createWorldServer( return { manifest: { name: process.env.WORLD_NAME ?? `World (${WORLD_ID})`, + type: "programmatic", theme: process.env.WORLD_THEME ?? "default", description: `A world on a ${WORLD_WIDTH}x${WORLD_HEIGHT} grid.`, objective: "Explore the world and interact with other agents.", rules: [ - `The world is a ${WORLD_WIDTH}x${WORLD_HEIGHT} grid.`, - "Agents can move to any tile by sending a move action with x,y coordinates.", - "Idle agents are evicted after 5 minutes.", + { id: "rule-1", text: `The world is a ${WORLD_WIDTH}x${WORLD_HEIGHT} grid.`, enforced: true }, + { id: "rule-2", text: "Agents can move to any tile by sending a move action with x,y coordinates.", enforced: true }, + { id: "rule-3", text: "Idle agents are evicted after 5 minutes.", enforced: false }, ], + lifecycle: { + matchmaking: "free", + evictionPolicy: "idle", + idleTimeoutMs: 5 * 60 * 1000, + }, actions: { move: { - params: { x: `0-${WORLD_WIDTH - 1}`, y: `0-${WORLD_HEIGHT - 1}` }, desc: "Move to position (x, y) on the grid.", + params: { + x: { type: "number", required: true, desc: "Target x position", min: 0, max: WORLD_WIDTH - 1 }, + y: { type: "number", required: true, desc: "Target y position", min: 0, max: WORLD_HEIGHT - 1 }, + }, }, }, state_fields: [ From 737c62de131d3c6891ead5f97f309c2051d9b4ba Mon Sep 17 00:00:00 2001 From: Yilin Jing Date: Wed, 18 Mar 2026 15:41:59 +0800 Subject: [PATCH 5/7] localize manifest docs to English only\n\nResult: {"status":"keep","total_ms":1336,"dap_build_ms":601,"dap_tests_ms":631,"pokemon_check_ms":47} --- autoresearch.jsonl | 1 + docs/WORLD_MANIFEST.md | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/autoresearch.jsonl b/autoresearch.jsonl index 14728f7..afcce0d 100644 --- a/autoresearch.jsonl +++ b/autoresearch.jsonl @@ -2,3 +2,4 @@ {"run":1,"commit":"7ab85dd","metric":2141,"metrics":{"dap_build_ms":1297,"dap_tests_ms":736,"pokemon_check_ms":47},"status":"keep","description":"baseline build+tests timings","timestamp":1773818689202,"segment":0} {"run":2,"commit":"6c30139","metric":2053,"metrics":{"dap_build_ms":1118,"dap_tests_ms":817,"pokemon_check_ms":62},"status":"keep","description":"normalize hosted manifest handling in SDK","timestamp":1773819111835,"segment":0} {"run":3,"commit":"6c30139","metric":2449,"metrics":{"dap_build_ms":1381,"dap_tests_ms":933,"pokemon_check_ms":43},"status":"discard","description":"doc + template manifest normalization (no perf gain)","timestamp":1773819295412,"segment":0} +{"run":4,"commit":"920a78a","metric":1588,"metrics":{"dap_build_ms":940,"dap_tests_ms":570,"pokemon_check_ms":37},"status":"keep","description":"manifest docs/template overhaul + incremental builds","timestamp":1773819458699,"segment":0} diff --git a/docs/WORLD_MANIFEST.md b/docs/WORLD_MANIFEST.md index 4869ba0..1b6addc 100644 --- a/docs/WORLD_MANIFEST.md +++ b/docs/WORLD_MANIFEST.md @@ -18,14 +18,14 @@ No registration or central database required. If your World Agent is on the netw | Type | Description | Typical examples | | --- | --- | --- | -| **Programmatic** | World Server =裁判+规则引擎。Agent 发送 `world.action`,服务器按规则执行并返回结果。胜负/状态由程序裁定。 | Pokemon Battle Arena、Chess、拍卖行 | -| **Hosted** | 有一个 Host Agent,World Server 只做“场所公告 + 撮合”。访客通过 manifest 获得 host 的 agentId / card / endpoints,之后点对点沟通。 | 咖啡厅、咨询室、工作室 | +| **Programmatic** | World Server acts as a referee + rules engine. Agents send `world.action`, the server applies deterministic logic, and wins/losses are decided purely by code. | Pokemon Battle Arena, chess, auction house | +| **Hosted** | A Host Agent exists; the World Server only handles venue announcements + matchmaking. Visitors obtain the host agentId/card/endpoints from the manifest and then communicate peer-to-peer. | Coffee shop, counseling room, personal studio | -世界作者通过 manifest 的 `type`、`host`、`lifecycle` 声明自身模式;SDK 会在 `world.join` 响应时把结构化信息返回给 agent,帮助其自动判断如何互动。 +World authors use the manifest `type`, `host`, and `lifecycle` fields to declare their mode; the SDK returns this structured manifest in every `world.join` response so agents can automatically decide how to interact. ## WORLD.md -每个 World 仓库应包含 `WORLD.md`,其 YAML frontmatter 描述世界元数据。示例: +Each world repository should include a `WORLD.md` file whose YAML frontmatter describes its metadata. Example: ```yaml --- @@ -75,7 +75,7 @@ manifest: Human-readable documentation about the world. ``` -Hosted 世界可在 manifest 中添加: +Hosted worlds can extend the manifest with: ```yaml manifest: @@ -83,7 +83,7 @@ manifest: host: agentId: aw:sha256:... name: "Max" - description: "咖啡厅老板,喜欢聊科技" + description: "Coffee shop host who enjoys chatting about technology" cardUrl: https://max.world/.well-known/agent.json endpoints: - transport: tcp @@ -94,13 +94,13 @@ manifest: ## Manifest Reference ### `type` -`"programmatic"`(默认)或 `"hosted"`。Hosted 模式下 SDK 会自动把 host 信息注入 manifest,并由访客直接联系 host agent。 +`"programmatic"` (default) or `"hosted"`. In hosted mode the SDK automatically injects host information into the manifest so visitors can contact the host agent directly. ### `rules` -数组,可为字符串或对象。对象格式:`{ id?: string, text: string, enforced: boolean }`。SDK 会为字符串自动生成 ID,并默认 `enforced: false`。 +Array of strings or objects. Object form: `{ id?: string, text: string, enforced: boolean }`. The SDK auto-generates IDs for strings and defaults `enforced` to `false`. ### `actions` -`Record`。现代 schema: +`Record`. Modern schema: ```yaml actions: @@ -115,19 +115,19 @@ actions: desc: "Move direction" ``` -参数描述支持 `type` (`string`/`number`/`boolean`)、`required`、`desc`、`min`/`max`、`enum`。老格式 `{ params: { key: "description" } }` 仍兼容,SDK 会自动转换。 +Parameter schemas support `type` (`string` / `number` / `boolean`), `required`, `desc`, `min` / `max`, and `enum`. The legacy `{ params: { key: "description" } }` format remains compatible; the SDK converts it automatically. ### `host` -Hosted 世界声明 host agent 的身份:`agentId`、`cardUrl`、`endpoints`、`name`、`description`。客户端应验证 host Agent Card 的 JWS 签名。 +Hosted worlds declare the host agent's identity via `agentId`, `cardUrl`, `endpoints`, `name`, `description`. Clients should verify the host Agent Card JWS signature. ### `lifecycle` -结构化匹配/淘汰规则: -- `matchmaking`: `"arena"` (擂台制) 或 `"free"` +Structured match/eviction hints: +- `matchmaking`: `"arena"` (king-of-the-hill) or `"free"` - `evictionPolicy`: `"idle" | "loser-leaves" | "manual"` - `idleTimeoutMs`, `turnTimeoutMs`, `turnTimeoutAction` (`"default-move" | "forfeit"`) ### `state_fields` -描述 `state` 对象字段,帮助 Agent 理解世界状态。 +Explains the keys inside the `state` object so agents can interpret snapshots. ## DAP Peer Protocol From f7282865527a49903fb6be8e12a54aec01dc76ca Mon Sep 17 00:00:00 2001 From: Yilin Jing Date: Wed, 18 Mar 2026 15:42:55 +0800 Subject: [PATCH 6/7] chore: add autoresearch.jsonl --- autoresearch.jsonl | 1 + 1 file changed, 1 insertion(+) diff --git a/autoresearch.jsonl b/autoresearch.jsonl index afcce0d..c024778 100644 --- a/autoresearch.jsonl +++ b/autoresearch.jsonl @@ -3,3 +3,4 @@ {"run":2,"commit":"6c30139","metric":2053,"metrics":{"dap_build_ms":1118,"dap_tests_ms":817,"pokemon_check_ms":62},"status":"keep","description":"normalize hosted manifest handling in SDK","timestamp":1773819111835,"segment":0} {"run":3,"commit":"6c30139","metric":2449,"metrics":{"dap_build_ms":1381,"dap_tests_ms":933,"pokemon_check_ms":43},"status":"discard","description":"doc + template manifest normalization (no perf gain)","timestamp":1773819295412,"segment":0} {"run":4,"commit":"920a78a","metric":1588,"metrics":{"dap_build_ms":940,"dap_tests_ms":570,"pokemon_check_ms":37},"status":"keep","description":"manifest docs/template overhaul + incremental builds","timestamp":1773819458699,"segment":0} +{"run":5,"commit":"737c62d","metric":1336,"metrics":{"dap_build_ms":601,"dap_tests_ms":631,"pokemon_check_ms":47},"status":"keep","description":"localize manifest docs to English only","timestamp":1773819719880,"segment":0} From 53c8e10079a8ee1218b7cab9c1ed2e161499d15f Mon Sep 17 00:00:00 2001 From: Yilin Jing Date: Wed, 18 Mar 2026 15:50:07 +0800 Subject: [PATCH 7/7] chore: remove autoresearch scaffolding Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- autoresearch.jsonl | 6 ------ autoresearch.md | 35 ---------------------------------- autoresearch.sh | 47 ---------------------------------------------- 3 files changed, 88 deletions(-) delete mode 100644 autoresearch.jsonl delete mode 100644 autoresearch.md delete mode 100755 autoresearch.sh diff --git a/autoresearch.jsonl b/autoresearch.jsonl deleted file mode 100644 index c024778..0000000 --- a/autoresearch.jsonl +++ /dev/null @@ -1,6 +0,0 @@ -{"type":"config","name":"Distributed World Authoring","metricName":"total_ms","metricUnit":"ms","bestDirection":"lower"} -{"run":1,"commit":"7ab85dd","metric":2141,"metrics":{"dap_build_ms":1297,"dap_tests_ms":736,"pokemon_check_ms":47},"status":"keep","description":"baseline build+tests timings","timestamp":1773818689202,"segment":0} -{"run":2,"commit":"6c30139","metric":2053,"metrics":{"dap_build_ms":1118,"dap_tests_ms":817,"pokemon_check_ms":62},"status":"keep","description":"normalize hosted manifest handling in SDK","timestamp":1773819111835,"segment":0} -{"run":3,"commit":"6c30139","metric":2449,"metrics":{"dap_build_ms":1381,"dap_tests_ms":933,"pokemon_check_ms":43},"status":"discard","description":"doc + template manifest normalization (no perf gain)","timestamp":1773819295412,"segment":0} -{"run":4,"commit":"920a78a","metric":1588,"metrics":{"dap_build_ms":940,"dap_tests_ms":570,"pokemon_check_ms":37},"status":"keep","description":"manifest docs/template overhaul + incremental builds","timestamp":1773819458699,"segment":0} -{"run":5,"commit":"737c62d","metric":1336,"metrics":{"dap_build_ms":601,"dap_tests_ms":631,"pokemon_check_ms":47},"status":"keep","description":"localize manifest docs to English only","timestamp":1773819719880,"segment":0} diff --git a/autoresearch.md b/autoresearch.md deleted file mode 100644 index 5f8944f..0000000 --- a/autoresearch.md +++ /dev/null @@ -1,35 +0,0 @@ -# Autoresearch: Distributed World Authoring - -## Objective -Make it dramatically easier to author and run distributed agent worlds by: -- Extending the DAP protocol / `@resciencelab/agent-world-sdk` manifests to cover programmatic & hosted worlds -- Updating the built-in `world/` template and the standalone `pokemon-world` example to follow the new manifest contract -- Ensuring the OpenClaw plugin, SDK, and example worlds stay fully buildable and testable while these capabilities evolve - -## Metrics -- **Primary**: `total_ms` (milliseconds, lower is better) — wall-clock time to build DAP, run the test suite, and syntax-check the Pokemon world -- **Secondary**: `dap_build_ms`, `dap_tests_ms`, `pokemon_check_ms` - -## How to Run -`./autoresearch.sh` — prints `METRIC name=value` lines for each step and exits non-zero on failure. - -## Files in Scope -- `packages/agent-world-sdk/**` — SDK types, peer protocol helpers, manifest + world server logic -- `world/**` — default world template, docs, helper scripts -- `docs/**` (especially `docs/WORLD_MANIFEST.md`) — published protocol documentation -- `src/**` (where SDK integration into the DAP plugin lives) when needed for end-to-end behavior -- `/Users/yilin/Developer/ReScienceLab/pokemon-world/**` — standalone example world that must reflect the new authoring flow - -## Off Limits -- `dist/**`, `node_modules/**`, build artifacts (regenerate instead of editing) -- Secrets / identity material under `~/.openclaw/**` -- Bootstrap node deployment scripts (only touch when explicitly needed) - -## Constraints -- Always run `npm run build && node --test test/*.test.mjs` successfully before keeping an experiment -- Maintain backwards compatibility for existing worlds (new manifest fields must be optional) -- No new runtime dependencies without clear justification -- `pokemon-world` must remain launchable via `node server.mjs` - -## What's Been Tried -- Baseline scaffolding (2026-03-18): set up autoresearch harness, no optimizations applied yet. diff --git a/autoresearch.sh b/autoresearch.sh deleted file mode 100755 index 97959ce..0000000 --- a/autoresearch.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -set -euo pipefail - -ROOT_DIR="$(cd "$(dirname "$0")" && pwd)" -POKEMON_WORLD_DIR="/Users/yilin/Developer/ReScienceLab/pokemon-world" - -if [ ! -d "$POKEMON_WORLD_DIR" ]; then - echo "[autoresearch] Missing pokemon-world repo at $POKEMON_WORLD_DIR" >&2 - exit 1 -fi - -now_ms() { - python - <<'PY' -import time -print(int(time.time() * 1000)) -PY -} - -run_step() { - local var_name="$1" - shift - local start_ms end_ms - start_ms=$(now_ms) - "$@" - end_ms=$(now_ms) - local duration=$((end_ms - start_ms)) - printf -v "$var_name" '%s' "$duration" -} - -start_total_ms=$(now_ms) - -echo "[autoresearch] npm run build" -run_step dap_build_ms npm run build - -echo "[autoresearch] node --test test/*.test.mjs" -run_step dap_tests_ms node --test test/*.test.mjs - -echo "[autoresearch] pokemon-world: node --check server.mjs" -run_step pokemon_check_ms bash -c "cd '$POKEMON_WORLD_DIR' && node --check server.mjs" - -end_total_ms=$(now_ms) -total_ms=$((end_total_ms - start_total_ms)) - -echo "METRIC dap_build_ms=$dap_build_ms" -echo "METRIC dap_tests_ms=$dap_tests_ms" -echo "METRIC pokemon_check_ms=$pokemon_check_ms" -echo "METRIC total_ms=$total_ms"