Shared AgentWorkforce primitives for persona-driven orchestration.
A persona is the runtime source of truth:
- prompt (
systemPrompt) - model
- harness
- harness settings
- optional
skillsarray of{ id, source, description }entries for reusable capability guidance (e.g. prpm.dev packages)
Each persona supports service tiers:
bestbest-valueminimum
Tiering controls depth, latency budget, and model cost envelope — not the quality bar. All tiers should enforce the same correctness/safety standards; lower tiers should be more concise, not lower-quality.
A routing profile is policy-only. It does not carry runtime fields; it only selects which persona tier to use per intent and explains why.
packages/workload-router— TypeScript SDK for typed persona + routing profile resolution.
personas/frontend-implementer.jsonpersonas/code-reviewer.jsonpersonas/architecture-planner.jsonpersonas/requirements-analyst.jsonpersonas/debugger.jsonpersonas/security-reviewer.jsonpersonas/technical-writer.jsonpersonas/verifier.jsonpersonas/test-strategist.jsonpersonas/tdd-guard.jsonpersonas/flake-hunter.jsonpersonas/opencode-workflow-specialist.jsonpersonas/npm-provenance-publisher.json
packages/workload-router/routing-profiles/default.jsonpackages/workload-router/routing-profiles/schema.json
The recommended entry point is usePersona(intent) — a synchronous,
side-effect-free factory that resolves a persona and returns grouped install
metadata plus a sendMessage() closure. Calling it does nothing but
pre-compute the routing; nothing is installed or spawned until you call
sendMessage() or run the install command yourself.
import { usePersona } from '@agentworkforce/workload-router';
const { sendMessage } = usePersona('npm-provenance');
// Installs the persona's skills, then runs the persona's harness agent
// with your task. Returns a PersonaExecution — awaitable, with
// `cancel()` and a `runId` promise attached.
//
// `await sendMessage(...)` only resolves on `status: 'completed'`. Non-zero
// exits / timeouts throw PersonaExecutionError; cancellation throws
// AbortError. Both carry the typed ExecuteResult on `err.result`.
try {
const result = await sendMessage('Set up npm trusted publishing for this repo', {
workingDirectory: '.',
timeoutSeconds: 600,
});
// result.status === 'completed' here
} catch (err) {
const execErr = err as Error & {
result?: { status: string; stderr: string; exitCode: number | null };
};
console.error(
'persona run failed',
execErr.result?.status,
execErr.result?.stderr,
);
}Despite the
use*prefix,usePersonais not a React hook. It is a plain synchronous factory with no implicit state — safe to call anywhere.
The full return shape is:
const {
selection,
install,
sendMessage,
} = usePersona('npm-provenance');selection: resolved persona choice and runtime metadata.install: grouped install metadata.install.plan: pure skill-install plan with no side effects.install.command: full install command as an argv array.install.commandString: full install command as a shell string.sendMessage(task, options?): runs the persona and returns an awaitablePersonaExecution.
For the full API — the install-only mode, pre-staged install with
installSkills: false, cancellation via AbortSignal, streaming progress,
the runId timing contract, and the double-install caveat when mixing
modes — see packages/workload-router/README.md.
Low-level primitives (advanced use — prefer usePersona for new code)
If you need to resolve a persona and materialize its skill install plan
without running an agent — or you want to drive install yourself with
custom process management — usePersona is built on top of two pure
helpers you can call directly:
import { resolvePersona, materializeSkillsFor } from '@agentworkforce/workload-router';
import { spawnSync } from 'node:child_process';
const selection = resolvePersona('npm-provenance');
// selection -> { personaId, tier, runtime, skills, rationale }
const plan = materializeSkillsFor(selection);
for (const install of plan.installs) {
// install.installCommand is an argv array (safer for execFile/spawn).
// For a shell string, use `usePersona(...).install.commandString`.
spawnSync(install.installCommand[0], install.installCommand.slice(1), { stdio: 'inherit' });
}These primitives are exported for callers who need direct access to the
plan object or want to skip the sendMessage() workflow entirely. New code
should prefer usePersona — it consolidates routing, install planning,
and agent execution into one call and gives you cancellation / progress /
run-id observability for free.
- Map user request to
intent:implement-frontendreviewarchitecture-planrequirements-analysisdebuggingsecurity-reviewdocumentationverificationtest-strategytdd-enforcementflake-investigationopencode-workflow-correctnessnpm-provenance
- Call
usePersona(intent, { profile? })to resolve the persona and receive the selected persona, grouped install metadata, and asendMessage()closure bound to its runtime (harness, model, settings, prompt). - Call
sendMessage(task, opts)to install the persona's skills and invoke its harness agent in one step. UseAbortSignal/execution.cancel()for cancellation andonProgressto stream stdout/stderr.
If you need to bypass sendMessage() and spawn the agent yourself — for
example, to integrate with an existing orchestrator — resolvePersona +
materializeSkillsFor remain available as the underlying primitives
(see the collapsed section above).
See runnable mapping example:
examples/openclaw-routing.ts
This keeps runtime configuration in personas, while routing policy stays explicit, typed, and auditable.
A persona can declare a skills array of reusable capability packages (e.g. from prpm.dev):
"skills": [
{
"id": "prpm/npm-trusted-publishing",
"source": "https://prpm.dev/packages/prpm/npm-trusted-publishing",
"description": "OIDC-based npm publish without long-lived tokens"
}
]Persona JSON is harness-agnostic — it declares what skill is needed, not how to install it. The SDK's materializeSkills(skills, harness) / materializeSkillsFor(selection) helper turns the declaration into a concrete install plan, routing each skill to the right on-disk convention per harness:
| Harness | Install flag | Skill directory |
|---|---|---|
claude |
prpm install --as claude |
.claude/skills/ |
codex |
prpm install --as codex |
.agents/skills/ |
opencode |
prpm install --as opencode |
.agents/skills/ |
Each returned SkillInstall carries an argv-style installCommand, installedDir, and installedManifest path. The helper is pure — it never shells out or touches disk — so callers (relay workflows, OpenClaw spawners, ad-hoc scripts) decide how to execute it. Once installed, Claude Code auto-discovers skills from .claude/skills/; for other harnesses, read the manifest off disk and inject it into the agent's task body.
Next step is a benchmark harness to score persona/tier combinations on:
- quality (task pass rate)
- cost
- latency
Then publish a versioned “recommended tier map” so default routing is data-backed.
corepack enable
pnpm install
pnpm run checkThis runs minimal guardrails across the workspace:
lint(currently TypeScript-only)typecheck(package + examples)test(Node test runner)