refactor: rename 'tier' to 'level' across the codebase

- Updated WorkerState type to use 'level' instead of 'tier'.
- Modified functions related to worker state management, including parseWorkerState, emptyWorkerState, getSessionForLevel, activateWorker, and deactivateWorker to reflect the new terminology.
- Adjusted health check logic to utilize 'level' instead of 'tier'.
- Refactored tick and setup tools to accommodate the change from 'tier' to 'level', including model configuration and workspace scaffolding.
- Updated tests to ensure consistency with the new 'level' terminology.
- Revised documentation and comments to reflect the changes in terminology from 'tier' to 'level'.
This commit is contained in:
Lauren ten Hoor
2026-02-11 03:04:17 +08:00
parent 1f95ad4518
commit 5df4b912c9
18 changed files with 296 additions and 278 deletions

View File

@@ -1,36 +1,20 @@
/**
* setup/config.ts — Plugin config writer (openclaw.json).
*
* Handles: model tier config, devClawAgentIds, tool restrictions, subagent cleanup.
* Handles: model level config, devClawAgentIds, tool restrictions, subagent cleanup.
*/
import fs from "node:fs/promises";
import path from "node:path";
import { DEV_TIERS, QA_TIERS, tierName, type Tier } from "../tiers.js";
import { HEARTBEAT_DEFAULTS } from "../services/heartbeat.js";
type ModelConfig = { dev: Record<string, string>; qa: Record<string, string> };
function openclawConfigPath(): string {
return path.join(process.env.HOME ?? "/home/lauren", ".openclaw", "openclaw.json");
}
/**
* Convert flat tier map to nested role-tier structure.
*/
function buildRoleTierModels(models: Record<Tier, string>): { dev: Record<string, string>; qa: Record<string, string> } {
const dev: Record<string, string> = {};
const qa: Record<string, string> = {};
for (const tier of DEV_TIERS) {
dev[tierName(tier)] = models[tier];
}
for (const tier of QA_TIERS) {
qa[tierName(tier)] = models[tier];
}
return { dev, qa };
}
/**
* Write DevClaw model tier config and devClawAgentIds to openclaw.json plugins section.
* Write DevClaw model level config and devClawAgentIds to openclaw.json plugins section.
*
* Also configures:
* - Tool restrictions (deny sessions_spawn, sessions_send) for DevClaw agents
@@ -39,7 +23,7 @@ function buildRoleTierModels(models: Record<Tier, string>): { dev: Record<string
* Read-modify-write to preserve existing config.
*/
export async function writePluginConfig(
models: Record<Tier, string>,
models: ModelConfig,
agentId?: string,
projectExecution?: "parallel" | "sequential",
): Promise<void> {
@@ -47,7 +31,7 @@ export async function writePluginConfig(
const config = JSON.parse(await fs.readFile(configPath, "utf-8"));
ensurePluginStructure(config);
config.plugins.entries.devclaw.config.models = buildRoleTierModels(models);
config.plugins.entries.devclaw.config.models = models;
if (projectExecution) {
config.plugins.entries.devclaw.config.projectExecution = projectExecution;

View File

@@ -4,12 +4,14 @@
* Coordinates: agent creation → model config → workspace scaffolding.
* Used by both the `setup` tool and the `openclaw devclaw setup` CLI command.
*/
import { ALL_TIERS, allDefaultModels, type Tier } from "../tiers.js";
import { DEFAULT_MODELS } from "../tiers.js";
import { migrateChannelBinding } from "../binding-manager.js";
import { createAgent, resolveWorkspacePath } from "./agent.js";
import { writePluginConfig } from "./config.js";
import { scaffoldWorkspace } from "./workspace.js";
export type ModelConfig = { dev: Record<string, string>; qa: Record<string, string> };
export type SetupOpts = {
/** Create a new agent with this name. Mutually exclusive with agentId. */
newAgentName?: string;
@@ -21,8 +23,8 @@ export type SetupOpts = {
agentId?: string;
/** Override workspace path (auto-detected from agent if not given). */
workspacePath?: string;
/** Model overrides per tier. Missing tiers use defaults. */
models?: Partial<Record<Tier, string>>;
/** Model overrides per role.level. Missing levels use defaults. */
models?: { dev?: Partial<Record<string, string>>; qa?: Partial<Record<string, string>> };
/** Plugin-level project execution mode: parallel or sequential. Default: parallel. */
projectExecution?: "parallel" | "sequential";
};
@@ -31,7 +33,7 @@ export type SetupResult = {
agentId: string;
agentCreated: boolean;
workspacePath: string;
models: Record<Tier, string>;
models: ModelConfig;
filesWritten: string[];
warnings: string[];
bindingMigrated?: {
@@ -107,14 +109,20 @@ async function tryMigrateBinding(
}
}
function buildModelConfig(overrides?: Partial<Record<Tier, string>>): Record<Tier, string> {
const models = allDefaultModels();
if (overrides) {
for (const [tier, model] of Object.entries(overrides)) {
if (model && (ALL_TIERS as readonly string[]).includes(tier)) {
models[tier as Tier] = model;
}
function buildModelConfig(overrides?: SetupOpts["models"]): ModelConfig {
const dev: Record<string, string> = { ...DEFAULT_MODELS.dev };
const qa: Record<string, string> = { ...DEFAULT_MODELS.qa };
if (overrides?.dev) {
for (const [level, model] of Object.entries(overrides.dev)) {
if (model) dev[level] = model;
}
}
return models;
if (overrides?.qa) {
for (const [level, model] of Object.entries(overrides.qa)) {
if (model) qa[level] = model;
}
}
return { dev, qa };
}

View File

@@ -8,6 +8,8 @@ import path from "node:path";
import {
AGENTS_MD_TEMPLATE,
HEARTBEAT_MD_TEMPLATE,
DEFAULT_DEV_INSTRUCTIONS,
DEFAULT_QA_INSTRUCTIONS,
} from "../templates.js";
/**
@@ -34,6 +36,20 @@ export async function scaffoldWorkspace(workspacePath: string): Promise<string[]
filesWritten.push("projects/projects.json");
}
// projects/roles/default/ (fallback role instructions)
const defaultRolesDir = path.join(projectsDir, "roles", "default");
await fs.mkdir(defaultRolesDir, { recursive: true });
const devRolePath = path.join(defaultRolesDir, "dev.md");
if (!await fileExists(devRolePath)) {
await fs.writeFile(devRolePath, DEFAULT_DEV_INSTRUCTIONS, "utf-8");
filesWritten.push("projects/roles/default/dev.md");
}
const qaRolePath = path.join(defaultRolesDir, "qa.md");
if (!await fileExists(qaRolePath)) {
await fs.writeFile(qaRolePath, DEFAULT_QA_INSTRUCTIONS, "utf-8");
filesWritten.push("projects/roles/default/qa.md");
}
// log/ directory (audit.log created on first write)
const logDir = path.join(workspacePath, "log");
await fs.mkdir(logDir, { recursive: true });