@agentworkforce/workload-router 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/eval.d.ts +25 -0
- package/dist/eval.d.ts.map +1 -0
- package/dist/eval.js +14 -0
- package/dist/eval.js.map +1 -0
- package/dist/index.d.ts +104 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +301 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +195 -0
- package/dist/index.test.js.map +1 -0
- package/package.json +5 -1
- package/routing-profiles/default.json +8 -0
- package/routing-profiles/schema.json +4 -2
package/dist/eval.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { PersonaIntent, PersonaTier } from './index.js';
|
|
2
|
+
export interface EvalCase {
|
|
3
|
+
id: string;
|
|
4
|
+
intent: PersonaIntent;
|
|
5
|
+
prompt: string;
|
|
6
|
+
expectedSignals: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface EvalResult {
|
|
9
|
+
caseId: string;
|
|
10
|
+
tier: PersonaTier;
|
|
11
|
+
score: number;
|
|
12
|
+
costUsd?: number;
|
|
13
|
+
latencyMs?: number;
|
|
14
|
+
notes?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Placeholder for a future benchmark runner that executes persona/tier combinations
|
|
18
|
+
* and computes quality/cost/latency tradeoffs.
|
|
19
|
+
*/
|
|
20
|
+
export declare function summarizeEval(results: readonly EvalResult[]): {
|
|
21
|
+
avgScore: number;
|
|
22
|
+
avgCostUsd: number;
|
|
23
|
+
avgLatencyMs: number;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=eval.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eval.d.ts","sourceRoot":"","sources":["../src/eval.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE7D,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG;IAC7D,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB,CAYA"}
|
package/dist/eval.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Placeholder for a future benchmark runner that executes persona/tier combinations
|
|
3
|
+
* and computes quality/cost/latency tradeoffs.
|
|
4
|
+
*/
|
|
5
|
+
export function summarizeEval(results) {
|
|
6
|
+
if (!results.length) {
|
|
7
|
+
return { avgScore: 0, avgCostUsd: 0, avgLatencyMs: 0 };
|
|
8
|
+
}
|
|
9
|
+
const avgScore = results.reduce((sum, r) => sum + r.score, 0) / results.length;
|
|
10
|
+
const avgCostUsd = results.reduce((sum, r) => sum + (r.costUsd ?? 0), 0) / results.length;
|
|
11
|
+
const avgLatencyMs = results.reduce((sum, r) => sum + (r.latencyMs ?? 0), 0) / results.length;
|
|
12
|
+
return { avgScore, avgCostUsd, avgLatencyMs };
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=eval.js.map
|
package/dist/eval.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eval.js","sourceRoot":"","sources":["../src/eval.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,OAA8B;IAK1D,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;IACzD,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAC/E,MAAM,UAAU,GACd,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IACzE,MAAM,YAAY,GAChB,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAE3E,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;AAChD,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export declare const HARNESS_VALUES: readonly ["opencode", "codex", "claude"];
|
|
2
|
+
export declare const PERSONA_TIERS: readonly ["best", "best-value", "minimum"];
|
|
3
|
+
export declare const PERSONA_INTENTS: readonly ["implement-frontend", "review", "architecture-plan", "requirements-analysis", "debugging", "security-review", "documentation", "verification", "test-strategy", "tdd-enforcement", "flake-investigation", "opencode-workflow-correctness", "npm-provenance"];
|
|
4
|
+
export type Harness = (typeof HARNESS_VALUES)[number];
|
|
5
|
+
export type PersonaTier = (typeof PERSONA_TIERS)[number];
|
|
6
|
+
export type PersonaIntent = (typeof PERSONA_INTENTS)[number];
|
|
7
|
+
export interface HarnessSettings {
|
|
8
|
+
reasoning: 'low' | 'medium' | 'high';
|
|
9
|
+
timeoutSeconds: number;
|
|
10
|
+
}
|
|
11
|
+
export interface PersonaRuntime {
|
|
12
|
+
harness: Harness;
|
|
13
|
+
model: string;
|
|
14
|
+
systemPrompt: string;
|
|
15
|
+
harnessSettings: HarnessSettings;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A skill is a named, reusable capability attached to a persona.
|
|
19
|
+
* `source` points to canonical guidance the persona should apply
|
|
20
|
+
* (e.g. a prpm.dev package URL, an internal runbook, a docs page).
|
|
21
|
+
*/
|
|
22
|
+
export interface PersonaSkill {
|
|
23
|
+
id: string;
|
|
24
|
+
source: string;
|
|
25
|
+
description: string;
|
|
26
|
+
}
|
|
27
|
+
export interface PersonaSpec {
|
|
28
|
+
id: string;
|
|
29
|
+
intent: PersonaIntent;
|
|
30
|
+
description: string;
|
|
31
|
+
skills: PersonaSkill[];
|
|
32
|
+
tiers: Record<PersonaTier, PersonaRuntime>;
|
|
33
|
+
}
|
|
34
|
+
export interface RoutingProfileRule {
|
|
35
|
+
tier: PersonaTier;
|
|
36
|
+
rationale: string;
|
|
37
|
+
}
|
|
38
|
+
export interface RoutingProfile {
|
|
39
|
+
id: string;
|
|
40
|
+
description: string;
|
|
41
|
+
intents: Record<PersonaIntent, RoutingProfileRule>;
|
|
42
|
+
}
|
|
43
|
+
export interface PersonaSelection {
|
|
44
|
+
personaId: string;
|
|
45
|
+
tier: PersonaTier;
|
|
46
|
+
runtime: PersonaRuntime;
|
|
47
|
+
skills: PersonaSkill[];
|
|
48
|
+
rationale: string;
|
|
49
|
+
}
|
|
50
|
+
export declare const SKILL_SOURCE_KINDS: readonly ["prpm"];
|
|
51
|
+
export type SkillSourceKind = (typeof SKILL_SOURCE_KINDS)[number];
|
|
52
|
+
/** Per-harness rules for where skills land on disk and how to ask prpm for them. */
|
|
53
|
+
export interface HarnessSkillTarget {
|
|
54
|
+
/** Value passed to `prpm install --as <flag>`. */
|
|
55
|
+
asFlag: string;
|
|
56
|
+
/** Directory (relative to repo root) where prpm drops the skill package. */
|
|
57
|
+
dir: string;
|
|
58
|
+
}
|
|
59
|
+
export declare const HARNESS_SKILL_TARGETS: Record<Harness, HarnessSkillTarget>;
|
|
60
|
+
export interface SkillInstall {
|
|
61
|
+
skillId: string;
|
|
62
|
+
/** Original `source` string from the persona JSON. */
|
|
63
|
+
source: string;
|
|
64
|
+
sourceKind: SkillSourceKind;
|
|
65
|
+
/** Normalized package reference used by prpm (e.g. `prpm/npm-trusted-publishing`). */
|
|
66
|
+
packageRef: string;
|
|
67
|
+
harness: Harness;
|
|
68
|
+
/** argv-style command — safer than a shell string for execFile/spawn callers. */
|
|
69
|
+
installCommand: readonly string[];
|
|
70
|
+
/** Directory the skill is expected to land in after install. */
|
|
71
|
+
installedDir: string;
|
|
72
|
+
/** Path to the installed SKILL.md manifest (for prompt injection fallback). */
|
|
73
|
+
installedManifest: string;
|
|
74
|
+
}
|
|
75
|
+
export interface SkillMaterializationPlan {
|
|
76
|
+
harness: Harness;
|
|
77
|
+
installs: SkillInstall[];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Given a set of persona skills and the harness the persona will run under,
|
|
81
|
+
* produce the concrete install plan: which `prpm install` invocations to run
|
|
82
|
+
* and where the skill will land on disk once installed.
|
|
83
|
+
*
|
|
84
|
+
* Pure function — does not execute commands or touch the filesystem.
|
|
85
|
+
*/
|
|
86
|
+
export declare function materializeSkills(skills: readonly PersonaSkill[], harness: Harness): SkillMaterializationPlan;
|
|
87
|
+
/**
|
|
88
|
+
* Convenience wrapper: derive the install plan directly from a resolved
|
|
89
|
+
* persona selection, using its tier's harness automatically.
|
|
90
|
+
*/
|
|
91
|
+
export declare function materializeSkillsFor(selection: PersonaSelection): SkillMaterializationPlan;
|
|
92
|
+
export declare const personaCatalog: Record<PersonaIntent, PersonaSpec>;
|
|
93
|
+
export declare const routingProfiles: {
|
|
94
|
+
readonly default: RoutingProfile;
|
|
95
|
+
};
|
|
96
|
+
export type RoutingProfileId = keyof typeof routingProfiles;
|
|
97
|
+
export declare function resolvePersona(intent: PersonaIntent, profile?: RoutingProfile | RoutingProfileId): PersonaSelection;
|
|
98
|
+
/**
|
|
99
|
+
* Backward-compatible helper for callers that already selected a tier directly.
|
|
100
|
+
* Prefer resolvePersona(intent, profile) for policy-driven selection.
|
|
101
|
+
*/
|
|
102
|
+
export declare function resolvePersonaByTier(intent: PersonaIntent, tier?: PersonaTier): PersonaSelection;
|
|
103
|
+
export * from './eval.js';
|
|
104
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import frontendImplementer from '../../../personas/frontend-implementer.json' with { type: 'json' };
|
|
2
|
+
import codeReviewer from '../../../personas/code-reviewer.json' with { type: 'json' };
|
|
3
|
+
import architecturePlanner from '../../../personas/architecture-planner.json' with { type: 'json' };
|
|
4
|
+
import requirementsAnalyst from '../../../personas/requirements-analyst.json' with { type: 'json' };
|
|
5
|
+
import debuggerPersona from '../../../personas/debugger.json' with { type: 'json' };
|
|
6
|
+
import securityReviewer from '../../../personas/security-reviewer.json' with { type: 'json' };
|
|
7
|
+
import technicalWriter from '../../../personas/technical-writer.json' with { type: 'json' };
|
|
8
|
+
import verifierPersona from '../../../personas/verifier.json' with { type: 'json' };
|
|
9
|
+
import testStrategist from '../../../personas/test-strategist.json' with { type: 'json' };
|
|
10
|
+
import tddGuard from '../../../personas/tdd-guard.json' with { type: 'json' };
|
|
11
|
+
import flakeHunter from '../../../personas/flake-hunter.json' with { type: 'json' };
|
|
12
|
+
import opencodeWorkflowSpecialist from '../../../personas/opencode-workflow-specialist.json' with { type: 'json' };
|
|
13
|
+
import npmProvenancePublisher from '../../../personas/npm-provenance-publisher.json' with { type: 'json' };
|
|
14
|
+
import defaultRoutingProfileJson from '../routing-profiles/default.json' with { type: 'json' };
|
|
15
|
+
export const HARNESS_VALUES = ['opencode', 'codex', 'claude'];
|
|
16
|
+
export const PERSONA_TIERS = ['best', 'best-value', 'minimum'];
|
|
17
|
+
export const PERSONA_INTENTS = [
|
|
18
|
+
'implement-frontend',
|
|
19
|
+
'review',
|
|
20
|
+
'architecture-plan',
|
|
21
|
+
'requirements-analysis',
|
|
22
|
+
'debugging',
|
|
23
|
+
'security-review',
|
|
24
|
+
'documentation',
|
|
25
|
+
'verification',
|
|
26
|
+
'test-strategy',
|
|
27
|
+
'tdd-enforcement',
|
|
28
|
+
'flake-investigation',
|
|
29
|
+
'opencode-workflow-correctness',
|
|
30
|
+
'npm-provenance'
|
|
31
|
+
];
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Skill materialization
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
//
|
|
36
|
+
// Personas declare *what* skill they need via `skills: [{ id, source, ... }]`.
|
|
37
|
+
// The SDK is the only layer that knows *how* to make that skill available to
|
|
38
|
+
// a given harness — because each harness has its own on-disk convention and
|
|
39
|
+
// its own prpm install flag. Keeping this mapping here means:
|
|
40
|
+
//
|
|
41
|
+
// 1. Workflow authors never hand-type `prpm install ... --as codex`.
|
|
42
|
+
// 2. Changing install rules is a one-line SDK edit, not a repo-wide grep.
|
|
43
|
+
// 3. Persona JSON stays harness-agnostic and forward-compatible.
|
|
44
|
+
//
|
|
45
|
+
// `materializeSkills` is a pure function: it returns the install plan but
|
|
46
|
+
// never touches the filesystem or spawns processes. Callers (relay workflows,
|
|
47
|
+
// the OpenClaw spawner, ad-hoc scripts) decide how to execute it.
|
|
48
|
+
export const SKILL_SOURCE_KINDS = ['prpm'];
|
|
49
|
+
export const HARNESS_SKILL_TARGETS = {
|
|
50
|
+
claude: { asFlag: 'claude', dir: '.claude/skills' },
|
|
51
|
+
codex: { asFlag: 'codex', dir: '.agents/skills' },
|
|
52
|
+
opencode: { asFlag: 'opencode', dir: '.skills' }
|
|
53
|
+
};
|
|
54
|
+
const PRPM_URL_RE = /^https?:\/\/prpm\.dev\/packages\/([^/\s?#]+)\/([^/\s?#]+)\/?(?:[?#].*)?$/i;
|
|
55
|
+
const PRPM_BARE_REF_RE = /^([^/\s]+)\/([^/\s]+)$/;
|
|
56
|
+
function resolveSkillSource(source) {
|
|
57
|
+
const urlMatch = source.match(PRPM_URL_RE);
|
|
58
|
+
if (urlMatch) {
|
|
59
|
+
return { kind: 'prpm', packageRef: `${urlMatch[1]}/${urlMatch[2]}` };
|
|
60
|
+
}
|
|
61
|
+
const bareMatch = source.match(PRPM_BARE_REF_RE);
|
|
62
|
+
if (bareMatch) {
|
|
63
|
+
return { kind: 'prpm', packageRef: source };
|
|
64
|
+
}
|
|
65
|
+
throw new Error(`Unsupported skill source: ${source}. ` +
|
|
66
|
+
`Supported forms: prpm.dev package URL (https://prpm.dev/packages/<scope>/<name>) ` +
|
|
67
|
+
`or a bare "<scope>/<name>" reference.`);
|
|
68
|
+
}
|
|
69
|
+
function deriveInstalledName(packageRef) {
|
|
70
|
+
const slash = packageRef.lastIndexOf('/');
|
|
71
|
+
return slash >= 0 ? packageRef.slice(slash + 1) : packageRef;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Given a set of persona skills and the harness the persona will run under,
|
|
75
|
+
* produce the concrete install plan: which `prpm install` invocations to run
|
|
76
|
+
* and where the skill will land on disk once installed.
|
|
77
|
+
*
|
|
78
|
+
* Pure function — does not execute commands or touch the filesystem.
|
|
79
|
+
*/
|
|
80
|
+
export function materializeSkills(skills, harness) {
|
|
81
|
+
const target = HARNESS_SKILL_TARGETS[harness];
|
|
82
|
+
if (!target) {
|
|
83
|
+
throw new Error(`No skill install target configured for harness: ${harness}`);
|
|
84
|
+
}
|
|
85
|
+
const installs = skills.map((skill) => {
|
|
86
|
+
const { kind, packageRef } = resolveSkillSource(skill.source);
|
|
87
|
+
const installedName = deriveInstalledName(packageRef);
|
|
88
|
+
const installedDir = `${target.dir}/${installedName}`;
|
|
89
|
+
return {
|
|
90
|
+
skillId: skill.id,
|
|
91
|
+
source: skill.source,
|
|
92
|
+
sourceKind: kind,
|
|
93
|
+
packageRef,
|
|
94
|
+
harness,
|
|
95
|
+
installCommand: Object.freeze([
|
|
96
|
+
'npx',
|
|
97
|
+
'-y',
|
|
98
|
+
'prpm',
|
|
99
|
+
'install',
|
|
100
|
+
packageRef,
|
|
101
|
+
'--as',
|
|
102
|
+
target.asFlag
|
|
103
|
+
]),
|
|
104
|
+
installedDir,
|
|
105
|
+
installedManifest: `${installedDir}/SKILL.md`
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
return { harness, installs };
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Convenience wrapper: derive the install plan directly from a resolved
|
|
112
|
+
* persona selection, using its tier's harness automatically.
|
|
113
|
+
*/
|
|
114
|
+
export function materializeSkillsFor(selection) {
|
|
115
|
+
return materializeSkills(selection.skills, selection.runtime.harness);
|
|
116
|
+
}
|
|
117
|
+
function isObject(value) {
|
|
118
|
+
return typeof value === 'object' && value !== null;
|
|
119
|
+
}
|
|
120
|
+
function isHarness(value) {
|
|
121
|
+
return typeof value === 'string' && HARNESS_VALUES.includes(value);
|
|
122
|
+
}
|
|
123
|
+
function isTier(value) {
|
|
124
|
+
return typeof value === 'string' && PERSONA_TIERS.includes(value);
|
|
125
|
+
}
|
|
126
|
+
function isIntent(value) {
|
|
127
|
+
return typeof value === 'string' && PERSONA_INTENTS.includes(value);
|
|
128
|
+
}
|
|
129
|
+
function parseRuntime(value, context) {
|
|
130
|
+
if (!isObject(value)) {
|
|
131
|
+
throw new Error(`${context} must be an object`);
|
|
132
|
+
}
|
|
133
|
+
const { harness, model, systemPrompt, harnessSettings } = value;
|
|
134
|
+
if (!isHarness(harness)) {
|
|
135
|
+
throw new Error(`${context}.harness must be one of: ${HARNESS_VALUES.join(', ')}`);
|
|
136
|
+
}
|
|
137
|
+
if (typeof model !== 'string' || !model.trim()) {
|
|
138
|
+
throw new Error(`${context}.model must be a non-empty string`);
|
|
139
|
+
}
|
|
140
|
+
if (typeof systemPrompt !== 'string' || !systemPrompt.trim()) {
|
|
141
|
+
throw new Error(`${context}.systemPrompt must be a non-empty string`);
|
|
142
|
+
}
|
|
143
|
+
if (!isObject(harnessSettings)) {
|
|
144
|
+
throw new Error(`${context}.harnessSettings must be an object`);
|
|
145
|
+
}
|
|
146
|
+
const { reasoning, timeoutSeconds } = harnessSettings;
|
|
147
|
+
if (!['low', 'medium', 'high'].includes(String(reasoning))) {
|
|
148
|
+
throw new Error(`${context}.harnessSettings.reasoning must be low|medium|high`);
|
|
149
|
+
}
|
|
150
|
+
if (typeof timeoutSeconds !== 'number' || !Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0) {
|
|
151
|
+
throw new Error(`${context}.harnessSettings.timeoutSeconds must be a positive number`);
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
harness,
|
|
155
|
+
model,
|
|
156
|
+
systemPrompt,
|
|
157
|
+
harnessSettings: {
|
|
158
|
+
reasoning: reasoning,
|
|
159
|
+
timeoutSeconds
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function parseSkills(value, context) {
|
|
164
|
+
if (value === undefined) {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
if (!Array.isArray(value)) {
|
|
168
|
+
throw new Error(`${context} must be an array if provided`);
|
|
169
|
+
}
|
|
170
|
+
return value.map((entry, idx) => {
|
|
171
|
+
const entryContext = `${context}[${idx}]`;
|
|
172
|
+
if (!isObject(entry)) {
|
|
173
|
+
throw new Error(`${entryContext} must be an object`);
|
|
174
|
+
}
|
|
175
|
+
const { id, source, description } = entry;
|
|
176
|
+
if (typeof id !== 'string' || !id.trim()) {
|
|
177
|
+
throw new Error(`${entryContext}.id must be a non-empty string`);
|
|
178
|
+
}
|
|
179
|
+
if (typeof source !== 'string' || !source.trim()) {
|
|
180
|
+
throw new Error(`${entryContext}.source must be a non-empty string`);
|
|
181
|
+
}
|
|
182
|
+
if (typeof description !== 'string' || !description.trim()) {
|
|
183
|
+
throw new Error(`${entryContext}.description must be a non-empty string`);
|
|
184
|
+
}
|
|
185
|
+
return { id, source, description };
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
function parsePersonaSpec(value, expectedIntent) {
|
|
189
|
+
if (!isObject(value)) {
|
|
190
|
+
throw new Error(`persona[${expectedIntent}] must be an object`);
|
|
191
|
+
}
|
|
192
|
+
const { id, intent, description, tiers, skills } = value;
|
|
193
|
+
if (typeof id !== 'string' || !id.trim()) {
|
|
194
|
+
throw new Error(`persona[${expectedIntent}].id must be a non-empty string`);
|
|
195
|
+
}
|
|
196
|
+
if (!isIntent(intent)) {
|
|
197
|
+
throw new Error(`persona[${expectedIntent}].intent is invalid`);
|
|
198
|
+
}
|
|
199
|
+
if (intent !== expectedIntent) {
|
|
200
|
+
throw new Error(`persona[${expectedIntent}] intent mismatch: got ${intent}`);
|
|
201
|
+
}
|
|
202
|
+
if (typeof description !== 'string' || !description.trim()) {
|
|
203
|
+
throw new Error(`persona[${expectedIntent}].description must be a non-empty string`);
|
|
204
|
+
}
|
|
205
|
+
if (!isObject(tiers)) {
|
|
206
|
+
throw new Error(`persona[${expectedIntent}].tiers must be an object`);
|
|
207
|
+
}
|
|
208
|
+
const parsedTiers = {};
|
|
209
|
+
for (const tier of PERSONA_TIERS) {
|
|
210
|
+
parsedTiers[tier] = parseRuntime(tiers[tier], `persona[${expectedIntent}].tiers.${tier}`);
|
|
211
|
+
}
|
|
212
|
+
const parsedSkills = parseSkills(skills, `persona[${expectedIntent}].skills`);
|
|
213
|
+
return {
|
|
214
|
+
id,
|
|
215
|
+
intent,
|
|
216
|
+
description,
|
|
217
|
+
skills: parsedSkills,
|
|
218
|
+
tiers: parsedTiers
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function parseRoutingProfile(value, context) {
|
|
222
|
+
if (!isObject(value)) {
|
|
223
|
+
throw new Error(`${context} must be an object`);
|
|
224
|
+
}
|
|
225
|
+
const { id, description, intents } = value;
|
|
226
|
+
if (typeof id !== 'string' || !id.trim()) {
|
|
227
|
+
throw new Error(`${context}.id must be a non-empty string`);
|
|
228
|
+
}
|
|
229
|
+
if (typeof description !== 'string' || !description.trim()) {
|
|
230
|
+
throw new Error(`${context}.description must be a non-empty string`);
|
|
231
|
+
}
|
|
232
|
+
if (!isObject(intents)) {
|
|
233
|
+
throw new Error(`${context}.intents must be an object`);
|
|
234
|
+
}
|
|
235
|
+
const parsedIntents = {};
|
|
236
|
+
for (const intent of PERSONA_INTENTS) {
|
|
237
|
+
const rule = intents[intent];
|
|
238
|
+
if (!isObject(rule)) {
|
|
239
|
+
throw new Error(`${context}.intents.${intent} must be an object`);
|
|
240
|
+
}
|
|
241
|
+
const { tier, rationale } = rule;
|
|
242
|
+
if (!isTier(tier)) {
|
|
243
|
+
throw new Error(`${context}.intents.${intent}.tier must be one of: ${PERSONA_TIERS.join(', ')}`);
|
|
244
|
+
}
|
|
245
|
+
if (typeof rationale !== 'string' || !rationale.trim()) {
|
|
246
|
+
throw new Error(`${context}.intents.${intent}.rationale must be a non-empty string`);
|
|
247
|
+
}
|
|
248
|
+
parsedIntents[intent] = { tier, rationale };
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
id,
|
|
252
|
+
description,
|
|
253
|
+
intents: parsedIntents
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
export const personaCatalog = {
|
|
257
|
+
'implement-frontend': parsePersonaSpec(frontendImplementer, 'implement-frontend'),
|
|
258
|
+
review: parsePersonaSpec(codeReviewer, 'review'),
|
|
259
|
+
'architecture-plan': parsePersonaSpec(architecturePlanner, 'architecture-plan'),
|
|
260
|
+
'requirements-analysis': parsePersonaSpec(requirementsAnalyst, 'requirements-analysis'),
|
|
261
|
+
debugging: parsePersonaSpec(debuggerPersona, 'debugging'),
|
|
262
|
+
'security-review': parsePersonaSpec(securityReviewer, 'security-review'),
|
|
263
|
+
documentation: parsePersonaSpec(technicalWriter, 'documentation'),
|
|
264
|
+
verification: parsePersonaSpec(verifierPersona, 'verification'),
|
|
265
|
+
'test-strategy': parsePersonaSpec(testStrategist, 'test-strategy'),
|
|
266
|
+
'tdd-enforcement': parsePersonaSpec(tddGuard, 'tdd-enforcement'),
|
|
267
|
+
'flake-investigation': parsePersonaSpec(flakeHunter, 'flake-investigation'),
|
|
268
|
+
'opencode-workflow-correctness': parsePersonaSpec(opencodeWorkflowSpecialist, 'opencode-workflow-correctness'),
|
|
269
|
+
'npm-provenance': parsePersonaSpec(npmProvenancePublisher, 'npm-provenance')
|
|
270
|
+
};
|
|
271
|
+
export const routingProfiles = {
|
|
272
|
+
default: parseRoutingProfile(defaultRoutingProfileJson, 'routingProfiles.default')
|
|
273
|
+
};
|
|
274
|
+
export function resolvePersona(intent, profile = 'default') {
|
|
275
|
+
const profileSpec = typeof profile === 'string' ? routingProfiles[profile] : profile;
|
|
276
|
+
const rule = profileSpec.intents[intent];
|
|
277
|
+
const spec = personaCatalog[intent];
|
|
278
|
+
return {
|
|
279
|
+
personaId: spec.id,
|
|
280
|
+
tier: rule.tier,
|
|
281
|
+
runtime: spec.tiers[rule.tier],
|
|
282
|
+
skills: spec.skills,
|
|
283
|
+
rationale: `${profileSpec.id}: ${rule.rationale}`
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Backward-compatible helper for callers that already selected a tier directly.
|
|
288
|
+
* Prefer resolvePersona(intent, profile) for policy-driven selection.
|
|
289
|
+
*/
|
|
290
|
+
export function resolvePersonaByTier(intent, tier = 'best-value') {
|
|
291
|
+
const spec = personaCatalog[intent];
|
|
292
|
+
return {
|
|
293
|
+
personaId: spec.id,
|
|
294
|
+
tier,
|
|
295
|
+
runtime: spec.tiers[tier],
|
|
296
|
+
skills: spec.skills,
|
|
297
|
+
rationale: `legacy-tier-override: ${tier}`
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
export * from './eval.js';
|
|
301
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { HARNESS_SKILL_TARGETS, HARNESS_VALUES, materializeSkills, materializeSkillsFor, personaCatalog, resolvePersona, resolvePersonaByTier } from './index.js';
|
|
4
|
+
test('resolves frontend implementer from default routing profile', () => {
|
|
5
|
+
const result = resolvePersona('implement-frontend');
|
|
6
|
+
assert.equal(result.personaId, 'frontend-implementer');
|
|
7
|
+
assert.equal(result.tier, 'best-value');
|
|
8
|
+
assert.equal(result.runtime.harness, 'opencode');
|
|
9
|
+
assert.match(result.rationale, /balanced-default/);
|
|
10
|
+
});
|
|
11
|
+
test('resolves review from custom routing profile rule', () => {
|
|
12
|
+
const result = resolvePersona('review', {
|
|
13
|
+
id: 'fast-review',
|
|
14
|
+
description: 'Aggressive low-cost mode for lightweight checks',
|
|
15
|
+
intents: {
|
|
16
|
+
'implement-frontend': {
|
|
17
|
+
tier: 'minimum',
|
|
18
|
+
rationale: 'fast and cheap'
|
|
19
|
+
},
|
|
20
|
+
review: {
|
|
21
|
+
tier: 'minimum',
|
|
22
|
+
rationale: 'small PR sanity checks only'
|
|
23
|
+
},
|
|
24
|
+
'architecture-plan': {
|
|
25
|
+
tier: 'best-value',
|
|
26
|
+
rationale: 'still needs decent quality'
|
|
27
|
+
},
|
|
28
|
+
'requirements-analysis': {
|
|
29
|
+
tier: 'minimum',
|
|
30
|
+
rationale: 'quick scope triage is enough here'
|
|
31
|
+
},
|
|
32
|
+
debugging: {
|
|
33
|
+
tier: 'best',
|
|
34
|
+
rationale: 'debugging still needs deeper reasoning'
|
|
35
|
+
},
|
|
36
|
+
'security-review': {
|
|
37
|
+
tier: 'best',
|
|
38
|
+
rationale: 'security stays on the strongest tier'
|
|
39
|
+
},
|
|
40
|
+
documentation: {
|
|
41
|
+
tier: 'minimum',
|
|
42
|
+
rationale: 'docs tweaks can be short'
|
|
43
|
+
},
|
|
44
|
+
verification: {
|
|
45
|
+
tier: 'best-value',
|
|
46
|
+
rationale: 'fresh evidence review needs balanced depth'
|
|
47
|
+
},
|
|
48
|
+
'test-strategy': {
|
|
49
|
+
tier: 'best-value',
|
|
50
|
+
rationale: 'needs balanced coverage planning'
|
|
51
|
+
},
|
|
52
|
+
'tdd-enforcement': {
|
|
53
|
+
tier: 'minimum',
|
|
54
|
+
rationale: 'short process reminders are enough'
|
|
55
|
+
},
|
|
56
|
+
'flake-investigation': {
|
|
57
|
+
tier: 'best',
|
|
58
|
+
rationale: 'deep debugging is worth the cost'
|
|
59
|
+
},
|
|
60
|
+
'opencode-workflow-correctness': {
|
|
61
|
+
tier: 'best',
|
|
62
|
+
rationale: 'cross-layer workflow failures need deeper investigation'
|
|
63
|
+
},
|
|
64
|
+
'npm-provenance': {
|
|
65
|
+
tier: 'best-value',
|
|
66
|
+
rationale: 'mechanical workflow wiring'
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
assert.equal(result.personaId, 'code-reviewer');
|
|
71
|
+
assert.equal(result.tier, 'minimum');
|
|
72
|
+
assert.equal(result.runtime.harness, 'opencode');
|
|
73
|
+
});
|
|
74
|
+
test('legacy tier override remains available via resolvePersonaByTier', () => {
|
|
75
|
+
const result = resolvePersonaByTier('architecture-plan', 'best');
|
|
76
|
+
assert.equal(result.runtime.harness, 'codex');
|
|
77
|
+
assert.equal(result.runtime.harnessSettings.reasoning, 'high');
|
|
78
|
+
assert.match(result.rationale, /legacy-tier-override/);
|
|
79
|
+
});
|
|
80
|
+
test('resolves testing personas from the default routing profile', () => {
|
|
81
|
+
const testStrategy = resolvePersona('test-strategy');
|
|
82
|
+
assert.equal(testStrategy.personaId, 'test-strategist');
|
|
83
|
+
assert.equal(testStrategy.tier, 'best-value');
|
|
84
|
+
const tdd = resolvePersona('tdd-enforcement');
|
|
85
|
+
assert.equal(tdd.personaId, 'tdd-guard');
|
|
86
|
+
assert.equal(tdd.tier, 'best-value');
|
|
87
|
+
const flake = resolvePersona('flake-investigation');
|
|
88
|
+
assert.equal(flake.personaId, 'flake-hunter');
|
|
89
|
+
assert.equal(flake.tier, 'best');
|
|
90
|
+
assert.equal(flake.runtime.harness, 'codex');
|
|
91
|
+
});
|
|
92
|
+
test('resolves newly added personas from the default routing profile', () => {
|
|
93
|
+
const analyst = resolvePersona('requirements-analysis');
|
|
94
|
+
assert.equal(analyst.personaId, 'requirements-analyst');
|
|
95
|
+
assert.equal(analyst.tier, 'best-value');
|
|
96
|
+
const debuggerSelection = resolvePersona('debugging');
|
|
97
|
+
assert.equal(debuggerSelection.personaId, 'debugger');
|
|
98
|
+
assert.equal(debuggerSelection.tier, 'best');
|
|
99
|
+
assert.equal(debuggerSelection.runtime.harness, 'codex');
|
|
100
|
+
const security = resolvePersona('security-review');
|
|
101
|
+
assert.equal(security.personaId, 'security-reviewer');
|
|
102
|
+
assert.equal(security.tier, 'best');
|
|
103
|
+
const docs = resolvePersona('documentation');
|
|
104
|
+
assert.equal(docs.personaId, 'technical-writer');
|
|
105
|
+
assert.equal(docs.tier, 'best-value');
|
|
106
|
+
const verification = resolvePersona('verification');
|
|
107
|
+
assert.equal(verification.personaId, 'verifier');
|
|
108
|
+
assert.equal(verification.tier, 'best-value');
|
|
109
|
+
const opencodeWorkflow = resolvePersona('opencode-workflow-correctness');
|
|
110
|
+
assert.equal(opencodeWorkflow.personaId, 'opencode-workflow-specialist');
|
|
111
|
+
assert.equal(opencodeWorkflow.tier, 'best');
|
|
112
|
+
assert.equal(opencodeWorkflow.runtime.harness, 'codex');
|
|
113
|
+
});
|
|
114
|
+
test('claude is a recognized harness value', () => {
|
|
115
|
+
assert.ok(HARNESS_VALUES.includes('claude'));
|
|
116
|
+
});
|
|
117
|
+
test('personas default to an empty skills array when none declared', () => {
|
|
118
|
+
const reviewer = personaCatalog.review;
|
|
119
|
+
assert.ok(Array.isArray(reviewer.skills));
|
|
120
|
+
assert.equal(reviewer.skills.length, 0);
|
|
121
|
+
});
|
|
122
|
+
test('resolves npm-provenance persona with the trusted publishing skill attached', () => {
|
|
123
|
+
const selection = resolvePersona('npm-provenance');
|
|
124
|
+
assert.equal(selection.personaId, 'npm-provenance-publisher');
|
|
125
|
+
assert.equal(selection.tier, 'best-value');
|
|
126
|
+
assert.equal(selection.skills.length, 1);
|
|
127
|
+
const [skill] = selection.skills;
|
|
128
|
+
assert.equal(skill.id, 'prpm/npm-trusted-publishing');
|
|
129
|
+
assert.match(skill.source, /prpm\.dev\/packages\/@prpm\/npm-trusted-publishing/);
|
|
130
|
+
assert.match(selection.runtime.systemPrompt, /prpm\/npm-trusted-publishing/);
|
|
131
|
+
});
|
|
132
|
+
test('resolvePersonaByTier carries persona skills through legacy path', () => {
|
|
133
|
+
const selection = resolvePersonaByTier('npm-provenance', 'best');
|
|
134
|
+
assert.equal(selection.runtime.harness, 'codex');
|
|
135
|
+
assert.equal(selection.skills[0]?.id, 'prpm/npm-trusted-publishing');
|
|
136
|
+
});
|
|
137
|
+
test('HARNESS_SKILL_TARGETS covers every harness value', () => {
|
|
138
|
+
for (const harness of HARNESS_VALUES) {
|
|
139
|
+
const target = HARNESS_SKILL_TARGETS[harness];
|
|
140
|
+
assert.ok(target, `missing target for harness ${harness}`);
|
|
141
|
+
assert.ok(target.asFlag.length > 0);
|
|
142
|
+
assert.ok(target.dir.length > 0);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
test('materializeSkills emits a codex-scoped prpm install for a prpm.dev URL', () => {
|
|
146
|
+
const plan = materializeSkills([
|
|
147
|
+
{
|
|
148
|
+
id: 'prpm/npm-trusted-publishing',
|
|
149
|
+
source: 'https://prpm.dev/packages/@prpm/npm-trusted-publishing',
|
|
150
|
+
description: 'trusted publishing skill'
|
|
151
|
+
}
|
|
152
|
+
], 'codex');
|
|
153
|
+
assert.equal(plan.harness, 'codex');
|
|
154
|
+
assert.equal(plan.installs.length, 1);
|
|
155
|
+
const [install] = plan.installs;
|
|
156
|
+
assert.equal(install.sourceKind, 'prpm');
|
|
157
|
+
assert.equal(install.packageRef, '@prpm/npm-trusted-publishing');
|
|
158
|
+
assert.deepEqual([...install.installCommand], ['npx', '-y', 'prpm', 'install', '@prpm/npm-trusted-publishing', '--as', 'codex']);
|
|
159
|
+
assert.equal(install.installedDir, '.agents/skills/npm-trusted-publishing');
|
|
160
|
+
assert.equal(install.installedManifest, '.agents/skills/npm-trusted-publishing/SKILL.md');
|
|
161
|
+
});
|
|
162
|
+
test('materializeSkills routes claude skills to .claude/skills via --as claude', () => {
|
|
163
|
+
const plan = materializeSkills([
|
|
164
|
+
{
|
|
165
|
+
id: 'prpm/npm-trusted-publishing',
|
|
166
|
+
source: '@prpm/npm-trusted-publishing',
|
|
167
|
+
description: 'bare ref form'
|
|
168
|
+
}
|
|
169
|
+
], 'claude');
|
|
170
|
+
const [install] = plan.installs;
|
|
171
|
+
assert.deepEqual([...install.installCommand], ['npx', '-y', 'prpm', 'install', '@prpm/npm-trusted-publishing', '--as', 'claude']);
|
|
172
|
+
assert.equal(install.installedDir, '.claude/skills/npm-trusted-publishing');
|
|
173
|
+
});
|
|
174
|
+
test('materializeSkillsFor derives an install plan from a resolved persona', () => {
|
|
175
|
+
const selection = resolvePersona('npm-provenance');
|
|
176
|
+
const plan = materializeSkillsFor(selection);
|
|
177
|
+
assert.equal(plan.harness, selection.runtime.harness);
|
|
178
|
+
assert.equal(plan.installs.length, 1);
|
|
179
|
+
const cmd = plan.installs[0].installCommand.join(' ');
|
|
180
|
+
assert.match(cmd, /prpm install @prpm\/npm-trusted-publishing --as /);
|
|
181
|
+
});
|
|
182
|
+
test('materializeSkills rejects unknown skill sources', () => {
|
|
183
|
+
assert.throws(() => materializeSkills([
|
|
184
|
+
{
|
|
185
|
+
id: 'x',
|
|
186
|
+
source: 'https://example.com/random',
|
|
187
|
+
description: 'not a prpm source'
|
|
188
|
+
}
|
|
189
|
+
], 'claude'), /Unsupported skill source/);
|
|
190
|
+
});
|
|
191
|
+
test('materializeSkills handles personas with no skills', () => {
|
|
192
|
+
const plan = materializeSkills([], 'claude');
|
|
193
|
+
assert.equal(plan.installs.length, 0);
|
|
194
|
+
});
|
|
195
|
+
//# sourceMappingURL=index.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentworkforce/workload-router",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
"README.md",
|
|
18
18
|
"package.json"
|
|
19
19
|
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/AgentWorkforce/workforce"
|
|
23
|
+
},
|
|
20
24
|
"publishConfig": {
|
|
21
25
|
"access": "public"
|
|
22
26
|
},
|
|
@@ -46,6 +46,14 @@
|
|
|
46
46
|
"flake-investigation": {
|
|
47
47
|
"tier": "best",
|
|
48
48
|
"rationale": "Intermittent failures are expensive and subtle; prioritize deeper reasoning for reproduction and root-cause analysis."
|
|
49
|
+
},
|
|
50
|
+
"opencode-workflow-correctness": {
|
|
51
|
+
"tier": "best",
|
|
52
|
+
"rationale": "Cross-layer opencode workflow failures are expensive to misdiagnose; default to the deepest tier for end-to-end reproduction and root-cause analysis."
|
|
53
|
+
},
|
|
54
|
+
"npm-provenance": {
|
|
55
|
+
"tier": "best-value",
|
|
56
|
+
"rationale": "Publishing setup is mostly mechanical workflow configuration; best-value is sufficient when guided by the prpm/npm-trusted-publishing skill."
|
|
49
57
|
}
|
|
50
58
|
}
|
|
51
59
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"intents": {
|
|
11
11
|
"type": "object",
|
|
12
12
|
"additionalProperties": false,
|
|
13
|
-
"required": ["implement-frontend", "review", "architecture-plan", "requirements-analysis", "debugging", "security-review", "documentation", "verification", "test-strategy", "tdd-enforcement", "flake-investigation"],
|
|
13
|
+
"required": ["implement-frontend", "review", "architecture-plan", "requirements-analysis", "debugging", "security-review", "documentation", "verification", "test-strategy", "tdd-enforcement", "flake-investigation", "opencode-workflow-correctness", "npm-provenance"],
|
|
14
14
|
"properties": {
|
|
15
15
|
"implement-frontend": { "$ref": "#/definitions/rule" },
|
|
16
16
|
"review": { "$ref": "#/definitions/rule" },
|
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
"verification": { "$ref": "#/definitions/rule" },
|
|
23
23
|
"test-strategy": { "$ref": "#/definitions/rule" },
|
|
24
24
|
"tdd-enforcement": { "$ref": "#/definitions/rule" },
|
|
25
|
-
"flake-investigation": { "$ref": "#/definitions/rule" }
|
|
25
|
+
"flake-investigation": { "$ref": "#/definitions/rule" },
|
|
26
|
+
"opencode-workflow-correctness": { "$ref": "#/definitions/rule" },
|
|
27
|
+
"npm-provenance": { "$ref": "#/definitions/rule" }
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
},
|